在 Hibernate 中将实体添加到未初始化的延迟集合时提交期间发生 TransientObjectException

问题描述 投票:0回答:1

假设,我们有两个简单的实体

Parent
Child
,如下所示:

@Entity
public class Parent extends BaseEntity {
    private String content;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Child> childs = new ArrayList<>();
}
@Entity
public class Child extends BaseEntity {
    @NotNull
    @ManyToOne
    private Parent parent;

    private int value;
}

BaseEntity
仅处理ID管理,不包含任何特殊内容。 本质上,我们有一个包含子实体列表的父实体。这种关系是惰性的,所有操作都是级联的,Child orphans在不再被引用时被移除。

现在我们检索一个父级并向其添加一个新的子级,如下所示:

@Transactional
public void run() {
        var p1 =  entityManager.find(Parent.class, 1L);

        var newChild = Child.builder()
                .value(999)
                .parent(p1)
                .build();
        p1.getChilds().add(newChild);

        entityManager.merge(p1);
}

这运行良好,并且在事务提交时添加创建的 newChild。 但是,如果我们访问父级的子集合并强制 Hibernate 填充该集合,如下所示

size()
-调用:

@Transactional
public void run() {
        var p1 =  entityManager.find(Parent.class, 1L);

        var newChild = Child.builder()
                .value(999)
                .parent(p1)
                .build();
        p1.getChilds().add(newChild);

        p1.getChilds().size();

        entityManager.merge(p1);
}

抛出异常:

org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: de.lmis.lazycollection.model.Child

问题就消失了

    在添加 newChild 之前调用
  • getChilds().size()
  • orphanRemoval 已关闭。
对我来说,这看起来很奇怪,我看不到这种行为的有意义的解释。对我来说,惰性集合包括这样的承诺:在需要时获取所需的数据,并且用户不必处理这种情况是否以及何时发生的问题。如果没有其他解释,我总是必须确保在向集合添加一些内容之前正确填充集合。 我哪里被误导了?

非常感谢您的所有提示, 多米尼克

hibernate jpa lazy-initialization
1个回答
0
投票
我认为您由于以下原因而收到此错误。

每当您对

get()

 变量调用 
childs
 方法时,它将通过代理类 
PersistentList
 进行初始化。持久列表只能包含活动会话对象。

在第一种情况下,你在向代理类添加一个transiet对象后做了一个

get()

,代理在尝试将其与数据库同步时会出现问题。

在第二种情况下,您已经通过执行

childs

 调用使用实际列表初始化了 
size()
 变量,因此它不会抱怨。

© www.soinside.com 2019 - 2024. All rights reserved.