假设,我们有两个简单的实体
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
。
问题就消失了
getChilds().size()
非常感谢您的所有提示, 多米尼克
每当您对
get()
变量调用
childs
方法时,它将通过代理类
PersistentList
进行初始化。持久列表只能包含活动会话对象。在第一种情况下,你在向代理类添加一个transiet对象后做了一个
get()
,代理在尝试将其与数据库同步时会出现问题。在第二种情况下,您已经通过执行
childs
调用使用实际列表初始化了
size()
变量,因此它不会抱怨。