【问题标题】:Self-referencing ManyToMany relations in HibernateHibernate 中的自引用多对多关系
【发布时间】:2017-09-26 09:44:03
【问题描述】:

我正在寻找一种方法来模拟 Hibernate 中同一类的两个或多个对象之间的关系。例如:我想创建一个我想模拟人与人之间关系的情况。在数据库中,我有两个表:

人:

  • 身份证
  • 姓名

关系:

  • 父 ID
  • 孩子身份证

我尝试在 Hibernate 中将其建模为 Person 有两个 ManyToMany 关系(Getter/Setter 注释是 Lombok):

@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "persons")
public class Person {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long Id

  @Column(name="name")
  private String name;


 @ManyToMany(fetch = FetchType.LAZY)
  @JoinTable(
      name = "relations",
      joinColumns = {@JoinColumn(name = "parent_id", nullable = false)},
      inverseJoinColumns = {@JoinColumn(name = "child_id", nullable = false)}
  )
  private Set<Person> children = new HashSet<>();

  @ManyToMany(fetch = FetchType.LAZY)
  @JoinTable(
      name = "relations",
      joinColumns = {@JoinColumn(name = "child_id", nullable = false)},
      inverseJoinColumns = {@JoinColumn(name = "parent_id", nullable = false)}
  )
  private Set<Person> parents = new HashSet<>();
}

这给了我以下问题:

  • 当获取类型为“LAZY”时,Hibernate 抱怨在调用 person.getChildren() 或 person.getParents() 时没有会话
  • 对于获取类型“EAGER”,Hibernate 会为集合返回 null,这会在尝试添加子级或父级时导致空指针。我担心的另一件事是可能的无休止的递归渴望获取。

为了解决这个问题,我尝试将 Relation 建模为一个类,这样我就可以在 PersonRepository 中使用 JPA 查询来查找孩子和父母,而不必弄乱 ManyToMany 的复杂性:

public interface PersonRepository extends JpaRepository<Person, Long> {
  @Query(
      "select p from Person p join Relation r on p.id = r.childId where r.childId = :childId"
  )
  List<Person> findParents(Long childId);
}

这导致 Hibernate 抱怨 Relation 没有 @Id 字段。我不想添加,因为数据库中的关系表应该对关系进行建模,并且没有自己的 id。

当尝试在线搜索 Hibernate 中的类似结构时,我通常会在 the documentation 中找到经典的多对多示例,以及像 this one 这样的问题,其中两个不同类之间存在关系同一类的两个对象之间的关系。

我正在寻找一种方法来模拟 Hibernate 中同一类的两个或多个对象之间的关系。我希望能够延迟获取关系,或者出于性能原因之后获取它们。

如果关系表可以有一个额外的字段“type”来指示人(孩子、父母、侄子、朋友)之间的关系类型,那就太好了,这样就可以为新的关系类型留出空间,而无需太多改变数据库和代码。

【问题讨论】:

  • 当涉及到使用链接表中的类型字段时,也许我在这里的回答会有所帮助:stackoverflow.com/questions/41732211/…
  • 谢谢,但这映射了两个不同类之间的多对多关系。我正在尝试将 Persons 映射到 Persons。

标签: java hibernate many-to-many


【解决方案1】:

不确定我是否理解正确,但我曾经遇到过类似的情况。 我所做的是在没有关系的情况下保留子/父,然后用它们的关系更新它们(仍然在同一个事务中)。

 private void insertEntity(final AbstractEntity entity) {
    insertedEntities.add(entity.getId());

    final List<AbstractEntity> relations = entity.onBeforeInsertion();
    for (final AbstractEntity relation : relations) {
        if (!insertedEntities.contains(relation.getId())) {
            insertEntity(relation);
        }
    }

    final RelationBundle relationBundle = new RelationBundle();
    entity.onInsertion(relationBundle);

    immediatelySaveNewEntityThroughProxy(entity);

    for (final AbstractEntity relation : relations) {
        entity.onRelationInsertion(relation, relationBundle);
    }

    dao.saveOrUpdate(entity);
}


private void immediatelySaveNewEntityThroughProxy(final DocumentEntity entity) {
    proxiedAccess().immediatelySaveNewEntity(entity);
}

private MyConsumer proxiedAccess() {
    return applicationContext.getBean(getClass());
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void immediatelySaveNewEntity(final DocumentEntity entity) {
    try {
        if (!dao.entityExistsFromId((int) entity.getId())) {
            dao.save(entity);
        }
    } catch (final Exception e) {
        LOGGER.error("Error saving entity: {}", entity.getId(), e);
    }
}

【讨论】:

  • 我试过这样做,但 Person.getChildren 返回 null,根据我的理解,这不应该发生。我该怎么做才能澄清这个问题?
  • 只有在更新失败时才会发生这种情况。如果您使用的是 Spring,则可能需要一个新的 Proxy 实例。参考:stackoverflow.com/questions/15767914/…docs.spring.io/spring/docs/3.2.x/spring-framework-reference/…
  • 用一些代码更新了答案。像onBeforeXXX 这样的事件只是删除关系并将它们保存到一个Bundle。
  • 我将关系建模为一个单独的关系,一个 ID,两个 Longs 作为父 ID 和子 ID。这个项目很匆忙。 Relation 存储库具有基于子 ID 获取父 Id 的方法,我使用这些 Id 来获取实际的人员对象。次优但不会影响预取的性能,并且不会导致过期会话出现问题。我真的很想将项目迁移到 MyBatis。
猜你喜欢
  • 2018-08-19
  • 2017-02-05
  • 2011-05-09
  • 1970-01-01
  • 2020-12-26
  • 2021-05-20
相关资源
最近更新 更多