【问题标题】:Hibernate - At least one child休眠 - 至少一个孩子
【发布时间】:2018-08-22 06:51:16
【问题描述】:

这个问题有点问我想要实现什么,但没有真正的答案:Hibernate validateManyToOnehas at least one

我有两个对象(A 和 B)。 A 是父母。 B是孩子。这是一对多的关系,但是,我需要每个 A 始终至少有一个 B。B 中的所有字段都有默认值,因此如果在没有 B 的情况下创建 A,则可以将默认 B 添加到确保始终有一个 B。如果添加了一个或多个 B 对象 A,则无需创建默认 B。

这是A:

[Fields]

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "key", nullable = false)
@Fetch(value = FetchMode.SUBSELECT)
private List<B> b = new ArrayList<>();

...

@PrePersist
protected void onCreate() {
    // Default values configured here, for example
    if (fieldA1 == null) {
        fieldA1 = "A DEFAULT";
    }
    ...
}

这是乙:

[Fields]

@PrePersist
protected void onCreate() {
    // Default values configured here, for example
    if (fieldB1 == null) {
        fieldB1 = "B DEFAULT";
    }
    ...
}

我以为我可以在A中使用相同的@PrePersist注解,检查是否有任何B对象,如果没有则创建一个默认B:

@PrePersist
protected void onCreate() {
    // Deafult values configured here
    ...
    if (b.size() == 0) {
        b.add(new B());
    }
}

这行不通。如果我创建一个没有 B 对象的 A,那么在日志中我只会得到:

在删除处理中处理瞬态实体

并且 A 是在没有 B 的情况下创建的。如果我尝试创建一个带有至少一个 B 的 A,那么我会收到以下错误:

引起:org.hibernate.TransientObjectException:对象引用 未保存的瞬态实例 - 之前保存瞬态实例 冲洗

有什么想法可以让我完成这项工作吗?

【问题讨论】:

  • 顺便说一句,我想赞扬你的问题。很少看到新的贡献者在他们的问题上付出了如此多的努力。
  • @Thomas,感谢您的客气话。这可能是我的第一个问题,但我已经在这里潜伏了很多年 :) 我见过好问题和坏问题,这有助于了解如何最好地构建它。

标签: java hibernate


【解决方案1】:

在不了解您的代码的情况下(无论如何对于 SO 来说这可能有点过多),我将不得不做出一些假设,但我会说当 onCreate() 被调用时,Hibernate 会话已经在“刷新”状态,因此不会接受任何新实体。

目前我可以想到 2 个选项,我们在某些情况下也会使用:

  1. 一旦当前事务成功完成,就会引发 CDI 事件并让事件处理程序(异步)触发在新事务中创建默认元素。

  2. 在 Hibernate 中创建一个从当前会话分叉并使用相同事务的子会话。然后使用这个子会话来创建你的默认元素。

这是我们在 Hibernate 中的操作方法PostInsertEventListener

if( postInsertEvent.getEntity() instanceof ClassThatNeedsToBeHandled ) {
  ClassThatNeedsToBeHandled insertedEntity = (ClassThatNeedsToBeHandled )postInsertEvent.getEntity(); 
  Session subSession = postInsertEvent.getSession().sessionWithOptions()
    .connection() //use the same connection as the parent session                  
    .noInterceptor() //we don't need additional interceptors
    .flushMode( FlushMode.AUTO ) //flush on close
    .autoClose( true) //close after the transaction is completed
    .autoJoinTransactions( true ) //use the same transaction as the parent session
    .openSession();

  subSession.saveOrUpdate( new SomeRelatedEntity( insertedEntity ) );
}

要记住的一点是,我们的SomeRelatedEntity 是关系的所有者。您的代码表明 A 将是所有者,但这可能会导致问题,因为您必须在刷新期间更改 A 实例才能使关系保持不变。如果B 是拥有方(它有一个对A 的反向引用,并且在A 你的@OneToMany 中有一个mappedBy)它应该可以工作。

编辑:

实际上,可能还有第三种选择:当你创建一个新的 A 时,添加一个默认元素,当你添加真正的元素时,你删除它。这样您就不必弄乱 Hibernate 会话或事务范围。

【讨论】:

  • 第三个选项是我首先想到的,因为这不应该给休眠部分增加任何复杂性(有时可能非常棘手)
  • @XtremeBaumer 哦,是的,它可以:)
  • 感谢您解释为什么我会看到这些消息/错误。说得通。我试图在休眠级别拦截缺少子对象,因为这似乎是有道理的,特别是因为我已经在 @PrePersist 中设置了默认值。您的第三个选项绝对是要走的路,因为似乎没有一种直接的休眠方式来解决这个问题。我已经实现了您的建议,并且在其余控制器中创建默认子对象效果很好。
猜你喜欢
  • 2016-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多