如何修复Hibernate“对象引用未保存的瞬态实例-在刷新之前保存瞬态实例”错误

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

object references an unsaved transient instance - save the transient instance before flushing
934363 次浏览

您应该在集合映射中包含cascade="all"(如果使用xml)或cascade=CascadeType.ALL(如果使用注释)。

发生这种情况是因为您的实体中有一个集合,而该集合有一个或多个数据库中不存在的项目。通过指定上述选项,您可以告诉hibernate在保存其父级时将它们保存到数据库中。

这不是错误的唯一原因。我刚才遇到它是因为我的编码中的一个错别字错误,我相信这设置了一个已经保存的实体的值。

X x2 = new X();x.setXid(memberid); // Error happened here - x was a previous global entity I created earlierY.setX(x2);

我通过准确查找导致错误的变量(在本例中为String xid)来发现错误。我在保存实体并打印跟踪的整个代码块周围使用了catch

{code block that performed the operation} catch (Exception e) {e.printStackTrace(); // put a break-point here and inspect the 'e'return ERROR;}

我相信这可能只是重复回答,但只是为了澄清,我在@OneToOne映射和@OneToMany映射上都得到了这个。在这两种情况下,我添加到ParentChild对象还没有保存在数据库中。所以当我将Child添加到Parent时,然后保存Parent,Hibernate会在保存父级时抛出"object references an unsaved transient instance - save the transient instance before flushing"消息。

Parent's引用的Child中添加cascade = {CascadeType.ALL}解决了这两种情况下的问题。这保存了ChildParent

很抱歉有重复的回答,只是想进一步澄清一下。

@OneToOne(cascade = {CascadeType.ALL})@JoinColumn(name = "performancelog_id")public PerformanceLog getPerformanceLog() {return performanceLog;}

或者,如果您想使用最小的“权力”(例如,如果您不想要级联删除)来实现您想要的,请使用

import org.hibernate.annotations.Cascade;import org.hibernate.annotations.CascadeType;
...
@Cascade({CascadeType.SAVE_UPDATE})private Set<Child> children;

当Hibernate认为它需要保存与您正在保存的对象相关联的对象时,会发生这种情况。

我遇到了这个问题,不想保存对引用对象的更改,所以我希望级联类型为NONE。

诀窍是确保被引用对象中的ID和VERSION被设置,这样Hibernate就不会认为被引用的对象是需要保存的新对象。这对我很有效。

查看要保存的类中的所有关系,以计算关联对象(以及关联对象的关联对象),并确保在对象树的所有对象中设置ID和VERSION。

我在持久化一个实体时发生了这种情况,其中数据库中的现有记录对带有@Version注释的字段具有NULL值(用于乐观锁定)。将数据库中的NULL值更新为0纠正了这一点。

如果您的集合为空,请尝试:object.SetYouColection(null);

在我的情况下,它是由双向关系的@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;

我得到这个错误当我使用

getSession().save(object)

但我用的时候没问题

getSession().saveOrUpdate(object)

另一个可能的原因:在我的情况下,我试图在拯救父母之前拯救孩子,在一个全新的实体上。

代码在User.java模型中是这样的:

this.lastName = lastName;this.isAdmin = isAdmin;this.accountStatus = "Active";this.setNewPassword(password);this.timeJoin = new Date();create();

setNewPassword()方法创建一个PasswordHistory记录并将其添加到User中的历史集合中。由于创建()语句尚未为父级执行,因此它试图保存到尚未创建的实体的集合中。我所要做的就是将setNewPassword()调用移动到创建()之后。

this.lastName = lastName;this.isAdmin = isAdmin;this.accountStatus = "Active";this.timeJoin = new Date();create();this.setNewPassword(password);

为了增加我的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设置为新员工实例,而不会首先实际获取部门实体,因为我不想触发另一个选择查询。

在某些情况下,deptId PKID作为null来自调用方法,我收到了相同的错误。

因此,请注意PK ID的null

解决这个问题的简单方法是保存两个实体。首先保存子实体,然后保存父实体。因为父实体依赖于子实体的外键值。

下面是一对一关系的简单考试

insert into Department (name, numOfemp, Depno) values (?, ?, ?)Hibernate: insert into Employee (SSN, dep_Depno, firstName, lastName, middleName, empno) values (?, ?, ?, ?, ?, ?)
Session session=sf.openSession();session.beginTransaction();session.save(dep);session.save(emp);

除了所有其他好的答案之外,如果您使用merge来持久化一个对象并且意外地忘记在父类中使用对象的合并引用,则可能会发生这种情况

merge(A);B.setA(A);persist(B);

在这种情况下,您合并了A但忘记使用A的合并对象。要解决这个问题,您必须像这样重写代码。

A=merge(A);//difference is hereB.setA(A);persist(B);

在hibernate中还有另一种可能导致此错误。您可以将对象A的未保存引用设置为附加实体B并希望持久化对象C。即使在这种情况下,您也会得到上述错误。

除非真的有必要,否则不要使用Cascade.AllRolePermission有双向的manyToMany关系。然后下面的代码可以正常工作

    Permission p = new Permission();p.setName("help");Permission p2 = new Permission();p2.setName("self_info");p = (Permission)crudRepository.save(p); // returned p has id filled in.p2 = (Permission)crudRepository.save(p2); // so does p2.Role role = new Role();role.setAvailable(true);role.setDescription("a test role");role.setRole("admin");List<Permission> pList = new ArrayList<Permission>();pList.add(p);pList.add(p2);role.setPermissions(pList);crudRepository.save(role);

如果对象只是一个“新”对象,那么它会抛出相同的错误。

错误的一个可能原因是父实体值的设置不存在;例如,对于部门-员工关系,您必须编写以下内容才能修复错误:

Department dept = (Department)session.load(Department.class, dept_code); // dept_code is from the jsp form which you get in the controller with @RequestParam String departmentemployee.setDepartment(dept);

这个错误的可能性有很多,其他一些可能性也在添加页面或编辑页面上。在我的情况下,我试图保存一个对象Advanced Salary。问题是,在编辑Advanced Salaryemployee.employee_id为空因为在编辑时我没有设置employee.employee_id。我做了一个隐藏字段并设置了它。我的代码工作绝对正常。

    @Entity(name = "ic_advance_salary")@Table(name = "ic_advance_salary")public class AdvanceSalary extends BaseDO{
@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "id")private Integer id;
@ManyToOne(fetch = FetchType.EAGER)@JoinColumn(name = "employee_id", nullable = false)private Employee employee;
@Column(name = "employee_id", insertable=false, updatable=false)@NotNull(message="Please enter employee Id")private Long employee_id;
@Column(name = "advance_date")@DateTimeFormat(pattern = "dd-MMM-yyyy")@NotNull(message="Please enter advance date")private Date advance_date;
@Column(name = "amount")@NotNull(message="Please enter Paid Amount")private Double amount;
@Column(name = "cheque_date")@DateTimeFormat(pattern = "dd-MMM-yyyy")private Date cheque_date;
@Column(name = "cheque_no")private String cheque_no;
@Column(name = "remarks")private String remarks;
public AdvanceSalary() {}
public AdvanceSalary(Integer advance_salary_id) {this.id = advance_salary_id;}
public Integer getId() {return id;}
public void setId(Integer id) {this.id = id;}
public Employee getEmployee() {return employee;}
public void setEmployee(Employee employee) {this.employee = employee;}

public Long getEmployee_id() {return employee_id;}
public void setEmployee_id(Long employee_id) {this.employee_id = employee_id;}
}

