Spring Boot OneToOne referencedColumnName not applied - spring-data-jpa

I have 2 tables:
CREATE TABLE user_profile (
id VARCHAR(36) PRIMARY KEY,
first_name VARCHAR(255) NOT NULL
);
CREATE TABLE user_phone (
id VARCHAR(36) PRIMARY KEY,
user_id VARCHAR(36),
phone_number VARCHAR(255) NOT NULL,
code VARCHAR(10) NOT NULL,
validated_at DATETIME NULL
);
ALTER TABLE user_phone ADD CONSTRAINT user_phone_FK FOREIGN KEY (user_id) REFERENCES user_profile(id) ON DELETE CASCADE;
so child has ID reference to his parent, because if I will delete parent I want this child to be deleted also.
In User class I have:
#Entity
#Table(name = "user_profile")
public class UserProfile {
...
#OneToOne
#JoinColumn(name = "id", referencedColumnName = "user_id")
private UserPhone phoneNumber;
but then when I run it I see in JPA logs:
from
user_profile userprofil0_
where
userprofil0_.id=?
why it looks there by ID ? should be by user_id field. Or I missunderstood smth there..
thanks!

One day later and finally I found the solution.
Its pretty tricky in spring and not maybe so intuitive.
So - from user_phone table Ive removed additional ID column, and now user_id is there primary key, only 1 change in sql.
Then in spring:
in UserProfile:
#OneToOne(mappedBy = "userProfile", cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private UserPhone userPhone;
and in UserPhone:
#Id
#Column(name = "user_id", nullable = false)
#Type(type = "uuid-char")
private UUID id;
#OneToOne(fetch = FetchType.LAZY)
#MapsId
#JoinColumn(name = "user_id")
private UserProfile userProfile;
#PrimaryKeyJoinColumn annotation indicates that the primary key of the UserProfile is used as the foreign key value for the associated UserPhone.

Related

Foreign Key violation on ManyToMany with inheritance

Im currently building the following scenario:
I have an Action which holds a list of Parameters. Those can be in other actions as well, so I have a ManyToMany relationship.
The Parameter is an abstract class, one implementation is a TextParameter.
So now I have the following code:
#Data
#Entity
public class Action {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(
name = "Action2ParameterMapping",
joinColumns = #JoinColumn(name = "actionId"),
inverseJoinColumns = #JoinColumn(name = "parameterId"))
private List<Parameter> parameters;
}
with Parameter as
#Data
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class ProductSample {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
And TextParameter:
#Data
#Entity
#PrimaryKeyJoinColumn(name = "parameterId")
public class TextParameter extends Parameter {
...
}
I now created the Tables as follows (I don't want to generate since we use Flyway migration):
CREATE TABLE Action
(
id BIGINT NOT NULL PRIMARY KEY IDENTITY
)
CREATE TABLE Parameter
(
id BIGINT NOT NULL PRIMARY KEY IDENTITY
)
CREATE TABLE TextParameter
(
parameterId BIGINT NOT NULL FOREIGN KEY REFERENCES Parameter (id)
)
-- Many-To-Many MappingTable
CREATE TABLE Action2ParameterMapping
(
actionId BIGINT NOT NULL FOREIGN KEY REFERENCES Action (id),
parameterId BIGINT NOT NULL FOREIGN KEY REFERENCES Parameter (id),
PRIMARY KEY (actionId, parameterId)
)
I use Quarkus and have the simple PanacheRepository
#ApplicationScoped
public class ActionRepository implements PanacheRepository<Action> {
}
So now, when I now create an Action-Object holding Parameter-Objects and persist it using actionRepository.persist(action), I get an SQLServerException The INSERT statement conflicted with the FOREIGN KEY constraint "FK__Action2Pa__actio__4242D080 and I don't understand why.
I understand that it tries to tell me, that it wanted to persist an entry in the MappingTable but the actionId did not belong to any Action, but how can that be?
I don't understand, why this won't work.
After having the problem for over 3 days, I've solved almost right after asking the question...
The problem was within the DB-Test-Suite.
The #AfterEachmethod tried to delete parameters, which violated the Contraint...

Hibernate cascade save with OneToOne relation

I have two tables Users
CREATE TABLE `users` (
`id` BIGINT NOT NULL,
PRIMARY KEY (`id`))
and Carts
CREATE TABLE `carts` (
`id` BIGINT NOT NULL,
`user_id` BIGINT,
PRIMARY KEY (`id`))
ALTER TABLE `carts` ADD CONSTRAINT Cart_User_FK
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`);
I create entities for them
#Entity
#Table(name = "users")
public class User{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "user")
private Cart cart;
}
and
#Entity
#Table(name = "carts")
public class Cart{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id")
private User user;
}
I try to set cart instance to user and save user
Cart cart = new Cart();
cartRepository.save(cart);
user.setCart(cart);
userRepository.save(user);
but hibernate don't update cart table
repositories extended from SimpleJpaRepository
Could somebody explain me how to update related object?

Why doesn't Eclipselink generate the ON DELETE CASCADE clause in a unidirectional #ManyToOne relationship?

These are two related entities in Eclipselink JPA:
#Entity
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
#ManyToOne(cascade={CascadeType.ALL})
private Department department;
}
and this is the generated DDL:
CREATE TABLE PERSON (ID BIGINT IDENTITY NOT NULL, FIRSTNAME VARCHAR, LASTNAME VARCHAR, DEPARTMENT_ID BIGINT, PRIMARY KEY (ID))
CREATE TABLE DEPARTMENT (ID BIGINT IDENTITY NOT NULL, NAME VARCHAR, PRIMARY KEY (ID))
ALTER TABLE PERSON ADD CONSTRAINT FK_PERSON_DEPARTMENT_ID FOREIGN KEY (DEPARTMENT_ID) REFERENCES DEPARTMENT (ID)
The environment is:
- eclipselink 2.5.2
- mysql-connector-java 5.1.6
I would expect to have at least a ON DELETE CASCADE clause on the foreign key definition.
What is the cascade option intended for, in the #ManyToOne relationship?
Do I really have to delete the children records manually before deleting the parent record?
The CascadeType you mention in your example is one value of those: ALL, PERSIST, MERGE, REMOVE, REFRESH, DETACH. This is ORM, but not SQL DDL related.
I guess You are searching for a SQL DDL foreign key constraint definition ...on delete cascade. To geht this SQL DDL generated, You need an #CascadeOnDelete annotation, as shown in this example:
...
#OneToMany(mappedBy="abc", orphanRemoval=true, cascade={CascadeType.ALL})
#CascadeOnDelete
private List<MobilPhoneNumer> mobilePhonesNumbers;
...

unidirectional OneToMany mapping problems

I'm facing some problems with unidirectional onetomany mapping.
I got tables Users and Rubrica:
User (
scode double precision NOT NULL,
...
CONSTRAINT utenti_pkey PRIMARY KEY (scode)
)
Rubrica (
id serial NOT NULL,
id_owner integer NOT NULL,
id_contact integer NOT NULL,
CONSTRAINT rubrica_pkey PRIMARY KEY (id ),
CONSTRAINT rubrica_fk01 FOREIGN KEY (id_owner)
REFERENCES users (scode) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT rubrica_fk02 FOREIGN KEY (id_contact)
REFERENCES users (scode) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Don't mind about Users double PKey. It's a customer's table and I can't modify it. Rubrica store relation between its owner, a User, and contacts, a set of User too.
User is mapped as follow:
#SuppressWarnings("serial")
#Entity
#Table(name = "utenti", schema = "public")
public class User implements Serializable {
#Id
#Column(name = "scode", unique = true, nullable = false)
private Integer scode;
...
}
Ok. Here is when problems come. If I map Rubrica like this:
public class Rubrica2 implements Serializable {
#Id
#Column(name = "id", nullable = false, unique = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "id_owner", nullable = false, unique = true, referencedColumnName = "scode")
private User owner;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "id_contact", nullable = false, updatable = false, insertable = true, referencedColumnName = "scode")
private Set<User> relations = new HashSet<User>();
...
}
JBoss gave me this exception at deploy:
Caused by: org.hibernate.MappingException: Unable to find column with logical name: scode in org.hibernate.mapping.Table(public.rubrica) and its related
supertables and secondary tables
If I map Rubrica this way:
public class Rubrica2 implements Serializable {
#Id
#Column(name = "id", nullable = false, unique = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "id_owner", nullable = false, unique = true, referencedColumnName = "scode")
private User owner;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "scode")
private Set<User> relations = new HashSet<User>();
...
}
I got bad behavior at runtime. If I run this code
r = new Rubrica2();
q2.setParameter("id", ownerID);
User owner = (User) q2.getSingleResult();
r.setOwner(owner);
q2.setParameter("id", contactID);
User u = (User) q2.getSingleResult();
r.getRelations().add(u);
entityManager.persist(r);
I got this exception:
Hibernate: insert into public.rubrica (id_owner) values (?)
11:08:19,440 DEBUG [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (EJB default - 7)
ERROR: null values in column "id_contact" violates not-null constraint [n/a]: org.postgresql.util.PSQLException:
ERROR: null values in column "id_contact" violates not-null constraint
I follow theory indicated here about onetomany unidirectional.
I'm using JPA2.0, Hibernate4 (as provided by JBoss7.1.1.Final) and PostgresSQL.
This mapping, or the database design, makes no sense. If you want one Rubrica to have many contacts, you can't have a foreign key to the relation in the rubrica table. A foreign key can only reference one contact, not many.
To map such a one-to-many association, you would need a foreign key in user to rubrica (all the users having the same rubrica_id would be the contacts of this rubrica), or a join table between both tables.

jpa composite primary key and jointable with unique key

here are the sql tables:
option primary key is name + value
CREATE TABLE `option` (
id int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) NOT NULL,
`value` varchar(45) NOT NULL,
PRIMARY KEY (`name`, `value`),
UNIQUE KEY `id_UNIQUE` (`id`)
)
product primary key is incremental id
CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
)
a product has several options (referenced by unique key "id")
CREATE TABLE product_option (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_product` int(11) NOT NULL,
`id_option` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `id_product` (`id_product`),
KEY `id_option` (`id_option`),
CONSTRAINT `FK_product_option` FOREIGN KEY (`id_product`) REFERENCES `product` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_option_product` FOREIGN KEY (`id_option`) REFERENCES `option` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
)
On java side, i've mapped "option" this way
#Embeddable
public class OptionId implements Serializable{
#Column(name="value")
private String value;
#Column(name="name")
private String name;
}
#Entity #Table(name="option")
public class Option {
#Column(name="id", unique=true)
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#EmbeddedId
private OptionId primaryKey;
public OptionId getPrimaryKey() {
return primaryKey;
}
public void setPrimaryKey(OptionId primaryKey) {
this.primaryKey = primaryKey;
}
}
and product this way
#Entity
#Table(name="product")
public class Product {
#Id #Column(name="id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#Column(name="name")
private String name
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
#JoinTable(
name="product_option",
joinColumns = #JoinColumn( name="id_product"),
inverseJoinColumns = #JoinColumn( name="id_option", referencedColumnName="id")
)
private Set<Option> options;
}
then at execution time i get this error
Caused by: java.lang.ArrayIndexOutOfBoundsException: 1
at org.hibernate.sql.SelectFragment.addColumns(SelectFragment.java:107)
at org.hibernate.persister.collection.BasicCollectionPersister.manyToManySelectFragment(BasicCollectionPersister.java:308)
at org.hibernate.persister.collection.BasicCollectionPersister.selectFragment(BasicCollectionPersister.java:294)
at org.hibernate.loader.JoinWalker.selectString(JoinWalker.java:1070)
at org.hibernate.loader.AbstractEntityJoinWalker.initStatementString(AbstractEntityJoinWalker.java:124)
at org.hibernate.loader.AbstractEntityJoinWalker.initStatementString(AbstractEntityJoinWalker.java:109)
at org.hibernate.loader.AbstractEntityJoinWalker.initAll(AbstractEntityJoinWalker.java:91)
at org.hibernate.loader.AbstractEntityJoinWalker.initAll(AbstractEntityJoinWalker.java:78)
at org.hibernate.loader.entity.CascadeEntityJoinWalker.<init>(CascadeEntityJoinWalker.java:52)
at org.hibernate.loader.entity.CascadeEntityLoader.<init>(CascadeEntityLoader.java:47)
at org.hibernate.persister.entity.AbstractEntityPersister.createLoaders(AbstractEntityPersister.java:3254)
at org.hibernate.persister.entity.AbstractEntityPersister.postInstantiate(AbstractEntityPersister.java:3191)
at org.hibernate.persister.entity.SingleTableEntityPersister.postInstantiate(SingleTableEntityPersister.java:728)
at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:348)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1872)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:906)
at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:74)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:225)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:308)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)
... 60 more
if i remove "referencedColumnName", of course i get the error
A Foreign key refering Option from Product has the wrong number of column. should be 2
but i can't figure out how to fix this
Based on your schemas, your entity annotations are slightly off. Where you specify 'product' and 'option' you should be using 'id_product' and 'id_option':
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
#JoinTable(
name="product_option",
joinColumns = #JoinColumn( name="id_product"),
inverseJoinColumns = #JoinColumn( name="id_option", referencedColumnName="id")
)
private Set<Option> options;
As an aside, your life will be considerably easier, in the long run, if you switch the primary and unique keys on your option table.