休眠-至少一个孩子

时间:2018-08-22 06:51:17

标签: java hibernate

这个问题问我要达到的目标,但实际上并没有答案:Hibernate validateManyToOnehas at least one

我有两个对象(A和B)。 A是父母。 B是孩子。这是一对多的关系,但是,我需要每个A至少有一个B。B中所有字段都有默认值,因此,如果创建的A没有B,则可以将默认B添加到确保始终有一个B。如果添加了一个或多个B对象,则无需创建默认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";
    }
    ...
}

这是B:

[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,那么在日志中我只会得到:

  

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

并且创建了一个不带B的A。如果我尝试创建一个至少包含一个B的A,则会出现以下错误:

  

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

有什么想法可以使我工作吗?

1 个答案:

答案 0 :(得分:0)

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

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

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

  2. 在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中,您的mappedBy中有@OneToMany),那么它应该起作用。

编辑:

实际上,可能有第三个选择:创建新的A时添加默认元素,而添加实元素时将其删除。这样,您就不必弄乱Hibernate会话或事务作用域了。