问题
I have a relationship one-to-one between two tables, but the foreign key is on the one I don't need to map, the DBA did this in favor of future changes.
Let's imagine we have User and Address, today every user has only one address, and it will be mapped this way, but DBA believe in the future it could be a one to many mapping, so the foreign key of user is on the address, but the application have instances of users, which is important to fetch address automatically.
We did it right, as follow:
@Entity
@Table(name = "user")
class User {
@Id
@Column(name = "user_id")
private Long id;
//...
@OneToOne(cascade = CascadeType.MERGE, mappedBy = "user")
private Address address; // this attribute is crucial
}
@Entity
@Table(name = "address")
class Address {
@Id
@Column(name = "address_id")
private Long id;
@OneToOne
@JoinColumn(name = "user_id")
private User user; // this attribute is not needed for the business at all, but mappedBy requires it
//...
}
Database:
-- SQL:
CREATE TABLE user
(
user_id INT NOT NULL,
-- ...
CONSTRAINT user_pk PRIMARY KEY (user_id)
);
CREATE TABLE address
(
address_id INT NOT NULL,
user_id INT NOT NULL,
-- ...
CONSTRAINT address_pk PRIMARY KEY (address_id),
CONSTRAINT address_user_id_fk FOREIGN KEY (user_id) REFERENCES user (user_id),
CONSTRAINT address_user_id_uk UNIQUE (user_id) -- it says it's a one to one relation for now, if in the future it won't be anymore, just remove this constraint
);
The problem is when save a instance of user with a new instance of address, the user's attribute of address is null
, so I was expecting Hibernate was smart enough to set it the value from the user's instance it comes from.
I'd been looking for a solution for a couple of days, but still didn't find how to solve this, meanwhile I'm setting the value manually, but I expect I don't need to do so.
回答1:
The standard solution is to properly update both sides of the bidirectional association (although only the owning side needs to be updated for the association to be saved to the database). Add to the Address
setter in the User
class:
public void setAddress(Address address) {
this.address = address;
address.setUser(this);
}
Also, you may want to extend cascading options for the address
property to include PERSIST
as well, so that it is always persisted together with its user:
@OneToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "user")
private Address address;
Then you can set an address to a user and persist both:
user.setAddress(address);
session.persist(user);
回答2:
If "private User user" is not needed, delete it and delete also mappedBy in 'User' entity. Use a uni-directional relation.
In the case of your example, mappedBy means that the owner of the association is the 'Address' entity, so save the instance of Adress and not User. Like :
adress.setUser(user);
session.save(adress);
回答3:
You need to add CasecadeType.PERSIST to make the creation Address casecade with creation of User.
In your java code, you need to do:
user.setAddress(address);
address.setUser(user);
session.persist(user);
Then your user will be created with an address.
If when you just want to read the Address of a new created User, then you need to do :
// your code to persist a new User and Address
session.flush();
session.refresh(user);
If it's not what you want, then you need to share your own Java code and give a detailed description on what you're expecting.
来源:https://stackoverflow.com/questions/31517989/onetoone-bidirectional-mapping-foreign-key-auto-fill