Hibernate 和 JPA“mappedBy”与关系和级联的所有者

Kam*_*mil 4 java sql hibernate

背景

\n\n

假设我们有双向OneToOne关系。\n有UserAddress实体。User有很多Addresses。

\n\n
CREATE SEQUENCE IF NOT EXISTS hibernate_sequence;\nCREATE TABLE users (\n  id BIGINT PRIMARY KEY\n);\n\nCREATE TABLE addresses (\n  id BIGINT PRIMARY KEY,\n  user_id BIGINT NOT NULL UNIQUE CONSTRAINT fk_addresses_user_id REFERENCES users(id) ON DELETE CASCADE,\n);\n
Run Code Online (Sandbox Code Playgroud)\n\n

\n\n
@Table(name = "users")\npublic class User {\n\n  @Id\n  @GeneratedValue(strategy = GenerationType.SEQUENCE)\n  private Long id;\n\n  //mappings\n  private Address address;\n}\n\n@Table(name = "addresses")\npublic class Address {\n\n  @Id\n  @GeneratedValue(strategy = GenerationType.SEQUENCE)\n  private Long id;\n\n  //mapings\n  private User user;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

免责声明:

\n\n

我用OneToOne的是一个例子,但问题是关于一般关系的,所以正确的方法也是有效的ManyToMany

\n\n

TL;DR - 问题是关于mappedBy<->relation owner<->cascades<->managing other side in setters一般使用

\n\n

问题:

\n\n
    \n
  1. 数据库中的地址表表中应该有user_id吧?那么谁是关系的所有者呢?
  2. \n
  3. 在实体中:哪一个应该映射为 - (UserAddress) / (Owner 或 Inverse)?\n\n
  4. \n
  5. 应放置在哪一侧(UserAddress)/(mappedBy 或反面) ?@OneToOne(cascade = {CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
  6. \n
  7. 最后一个问题:哪个实体应该像这样管理关系:
  8. \n
\n\n
public void addAddress(Address address) {\n  if (address != null) {\n    address.addUser(this);\n  }\n  this.addresses.add(address);\n}\n\npublic void removeAddress(Address address) {\n  if (address != null) {\n    address.removeUser(this);\n  }\n  this.addresses.remove(address);\n}\n\npublic Set<Address> getAddresses() {\n  return Collections.unmodifiableSet(this.addresses);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

(UserAddress) / (mappedBy 或反面)?

\n\n\n\n

此外 - 看看这两个链接:

\n\n\n\n

先感谢您

\n\n

附言。我的问题的先前版本用作OneToMany示例,但因为逆 -ManyToOne没有映射,所以我将其更改为OneToOne更好地显示问题

\n

Vla*_*cea 6

表关系与实体关系

在关系数据库系统中,表关系分为三种类型:

  • 一对多(通过外键列)
  • 一对一(通过共享主键)
  • 多对多(通过具有两个外键引用两个单独父表的链接表)

因此,one-to-many表关系如下所示:

一对多表关系

请注意,该关系基于post_idpost_comment表中的外键列。

因此,在管理表关系时,外键列是唯一的事实来源one-to-many

现在,让我们采用一个双向 JPA 实体关系来映射one-to-many我们之前看到的表关系:

双向一对多实体关联

如果您看一下上图,您会发现有两种方法可以管理这种关系。

Post实体中,您拥有以下comments集合:

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
Run Code Online (Sandbox Code Playgroud)

并且,在 中PostCommentpost关联映射如下:

@ManyToOne(
    fetch = FetchType.LAZY
)
@JoinColumn(name = "post_id")
private Post post;
Run Code Online (Sandbox Code Playgroud)

因此,您有两个可以更改实体关联的方面:

  • 通过在子集合中添加条目comments,新post_comment行应post通过其post_id列与父实体关联。
  • 通过设置实体post的属性PostComment,该post_id列也应该更新。

由于有两种方法来表示外键列,因此在将关联状态更改转换为其等效的外键列值修改时,您必须定义哪种方法是真实来源。

MappedBy(又名反面)

mappedBy属性告诉@ManyToOne一侧负责管理外键列,并且该集合仅用于获取子实体并将父实体状态更改级联到子实体(例如,删除父实体也应该删除子实体)。

它被称为反面,因为它引用管理此表关系的子实体属性。

同步双向关联的双方

现在,即使您定义了mappedBy属性并且子端@ManyToOne关联管理外键列,您仍然需要同步双向关联的两侧。

最好的方法是添加这两个实用方法:

public void addComment(PostComment comment) {
    comments.add(comment);
    comment.setPost(this);
}

public void removeComment(PostComment comment) {
    comments.remove(comment);
    comment.setPost(null);
}
Run Code Online (Sandbox Code Playgroud)

addComment方法removeComment确保双方同步。因此,如果我们添加一个子实体,则子实体需要指向父实体,并且父实体应该将子实体包含在子集合中。