如果您使用的是Spring Data JPA,那么在您的服务实现中添加@Transactional注释将解决问题。

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

为了完整起见:A

org.hibernate.TransientPropertyValueException

与消息

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

当您尝试持久化/合并一个实体并引用恰好是分离的另一个实体时,也会发生。

我认为这是因为你试图持久化一个对象,这个对象引用了另一个还没有持久化的对象,所以它尝试在“数据库端”引用不存在的行

案例1:当我尝试创建父级并将该父级引用保存到其子级时,我遇到了这个异常,然后保存了其他一些DELETE/UPDATE查询(JPQL)。所以我只是在创建父级和使用相同的父级引用创建子级后刷新()新创建的实体。它对我有用。

案例2:

父类

public class Reference implements Serializable {
@Id@Column(precision=20, scale=0)private BigInteger id;
@Temporal(TemporalType.TIMESTAMP)private Date modifiedOn;
@OneToOne(mappedBy="reference")private ReferenceAdditionalDetails refAddDetails;...}

儿童班:

public class ReferenceAdditionalDetails implements Serializable{
private static final long serialVersionUID = 1L;
@Id@OneToOne@JoinColumn(name="reference",referencedColumnName="id")private Reference reference;
private String preferedSector1;private String preferedSector2;..
}

在上面的情况下,父(引用)和子(引用附加详细信息)具有OneToOne关系,当你尝试创建引用实体然后创建其子(引用附加详细信息)时,它会给你同样的异常。因此为了避免异常,你必须为子类设置null,然后创建父类。(示例代码)

..reference.setRefAddDetails(null);reference = referenceDao.create(reference);entityManager.flush();..

我也遇到了同样的情况。通过在属性上方设置以下注释,使其解决了提示的异常。

