使用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的空值

其他回答

在引入乐观锁定(@Version)之后,我对所有PUT HTTP事务都面临同样的错误

在更新实体时,必须发送该实体的id和版本。如果任何实体字段与其他实体相关,那么对于该字段,我们也应该提供id和版本值,而不是JPA首先将相关实体作为新实体持久化

示例:我们有两个实体-->Vehicle(id、Car、version);汽车(id、版本、品牌);要更新/保存车辆实体,请确保车辆实体中的“车辆”字段已提供id和版本字段

介绍

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

在我的例子中,当我试图使用对具有空id的实体的引用来检索相关实体时,发生了这种情况。

@Entity
public class User {
@Id
private Long id;
}

@Entity
public class Address {
@Id
private Long id;
@JoinColumn(name="user_id")
@OneToOne
private User user;
}

interface AddressRepository extends JpaRepository<Address, Long> {
Address findByUser(User user);
}

User user = new User(); // this is transient, does not have id populated
// user.setId(1L) // works fine if this is uncommented

addressRepository.findByUser(user); // throws exception

在我的例子中,这是由于双向关系的@ManyToOne一侧没有CascadeType导致的。更准确地说,我在@OneToMany端有CascadeType.ALL,而在@ManyToOne端没有。将CascadeType.ALL添加到@ManyToOne解决了该问题。一对多:

@OneToMany(cascade = CascadeType.ALL, mappedBy="globalConfig", orphanRemoval = true)
private Set<GlobalConfigScope>gcScopeSet;

多对一(导致问题)

@ManyToOne
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;

多对一(通过添加CascadeType.PERSIST修复)

@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;

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