使用Hibernate保存对象时收到以下错误
object references an unsaved transient instance - save the transient instance before flushing
使用Hibernate保存对象时收到以下错误
object references an unsaved transient instance - save the transient instance before flushing
当前回答
当我没有持久化父对象但我正在保存孩子时,我遇到了这个异常。为了解决这个问题,在同一个会话中,我保留了子对象和父对象,并在父对象上使用了CascadeType.ALL。
其他回答
就我而言,问题完全不同。我有两节课,比如c1和c2。C1和C2之间的依赖关系是OneToMany。现在,如果我在DB中保存C1,它将抛出上述错误。
解决这个问题的方法是从消费者请求中获取第一个C2的id,然后通过存储库调用找到C2。然后将C2保存到C1对象中。现在,如果我保存C1,它可以正常工作。
当您具有OneToMany关系并且尝试将子实体添加到父实体中的列表中,然后通过父实体检索此列表(在保存此父实体之前),而不保存子实体本身时,也可能发生这种情况,例如:
Child childEntity = new Child();
parentEntity.addChild(childEntity);
parentEntity.getChildren(); // I needed the retrieval for logging, but one may need it for other reasons.
parentRepository.save(parentEntity);
保存父实体时引发了错误。如果我删除了前一行中的检索,则不会抛出错误,但当然这不是解决方案。
解决方案是保存子实体并将保存的子实体添加到父实体,如下所示:
Child childEntity = new Child();
Child savedChildEntity = childRepository.save(childEntity);
parentEntity.addChild(savedChildEntity);
parentEntity.getChildren();
parentRepository.save(parentEntity);
介绍
使用JPA和Hibernate时,实体可以处于以下4种状态之一:
新建-新创建的对象从未与Hibernate会话(也称为持久性上下文)关联,且未映射到任何数据库表行,则被视为处于新建或临时状态。
为了持久化,我们需要显式调用持久化方法或使用传递持久化机制。
持久性-持久性实体已与数据库表行关联,并由当前运行的持久性上下文管理。
对此类实体所做的任何更改都将被检测并传播到数据库(在会话刷新时间内)。
分离-一旦当前运行的Persistence Context关闭,所有以前管理的实体都将分离。将不再跟踪连续的更改,并且不会发生自动数据库同步。移除-虽然JPA要求只允许移除托管实体,但Hibernate也可以删除分离的实体(但只能通过移除方法调用)。
实体状态转换
要将实体从一个状态移动到另一个状态,可以使用persistent、remove或merge方法。
解决问题
您在问题中描述的问题:
object references an unsaved transient instance - save the transient instance before flushing
是通过将状态为“新建”的实体与状态为“托管”的实体关联而导致的。
当您将子实体关联到父实体中的一对多集合,并且集合不级联实体状态转换时,可能会发生这种情况。
因此,您可以通过向触发此故障的实体关联添加级联来解决此问题,如下所示:
@OneToOne协会
@OneToOne(
mappedBy = "post",
orphanRemoval = true,
cascade = CascadeType.ALL)
private PostDetails details;
请注意我们为级联属性添加的CascadeType.ALL值。
@OneToMany协会
@OneToMany(
mappedBy = "post",
orphanRemoval = true,
cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>();
同样,CascadeType.ALL适用于双向@OneToMany关联。
现在,为了使级联在双向中正常工作,还需要确保父关联和子关联同步。
@ManyToMany协会
@ManyToMany(
mappedBy = "authors",
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
}
)
private List<Book> books = new ArrayList<>();
在@ManyToMany关联中,不能使用CascadeType.ALL或orphanRemove,因为这会将删除实体状态转换从一个父实体传播到另一个父图元。
因此,对于@ManyToMany关联,通常级联CascadeType.PERSIST或CascadeType.MERGE操作。或者,可以将其扩展到DETACH或REFRESH。
再加上我的2美分,当我意外地发送null作为ID时,我也遇到了同样的问题。下面的代码描述了我的场景(OP没有提到任何特定的场景)。
Employee emp = new Employee();
emp.setDept(new Dept(deptId)); // --> when deptId PKID is null, same error will be thrown
// calls to other setters...
em.persist(emp);
在这里,我将现有部门id设置为一个新的雇员实例,而实际上没有首先获取部门实体,因为我不想再启动另一个select查询。
在某些情况下,deptId PKID在调用方法时变为null,我得到了同样的错误。
因此,请注意PK ID的空值
另一个可能的原因是:在我的案例中,我试图在一个全新的实体上,先救孩子,再救父母。
User.java模型中的代码如下:
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.setNewPassword(password);
this.timeJoin = new Date();
create();
setNewPassword()方法创建PasswordHistory记录,并将其添加到User中的历史记录集合中。由于尚未为父级执行create()语句,因此它试图保存到尚未创建的实体集合中。我所要做的就是在调用create()之后移动setNewPassword()调用。
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.timeJoin = new Date();
create();
this.setNewPassword(password);