休眠和休眠JPA&"mappedBy&"与关系的所有者& amp;级联 [英] Hibernate & JPA "mappedBy" vs. owner of the relation & cascades
问题描述
背景
假设我们具有双向的 OneToOne
关系.有 User
和 Address
实体. User
有许多 Address
es.
在不存在时创建序列hibernate_sequence;CREATE TABLE用户(ID BIGINT主键);创建表地址(ID BIGINT主键,user_id BIGINT不是NULL唯一约束fk_addresses_user_id在删除级联上引用users(id),);
和
@Table(name ="users")公共类用户{@ID@GeneratedValue(策略= GenerationType.SEQUENCE)私人Long ID;//映射私人地址;}@Table(name =地址")公共课程地址{@ID@GeneratedValue(策略= GenerationType.SEQUENCE)私人Long ID;//映射私人用户用户;}
免责声明:
我以 OneToOne
为例,但问题是有关关系的,因此正确的方法对 ManyToMany
同样有效.
TL; DR-问题是关于通常使用 mappedBy--关系所有者-级联-管理管理设置器中的另一端
问题:
- 在数据库中,地址表表中应该有user_id,对吗?那么关系的所有者是谁?
- 在实体中:应该映射哪个实体-(
User
或Address
)/(所有者或反向)?- 此链接:
请注意,该关系基于子
post_comment
表中的post_id
外键列.因此,在管理
一对多
表关系时,外键列是唯一的事实来源.现在,让我们采用双向JPA实体关系,该关系映射我们之前看到的
一对多
表关系:看看上面的图,您会发现有两种方法可以管理这种关系.
在
Post
实体中,您具有comments
集合:@OneToMany(mappingBy ="post",级联= CascadeType.ALL,orphanRemoval = true)私人列表< PostComment>评论=新的ArrayList<>();
而且,在
PostComment
中,post
关联的映射如下:@ManyToOne(提取= FetchType.LAZY)@JoinColumn(名称="post_id")私人邮政;
因此,您有两个方面可以更改实体关联:
- 通过在
comments
子集合中添加条目,新的post_comment
行应通过其与其父
列.post
实体相关联> post_id - 通过设置
PostComment
实体的post
属性,还应更新post_id
列.
因为有两种表示外键列的方法,所以在将关联状态更改转换为等效的外键列值修改时,必须定义哪个是真相的来源.
MappedBy(也称为反面)
mappedBy
属性告诉@ManyToOne
一方负责管理外键列,并且该集合仅用于获取子实体并进行级联父实体状态更改为子实体(例如,删除父实体也应删除子实体).之所以称为反面,是因为它引用了管理此表关系的子实体属性.
同步双向关联的两端
现在,即使您定义了
mappedBy
属性,并且由子端的@ManyToOne
关联管理外键"列,您仍然需要同步双向关联.做到这一点的最佳方法是添加以下两个实用程序方法:
public void addComment(PostComment注释){comments.add(comment);comment.setPost(this);}公共无效removeComment(PostComment评论){comments.remove(comment);comment.setPost(null);}
addComment
和removeComment
方法可确保双方已同步.因此,如果我们添加一个子实体,则该子实体需要指向父实体,并且该父实体应在子集合中包含该子实体.Background
Suppose we have bidirectional
OneToOne
relationship. There areUser
andAddress
entity.User
has manyAddress
es.CREATE SEQUENCE IF NOT EXISTS hibernate_sequence; CREATE TABLE users ( id BIGINT PRIMARY KEY ); CREATE TABLE addresses ( id BIGINT PRIMARY KEY, user_id BIGINT NOT NULL UNIQUE CONSTRAINT fk_addresses_user_id REFERENCES users(id) ON DELETE CASCADE, );
and
@Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id; //mappings private Address address; } @Table(name = "addresses") public class Address { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id; //mapings private User user; }
Disclaimer:
I'm using
OneToOne
as an example but the question is about relations in general so the right approach is valid forManyToMany
as well.TL;DR - the question is about using
mappedBy<->relation owner<->cascades<->managing other side in setters
in generalQuestions:
- In database there should be user_id in address table table, right? Who is the owner of the relation then?
- In entities: which one should have mappedBy - (
User
orAddress
) / (Owner or Inverse)?- This link: https://coderanch.com/t/595728/databases/Newbie-Hibernate-OneToMany-owning-side states: "Note that the mappedBy automatically makes the relationship the inverse or non-owning side." so
Address
should have mappedBy - While in this link: https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/
Post
(User
in my example) have mappedBy toPostDetails
(Address
in my example) - Javadoc: https://docs.oracle.com/javaee/7/api/javax/persistence/OneToOne.html#mappedBy() says "The field that owns the relationship" so
User
owns the relation, and thereforeAddress
should have mappedBy (CustomerRecord
in JavaDoc) - But hibernate documentation: http://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#associations-one-to-one-bidirectional the parent
Phone
(Person
in my example) has mappedBy toPhoneDetails
(Address
)
- This link: https://coderanch.com/t/595728/databases/Newbie-Hibernate-OneToMany-owning-side states: "Note that the mappedBy automatically makes the relationship the inverse or non-owning side." so
- On which side (
User
orAddress
) / (mappedBy or inverse-side) the@OneToOne(cascade = {CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
should be placed? - And last question: which of the entities should manage the relation like this:
public void addAddress(Address address) { if (address != null) { address.addUser(this); } this.addresses.add(address); } public void removeAddress(Address address) { if (address != null) { address.removeUser(this); } this.addresses.remove(address); } public Set<Address> getAddresses() { return Collections.unmodifiableSet(this.addresses); }
the (
User
orAddress
) / (mappedBy or inverse-side)?- This link: https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/ shows the approach listed above (where the parent,
User
in my example, manages the relation) - But here: https://www.quora.com/What-is-the-purpose-of-mapped-by-element-in-OneToMany-annotation-in-Hibernate Vlad says: "it’s always good practice if the child side, where the Foreign Key is located, controls the association", so the Address should "controll the relation" (exactly the opposite)?
Moreover - look at theese two links:
- https://docs.oracle.com/javaee/7/api/javax/persistence/OneToOne.html - here CustomerRecord has mappedBy
- https://docs.oracle.com/javaee/7/api/javax/persistence/OneToMany.html - but here Customer has mappedBy!
Thank you in advance
PS. Previous version of my question was using
OneToMany
as an example, but because inverse -ManyToOne
does not have mapped by I changed it toOneToOne
which better shows the problem解决方案Table relationships vs. entity relationships
In a relational database system, there are three types of table relationships:
- one-to-many (via a Foreign Key column)
- one-to-one (via a shared Primary Key)
- many-to-many (via a link table with two Foreign Keys referencing two separate parent tables)
So, a
one-to-many
table relationship looks like this:Note that the relationship is based on the
post_id
Foreign Key column in the childpost_comment
table.So, the Foreign Key column is the single source of truth when it comes to managing a
one-to-many
table relationship.Now, let's take a bidirectional JPA entity relationship that maps the
one-to-many
table relationship we saw previously:If you take a look at the diagram above, you can see that there are two ways to manage this relationship.
In the
Post
entity, you have thecomments
collection:@OneToMany( mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true ) private List<PostComment> comments = new ArrayList<>();
And, in the
PostComment
, thepost
association is mapped as follows:@ManyToOne( fetch = FetchType.LAZY ) @JoinColumn(name = "post_id") private Post post;
So, you have two sides that can change the entity association:
- By adding an entry in the
comments
child collection, a newpost_comment
row should be associated with the parentpost
entity via itspost_id
column. - By setting the
post
property of thePostComment
entity, thepost_id
column should be updated as well.
Because there are two ways to represent the Foreign Key column, you must define which is the source of truth when it comes to translating the association state change into its equivalent Foreign Key column value modification.
MappedBy (a.k.a the inverse side)
The
mappedBy
attribute tells that the@ManyToOne
side is in charge of managing the Foreign Key column, and the collection is used only to fetch the child entities and to cascade parent entity state changes to children (e.g., removing the parent should also remove the child entities).It's called the inverse side because it references the child entity property that manages this table relationship.
Synchronize both sides of a bidirectional association
Now, even if you defined the
mappedBy
attribute and the child-side@ManyToOne
association manages the Foreign Key column, you still need to synchronize both sides of the bidirectional association.The best way to do that is to add these two utility methods:
public void addComment(PostComment comment) { comments.add(comment); comment.setPost(this); } public void removeComment(PostComment comment) { comments.remove(comment); comment.setPost(null); }
The
addComment
andremoveComment
methods ensure that both sides are synchronized. So, if we add a child entity, the child entity needs to point to the parent and the parent entity should have the child contained in the child collection.这篇关于休眠和休眠JPA&"mappedBy&"与关系的所有者& amp;级联的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- 通过在
- 此链接: