繁体   English   中英

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

[英]How to fix the Hibernate "object references an unsaved transient instance - save the transient instance before flushing" error

当我使用 Hibernate 保存 object 时收到以下错误

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

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

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

我相信这可能只是重复的答案,但只是为了澄清,我在@OneToOne映射和@OneToMany上得到了这个。 在这两种情况下,事实是我添加到ParentChild对象尚未保存在数据库中。 因此,当我将Child添加到Parent ,然后保存Parent ,Hibernate 会在保存Parent抛出"object references an unsaved transient instance - save the transient instance before flushing"消息。

Parent'sChild Parent's引用上添加cascade = {CascadeType.ALL}解决了这两种情况下的问题。 这拯救了ChildParent

对不起,任何重复的答案,只是想为人们进一步澄清。

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

介绍

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

  • New - 从未与 Hibernate Session(又名 Persistence Context)关联且未映射到任何数据库表行的新创建的对象被视为处于 New 或 Transient 状态。

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

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

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

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

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

实体状态转换

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

JPA 实体状态

解决问题

您在问题中描述的问题:

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

是由于将处于New状态的实体与处于Managed状态的实体相关联引起的。

当您将子实体与父实体中的一对多集合相关联时,可能会发生这种情况,并且该集合不会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

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

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

诀窍是确保引用对象中的 ID 和 VERSION 已设置,以便 Hibernate 不会认为引用的对象是需要保存的新对象。 这对我有用。

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

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

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;

...

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

就我而言,这是由于双向关系的@ManyToOne一侧没有CascadeType引起的。 更准确地说,我在@OneToMany端有CascadeType.ALL@OneToMany@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;

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

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

X x2 = new X();
x.setXid(memberid); // Error happened here - x was a previous global entity I created earlier
Y.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;
}

不要使用Cascade.All除非你真的需要。 RolePermission具有双向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);

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

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

除了所有其他好的答案之外,如果您使用merge来持久化对象并且不小心忘记在父类中使用对象的合并引用,则可能会发生这种情况。 考虑下面的例子

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

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

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

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

@Transactional
public 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 exception
    someDao.getSomething();

    entityDao.create(someEntity);
}

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

为了增加我的 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

我在使用时收到此错误

getSession().save(object)

但是当我使用时它没有问题

getSession().saveOrUpdate(object) 

为完整起见:A

org.hibernate.TransientPropertyValueException 

有消息

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

当您尝试保留/合并一个实体并引用另一个碰巧分离的实体时,也会发生这种情况。

我也面临同样的情况。 通过在属性上方设置以下注释,可以解决提示的异常。

我遇到的异常。

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 在保存父对象的同时将它们保存到数据库中。

当您拥有 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);

当我保存父实体时抛出错误。 如果我删除了前一行中的检索,则不会抛出错误,但这当然不是解决方案。

解决方案是保存childEntity并加入救孩子实体父实体,就像这样:

Child childEntity = new Child();
Child savedChildEntity = childRepository.save(childEntity);
parentEntity.addChild(savedChildEntity);
parentEntity.getChildren();
parentRepository.save(parentEntity);

另一个可能的原因:就我而言,我试图在一个全新的实体上拯救孩子,然后再拯救父母。

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并希望保留对象C 即使在这种情况下,您也会收到上述错误。

这个错误有很多可能性,在添加页面或编辑页面上还有其他一些可能性。 就我而言,我试图保存一个对象 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;
        }

    }

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

我认为是因为您尝试持久化一个对象,该对象引用另一个尚未持久化的对象,因此它尝试在“数据库端”中放置对不存在的行的引用

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

}

在上述情况下,父(Reference) 和子(ReferenceAdditionalDetails) 具有OneToOne 关系,当您尝试创建Reference 实体然后创建其子实体(ReferenceAdditionalDetails) 时,它会给您相同的异常。 因此,为了避免异常,您必须为子类设置 null,然后创建父类。(示例代码)

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

就我而言,问题完全不同。 我有两个班级,比如说 c1 和 c2。 C1 和 C2 之间的依赖是 OneToMany。 现在,如果我将 C1 保存在 DB 中,它会抛出上述错误。

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

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

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

示例:我们有两个实体 --> Vehicle (id,Car,version) ; 汽车(ID、版本、品牌); 更新/保留 Vehicle 实体,确保 Vehicle 实体中的 Car 字段提供了 id 和 version 字段

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

下面简单考查一对一的关系

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

错误的一种可能原因是父实体的值设置不存在; 例如,对于部门与员工的关系,您必须这样写才能修复错误:

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

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

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

这个问题在某种程度上与我父母的顺序有关。 如果我将值分配给该属性,问题就解决了。

前任。 如果我有可以有一些类别(一个或多个)的实体 Question 并且实体 Question 有一个序列:

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

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

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

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

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

在我的情况下,当我尝试使用对具有 null 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

我刚刚收到此错误,因为我在保存之前设置了一个未代理的实体而不是另一个实体。

我应该链接一个代理实体实例。

请看下面的解释:

Child saved = childRepository.save(child);

// INCORRECT
parent.setChild(child);  // <-- 'child' is NOT managed (not proxied)

// CORRECT
parent.setChild(saved);  // <-- 'saved' is managed (proxied)

我是弹簧靴的新手。 我有两个实体,当我执行时,收到此错误:“对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例”

我的springboot版本:2.5.7

下面给出两个实体类:

@Entity
public class Topic {

    @Id
    private String id;
    private String name;
    private String description;

    public Topic() {

    }

    public Topic(String id, String name, String description) {
        super();
        this.id = id;
        this.name = name;
        this.description = description;
    }

@Entity
public class Course {
    
    @Id
    private String id;
    private String name;
    private String description;

    @ManyToOne
    private Topic topic;

    public Course() {

    }

    public Course(String id, String name, String description, String topicId) {
        super();
        this.id = id;
        this.name = name;
        this.description = description;
        this.topic = new Topic(topicId,"","");
    }

不知道我该如何解决这个问题。 有人可以帮忙吗?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM