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

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

当前回答

当我使用

getSession().save(object)

但当我使用

getSession().saveOrUpdate(object) 

其他回答

介绍

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

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

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

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

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

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

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

我通过准确查找导致错误的变量(在本例中为Stringxid)发现了错误。我在保存实体并打印痕迹的整个代码块周围使用了捕获。

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

当我在标记为@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);
}

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