我遇到的例外。

Exception in thread "main" java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.model.Car_OneToMany

为了克服,我使用的注释。

    @OneToMany(cascade = {CascadeType.ALL})@Column(name = "ListOfCarsDrivenByDriver")private List<Car_OneToMany> listOfCarsBeingDriven = new ArrayList<Car_OneToMany>();

是什么让Hibernate抛出异常:

此异常在您的控制台中引发,因为我附加到父对象的子对象此时不存在于数据库中。

通过提供@OneToMany(cascade = {CascadeType.ALL}),它告诉Hibernate在保存父对象的同时将它们保存到数据库。

当我在标记为@Transactional的方法中创建一个新实体和一个关联实体,然后在保存之前执行查询时,这个问题就发生在我身上。例如

@Transactionalpublic someService() {Entity someEntity = new Entity();AssocaiatedEntity associatedEntity = new AssocaitedEntity();someEntity.setAssociatedEntity(associatedEntity);associatedEntity.setEntity(someEntity);
// Performing any query was causing hibernate to attempt to persist the new entity. It would then throw an exceptionsomeDao.getSomething();
entityDao.create(someEntity);}

为了解决这个问题,我在创建新实体之前执行了查询。

导言

使用JPA和Hibernate时,实体可以处于以下4种状态之一:

  • -新创建的对象从未与Hibernate会话(也称为持久化上下文)关联并且未映射到任何数据库表行,则被认为处于New状态或暂时状态。

要持久化,我们需要显式调用persist方法或使用传递持久化机制。

  • 持久-持久化实体已与数据库表行相关联,并由当前运行的持久化上下文管理。

对此类实体所做的任何更改都将被检测并传播到数据库(在会话刷新期间)。

  • 分离-关闭当前运行的持久性上下文后,所有以前管理的实体都将被分离。不再跟踪连续的更改,也不会发生自动数据库同步。

  • 删除-尽管JPA要求只允许删除托管实体,但Hibernate也可以删除分离的实体(但只能通过remove方法调用)。

实体状态转换

要将实体从一个状态移动到另一个状态,您可以使用persistremovemerge方法。

JPA实体状态

解决问题

您在问题中描述的问题:

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

是由于将状态为的实体关联到状态为管理的实体而引起的。

当您将子实体关联到父实体中的一对多集合时,可能会发生这种情况,并且集合不会cascade实体状态转换。

因此,您可以通过将级联添加到触发此故障的实体关联来解决此问题,如下所示:

@OneToOne协会

@OneToOne(mappedBy = "post",orphanRemoval = true,cascade = CascadeType.ALL)private PostDetails details;

注意我们为cascade属性添加的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.ALLorphanRemoval,因为这会将删除实体状态转换从一个父实体传播到另一个父实体。

因此,对于@ManyToMany关联,您通常会级联CascadeType.PERSISTCascadeType.MERGE操作。或者,您可以将其扩展为DETACHREFRESH

我的问题与JUnit的@BeforeEach有关。即使我保存了相关实体(在我的案例@ManyToOne中),我也会遇到同样的错误。

这个问题在某种程度上与我父母的序列有关。如果我为该属性赋值,问题就解决了。

前。如果我有实体问题,可以有一些类别(一个或多个)和实体问题有一个序列:

@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "feedbackSeq")@Idprivate Long id;

我必须分配值question.setId(1L);

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

在我的情况下,问题完全不同。我有两个类,假设c1和c2。在C1和C2之间的依赖关系是OneTo许多。现在,如果我在DB中保存C1,它会抛出上面的错误。

这个问题的解决方案是从消费者请求中获取第一个C2的id,并通过存储库调用找到C2。然后将c2保存到C1对象中。现在如果我正在保存C1,它工作正常。

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

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

示例:我们有两个实体-->车辆(id, Car, version);汽车(id, version, land);来更新/保存车辆实体,确保车辆实体中的Car字段提供了id和version字段

在我的例子中,使用Cascade.ALL是在表中添加不必要的条目,它已经有了相同的对象(生成一个没有id的子值就可以做到这一点)。所以我必须先从存储库中获取子对象,并将其设置为主对象,而不是导致问题的对象。

Student s = studentRepo.findByName(generatedObject.getStudent().getName())generatedObject.student(s);

它也可能发生在当你有一个到多个关系,你试图将子实体添加到父实体的列表中,然后通过父实体检索这个列表(在保存这个父实体之前),不保存子实体本身,例如:

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);

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

@Entitypublic class User {@Idprivate Long id;}
@Entitypublic class Address {@Idprivate Long id;@JoinColumn(name="user_id")@OneToOneprivate 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