休眠和休眠JPA&"mappedBy&"与关系的所有者& amp;级联 [英] Hibernate & JPA "mappedBy" vs. owner of the relation & cascades

查看:54
本文介绍了休眠和休眠JPA&"mappedBy&"与关系的所有者& amp;级联的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

假设我们具有双向的 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--关系所有者-级联-管理管理设置器中的另一端

问题:

  1. 在数据库中,地址表表中应该有user_id,对吗?那么关系的所有者是谁?
  2. 在实体中:应该映射哪个实体-( 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 are User and Address entity. User has many Addresses.

      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 for ManyToMany as well.

      TL;DR - the question is about using mappedBy<->relation owner<->cascades<->managing other side in setters in general

      Questions:

      1. In database there should be user_id in address table table, right? Who is the owner of the relation then?
      2. In entities: which one should have mappedBy - (User or Address) / (Owner or Inverse)?
      3. On which side (User or Address) / (mappedBy or inverse-side) the @OneToOne(cascade = {CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH}) should be placed?
      4. 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 or Address) / (mappedBy or inverse-side)?

      Moreover - look at theese two links:

      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 to OneToOne 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 child post_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 the comments collection:

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

      And, in the PostComment, the post 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 new post_comment row should be associated with the parent post entity via its post_id column.
      • By setting the post property of the PostComment entity, the post_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 and removeComment 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&amp;"mappedBy&amp;"与关系的所有者&amp; amp;级联的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