object引用未保存的瞬态实例 - 在刷新之前保存瞬态实例

Tus*_*rao 546 java orm hibernate

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

object references an unsaved transient instance - save the transient instance before flushing
Run Code Online (Sandbox Code Playgroud)

Boz*_*zho 727

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

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

  • @Marcus - 不,不是.您可能希望手动处理它们. (28认同)
  • 它不仅适用于集合,也适用于简单的一对一映射 (20认同)
  • 从CascadeType.PERSIST开始并使用persist来保存不是更好吗? (11认同)
  • 这不是隐含的吗?难道你不总是希望Hibernate保存它们吗? (7认同)
  • 博兹是对的.我遇到过这样的情况:由于规模大小,或者由于业务规则不允许集合中的所有对象同时保存,因此我想要手动管理集合. (5认同)
  • @shevchik使用`CascadeType.PERSIST`对我不起作用.我希望有人解释原因. (3认同)
  • 对于任何在使用外键时遇到此错误的人:级联对我来说不是一个解决方案,因为我不想在我的父实体中存储集合。我想做的就是将另一个(外键)子实体的 ID 保存在我的父实体中。我的问题似乎是我在子实体中使用了“长”id,而不是“长”(小写与大写)。抱歉用词不当。 (3认同)
  • 使用Cascade ='all'将重新插入父级。如何避免这种情况?没有级联,将引发上述异常 (2认同)
  • 但是,不要不动脑子就添加CascadeType.ALL。您可能具有相同类型的继承者/前任!这将导致某种无限循环。 (2认同)

小智 225

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

在两个案例中添加了cascade = {CascadeType.ALL}Parent's引用的Child解决问题.这节省了ChildParent.

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

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

  • 如果我不想在@OneToOne关系上级联保存怎么办?在第一次创建两个对象时,如何在不触发异常的情况下将其保存到数据库? (5认同)
  • 如果我想在某些情况下而不是其他情况下保存孩子该怎么办? (3认同)

Vla*_*cea 88

介绍

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

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

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

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

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

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

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

实体状态转换

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

JPA 实体状态

解决问题

您在问题中描述的问题:

object references an unsaved transient instance - save the transient instance before flushing
Run Code Online (Sandbox Code Playgroud)

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

当您将子实体与父实体中的一对多集合相关联,并且该集合没有cascade实体状态转换时,就会发生这种情况。

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

@OneToOne协会

@OneToOne(
    mappedBy = "post",
    orphanRemoval = true,
    cascade = CascadeType.ALL)
private PostDetails details;
Run Code Online (Sandbox Code Playgroud)

注意CascadeType.ALL我们为cascade属性添加的值。

@OneToMany协会

@OneToMany(
    mappedBy = "post", 
    orphanRemoval = true,
    cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>();
Run Code Online (Sandbox Code Playgroud)

同样,CascadeType.ALL适用于双向@OneToMany关联。

现在,为了让级联在双向中正常工作,您还需要确保父子关联是同步的。

@ManyToMany协会

@ManyToMany(
    mappedBy = "authors",
    cascade = {
        CascadeType.PERSIST, 
        CascadeType.MERGE
    }
)
private List<Book> books = new ArrayList<>();
Run Code Online (Sandbox Code Playgroud)

@ManyToMany关联中,您不能使用CascadeType.ALLororphanRemoval因为这会将删除实体状态转换从一个父实体传播到另一个父实体。

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

  • 对于合并,它运行 SELECT 查询。否则,它会检查 ID 和版本。 (2认同)

Coo*_*ino 59

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

我有这个问题,并且不想保存对引用对象的更改,所以我希望级联类型为NONE.

诀窍是确保引用对象中的ID和VERSION设置为Hibernate不认为引用的对象是需要保存的新对象.这对我有用.

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

  • 这条评论让我走上正轨.我正在将父项的新实例分配给其子项的属性.所以NH认为他们是不同的例子. (4认同)
  • 是。例如,如果不包括关联对象的ID(例如,@ JsonIgnore忽略了该ID),则会发生这种情况。Hibernate无法识别关联的实体,因此它想保存它。 (2认同)

Har*_*gić 28

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

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

...

@Cascade({CascadeType.SAVE_UPDATE})
private Set<Child> children;
Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案.CascadeType.ALL过于宽泛 (10认同)
  • 从Hibernate 5.2.8开始,似乎无法用JPA注释实现相同的效果.例如,`@ManyToMany(cascade = {PERSIST,MERGE,REFRESH,DETACH})`(除了REMOVE之外)不会像Hibernate的`CascadeType.SAVE_UPDATE`那样级联更新. (4认同)

all*_*lap 22

在我的情况下,它是由尚未造成CascadeType@ManyToOne双向关系的一面.更确切地说,我CascadeType.ALL站在@OneToMany一边并且没有使用它@ManyToOne.添加CascadeType.ALL@ManyToOne解决了这个问题. 一对多方面:

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

多对一方(导致问题)

@ManyToOne
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;
Run Code Online (Sandbox Code Playgroud)

多对一(通过添加固定CascadeType.PERSIST)

@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;
Run Code Online (Sandbox Code Playgroud)

  • 如果我这样做,并保存父实体,会发生的情况是我的父表中有两个插入,即两行。我认为这是因为我们在父实体和子实体上都有级联? (3认同)

duk*_*ash 16

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


nan*_*eck 10

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

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

我通过找到导致错误的确切变量(在这种情况下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;
}
Run Code Online (Sandbox Code Playgroud)


小智 8

当您具有 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);
Run Code Online (Sandbox Code Playgroud)

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

解决方案是保存 childEntity 并将保存的子实体添加到父实体,如下所示:

Child childEntity = new Child();
Child savedChildEntity = childRepository.save(childEntity);
parentEntity.addChild(savedChildEntity);
parentEntity.getChildren();
parentRepository.save(parentEntity);
Run Code Online (Sandbox Code Playgroud)


Joa*_*ena 6

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


Tii*_*ina 6

Cascade.All你真的必须这样做之前不要使用.Role并且Permission具有双向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);
Run Code Online (Sandbox Code Playgroud)

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

  • 100% 正确,Cascade.All 是惰性解决方案,仅应在需要时应用。首先,如果实体已经存在,则检查当前实体管理器中是否加载了该实体,如果没有则加载。 (2认同)

mad*_*fox 6

当我在标记为 的方法中创建新实体和关联实体@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);
}
Run Code Online (Sandbox Code Playgroud)

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


man*_*nta 5

要加上我的2美分,当我不小心发送nullID 时也遇到了同样的问题。下面的代码描述了我的情况(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);
Run Code Online (Sandbox Code Playgroud)

在这里,我将现有部门ID设置为新的员工实例,而实际上并没有首先获取部门实体,因为我不想触发另一个选择查询。

在某些情况下,deptIdPKID null来自调用方法,并且出现相同的错误。

因此,请注意nullPK ID的值


Hos*_*asr 5

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

merge(A);
B.setA(A);
persist(B);
Run Code Online (Sandbox Code Playgroud)

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

A=merge(A);//difference is here
B.setA(A);
persist(B);
Run Code Online (Sandbox Code Playgroud)