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

884

我使用Hibernate保存对象时遇到以下错误:

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

2
这个错误出现在什么上下文中?是与瞬态变量有关还是没有? - vijay
1
如果您的对象之间存在关联关系,确保它们按正确顺序保存。例如:作者在书之前保存,因为作者是书的必要条件。同样地。 - Gayal Rupasinghe
33个回答

1

这个错误有很多可能性,其他可能性也可能出现在添加页面或编辑页面。在我这个案例中,我试图保存一个AdvanceSalary对象。问题是,在编辑AdvanceSalary时,employee.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;
        }

    }

1
一种可能的原因是:在我的情况下,我试图在保存父实体之前保存子实体,而且这是一个全新的实体。
在 User.java 模型中,代码大致如下:
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.setNewPassword(password);
this.timeJoin = new Date();
create();

setNewPassword() 方法创建一个 PasswordHistory 记录并将其添加到 User 中的历史记录集合中。由于父实体的 create() 语句尚未执行,因此它试图保存到尚未创建的实体集合中。我所要做的就是将 setNewPassword() 调用移动到 create() 调用之后以解决问题。
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.timeJoin = new Date();
create();
this.setNewPassword(password);

1

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


1
在我的情况下,问题完全不同。我有两个类,假设是c1和c2。C1和C2之间的依赖关系是OneToMany。现在,如果我将C1保存在数据库中,它会抛出上述错误。
解决此问题的方法是从消费者请求中获取第一个C2的ID,并通过存储库调用查找C2。然后将c2保存到C1对象中。现在,如果我保存C1,它就可以正常工作了。

1
我在所有PUT HTTP事务中遇到了相同的错误,引入乐观锁定(@Version)后。
更新实体时,必须发送该实体的id和version。如果任何实体字段与其他实体相关,则对于该字段,我们还应提供id和version值,否则JPA会首先将其作为新实体持久化。
例如:我们有两个实体--->车辆(id、车、版本);汽车(id、版本、品牌);要更新/持久化车辆实体,请确保车辆实体中的汽车字段已提供id和version字段。

1

案例1: 当我尝试创建一个父对象并将其引用保存到其子对象中,然后执行其他DELETE / UPDATE查询(JPQL)时,我遇到了此异常。因此,我在创建父对象和使用相同的父引用创建子对象后,只需flush()新创建的实体即可。这对我起作用了。

案例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;
    .
    .

}

在上述情况下,当父(Reference)和子(ReferenceAdditionalDetails)之间存在OneToOne关系时,如果您尝试创建Reference实体,然后再创建其子项(ReferenceAdditionalDetails),它将给出相同的异常。因此,为了避免异常,您必须将子类设置为null,然后创建父类。(示例代码)
.
.
reference.setRefAddDetails(null);
reference = referenceDao.create(reference);
entityManager.flush();
.
.

我遇到了类似于第二种情况的问题,即在父对象P1中有一个C1对象引用,而C1对象是可选的。当用户不添加C1对象时,在保存P1对象时,我必须显式地将C1对象设置为null。 - Sumit Rane

0

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

下面是一个简单的一对一关系示例

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

1
这是相反的 - 子实体持有外键值并依赖于父实体,因此您需要先保存父实体!在代码块中,您做得很正确。 - Sõber

0
在我的情况下,这是发生的当我试图使用一个空 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

0
只需在基类中创建映射的构造函数。 例如,如果您想在实体A和实体B之间建立一对一关系, 如果您将A作为基类,则A必须有一个以B为参数的构造函数。

0

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

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 department
employee.setDepartment(dept);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接