Criteria.list()期间的org.hibernate.TransientObjectException

时间:2010-04-08 20:43:38

标签: java hibernate

我在互联网上看过有关如何在保存/更新/删除期间修复TransientObjectExceptions的帖子,但在我的Criteria上调用list时出现此问题。

我有两个对象A和B.A有一个名为b的字段,它是B类型。在我的映射中,b映射为多对一。这一切都运行在一个更大的持久性框架中(框架有点像Core Data),因此我不会在我的hibernate映射中使用任何级联,因为级联是在更高级别处理的。

这是围绕我的标准的有趣代码:

A a = new A();
B b = new B();
a.setB(b);

session.save("B", b); // Actually handled by the higher level
session.save("A", a); // framework, this is just for clarity

// transaction committed and session closed
...
// new session opened

Criteria criteria = session.createCriteria(A.class);
criteria.add(Restrictions.eq("b", b));
List<?> objects = criteria.list();

基本上我正在寻找A类的所有对象,这样Ab等于b的特定实例(我实际上尝试重构一个查询,以便我传入b的id只是为了确保b不会导致我问题)。

这是我调用criteria.list()时发生的堆栈跟踪:

org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: B
at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:244)
at org.hibernate.type.EntityType.getIdentifier(EntityType.java:449)
at org.hibernate.type.ManyToOneType.nullSafeSet(ManyToOneType.java:141)
at org.hibernate.loader.Loader.bindPositionalParameters(Loader.java:1769)
at org.hibernate.loader.Loader.bindParameterValues(Loader.java:1740)
at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1612)
at org.hibernate.loader.Loader.doQuery(Loader.java:717)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:270)
at org.hibernate.loader.Loader.doList(Loader.java:2294)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2172)
at org.hibernate.loader.Loader.list(Loader.java:2167)
at org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:119)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1706)
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:347)

这是我的映射:

  

<class entity-name="A" lazy="false">
      <tuplizer entity-mode="dynamic-map" class="MyTuplizer" />
      <id type="long" column="id">
          <generator class="native" />
      </id>
      <many-to-one name="b" entity-name="B" column="b_id" lazy="false" />
      </class>
  
      <class entity-name="B" lazy="false">
      <tuplizer entity-mode="dynamic-map" class="MyTuplizer" />
      <id type="long" column="id">
      <generator class="native" />
      </id>
      </class>

任何人都可以帮我弄清楚为什么我会在获取期间获得TransientObjectException?我希望找到一种不依赖于级联的解决方案,因为它们往往会掩盖更高级别框架中出现的问题。

3 个答案:

答案 0 :(得分:1)

问题是b在另一个会话中是持久的,该会话已关闭,并且在新会话中创建查询。关闭会话时,其持久性上下文中的所有对象都将变为分离。如果您想稍后在另一个会话中重用它们,则需要先将它们重新附加到该会话:

session.update(b);

引用Hibernate的书:

  

update()操作   在Session上将分离的对象重新附加到持久化上下文和   安排SQL UDPATE。 Hibernate必须假设客户端修改了   对象虽然被分离了。 (否则,如果你确定它没有被修改,   一个lock()就足够了。)持久化上下文会自动刷新   当会话中的第二个事务提交时,任何   对曾经分离的和现在持久的对象的修改是同步的   与数据库。

     

saveOrUpdate()方法实际上比update()更有用,   save()lock():在复杂的对话中,您不知道该项目是否在   分离状态或它是否是新的和瞬态的并且必须保存。自动   当你saveOrUpdate()提供的状态检测变得更有用   不仅可以处理单个实例,还可以重新连接或保留网络   连接对象并应用级联选项。

请注意,还有一个merge()方法,用于在重新附加较旧的分离实例之前将相同实体加载到新的持久性上下文中的情况。在这种情况下,您有两个表示同一实体的物理上不同的实例,因此应合并它们以避免NonUniqueObjectException

答案 1 :(得分:0)

另一种简单的方法是在父类映射中对子类的集合映射使用属性cascade = all。这是映射的外观

<class entity-name="A" lazy="false">
<tuplizer entity-mode="dynamic-map" class="MyTuplizer" />
<id type="long" column="id">
<generator class="native" />
</id>
<many-to-one name="b" entity-name="B" column="b_id" lazy="false" cascade="all" />
</class>

<class entity-name="B" lazy="false">
<tuplizer entity-mode="dynamic-map" class="MyTuplizer" />
<id type="long" column="id">
<generator class="native" />
</id>
</class>

答案 2 :(得分:0)

由于你有插入数据,例如在persist()中,在hibernate中保存()。但是上面的错误是你只需要执行merge()和其他可以执行更新信息的方法。