OneToOne bidirectional mapping foreign key auto fill

泄露秘密 提交于 2019-12-11 03:34:47

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!