使用Hibernate保存对象时收到以下错误

object references an unsaved transient instance - save the transient instance before flushing

当前回答

再加上我的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的空值

其他回答

再加上我的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);

只需在基类中创建映射的构造函数。就像你想要实体A、实体B中的一对一关系一样。如果你将A作为基类,那么A必须有一个构造函数,B作为参数。

介绍

使用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。

当我没有持久化父对象但我正在保存孩子时,我遇到了这个异常。为了解决这个问题,在同一个会话中,我保留了子对象和父对象,并在父对象上使用了CascadeType.ALL。