2013-04-26 72 views
52

我基本上具有在该结构中的一些对象相关联的(真正的数据模型是一个比较复杂的):休眠错误:用相同的标识符值不同的物体已经与所述会话

  • A具有许多与B.一对多关系(B具有inverse="true"
  • B具有与C的多对一的关系(I有cascade集到"save-update"
  • C是一种类型/类别表的。

另外,我应该提到主键是保存时由数据库生成的。

有了我的数据,我有时会遇到问题,其中A有一组不同的B对象,而这些B对象引用相同的C对象。

当我打电话给session.saveOrUpdate(myAObject)时,出现一个休眠错误:"a different object with the same identifier value was already associated with the session: C"。我知道hibernate不能在同一个会话中插入/更新/删除同一个对象两次,但是有没有办法解决这个问题?这似乎并不是罕见的情况。

在我研究这个问题的过程中,我看到有人建议使用session.merge(),但是当我这样做时,任何“冲突”的对象都会作为空白对象插入到数据库中,所有值都设置为空。显然,这不是我们想要的。

[编辑]我忘记提及的另一件事是(由于体系结构的原因超出我的控制),每个读或写需要在一个单独的会话中完成。

+0

看看这个[ **答案**](http://stackoverflow.com/questions/1074081/hibernate-error-org-hibernate-nonuniqueobjectexception-a-different-object-with)可以帮助你.. – joaonlima 2013-04-26 23:43:01

回答

60

很可能是因为B对象不是指同一个Java C对象实例。它们指的是数据库中的同一行(即相同的主键),但它们是不同的副本。

因此,发生的事情是管理实体的Hibernate会话将跟踪哪个Java对象与具有相同主键的行对应。

一个选项是确保引用同一行的对象B的实体实际上是指C的同一对象实例。或者关闭该成员变量的级联。当B坚持时,这种方式C不是。不过你必须手动保存C.如果C是一个类型/类别表,那么这样做可能是有意义的。

+2

谢谢jbx。正如你所说,事实证明,B对象指的是内存中的多个C实例。基本上发生的是我的程序的一部分是用C读取的,并将它附加到B.另一部分是用数据库中的相同C加载不同的B。两者都被连接到A,这会在保存时触发错误。 我已经将B-> C关系的

cascade
设置为“
none
”,但我仍然收到相同的错误。在多对一或者一对多的关系中,有没有一种方法可以告诉Hibernate只能改变外键而不用担心其余的问题? – John 2013-04-29 15:58:45

+1

C的主键是否有任何ID生成策略?像序列发生器或类似的东西? – jbx 2013-04-30 18:26:24

+0

是的,每个在数据库中都有自己的序列。 正如你所说,级联原来是问题。我们关闭了类型表的级联,而其他我们使用了“合并”级联,这使得我们可以在不创建所有这些空行的情况下调用merge()。我相应地标记了你的答案,谢谢! – John 2013-05-10 16:48:43

0

在调用update query之前,您可能不会设置对象的标识符。

+1

如果他不是,他不会有这个问题。问题是他有两个具有相同标识符的对象。 – aalku 2013-11-11 10:19:57

2

刚刚遇到此消息,但在C#代码。不知道它是否相关(尽管相同的错误信息)。

我正在调试带有断点的代码,并在调试器处于断点时通过私有成员扩展了一些集合。重新运行代码而不通过结构挖掘使错误消息消失。看起来像查看私有惰性加载集合的行为已经使NHibernate加载了当时不应该加载的东西(因为它们在私人成员中)。

代码本身包装在一个相当复杂的事务中,该事务可以更新大量记录和许多依赖项作为该事务的一部分(导入过程)。

希望能够找到遇到问题的任何人的线索。

5

传输通过分配从休眠到数据库中的对象ID的任务:

<generator class="native"/> 

这解决了这个问题对我来说。

5

你只需要做一件事。运行session_object.clear(),然后保存新的对象。这将清除会话(如适当命名)并从会话中删除违规的重复对象。

3

解决上述问题的一种方法是覆盖hashcode()
同时在保存之前和之后刷新休眠会话。

getHibernateTemplate().flush(); 

明确设置分离对象null也有帮助。

1

将注释 @GeneratedValue 添加到要插入的bean。

1

我有这个错误几天走了,我加快了修复这个错误太多时间。

public boolean save(OrderHeader header) { 
    Session session = sessionFactory.openSession(); 


    Transaction transaction = session.beginTransaction(); 

    try { 
     session.save(header); 

     for (OrderDetail detail : header.getDetails()) { 
      session.save(detail); 
     } 

     transaction.commit(); 
     session.close(); 

     return true; 
    } catch (HibernateException exception) { 

     exception.printStackTrace(); 
     transaction.rollback(); 
     return false; 
    } 
} 

在我得到这个错误之前,我没有提到OrderDetil对象上的ID生成类型。当没有生成Orderdetails的id时,它将Id保持为每个OrderDetail对象的0。这是#jbx解释的。是的,这是最好的答案。这个例子是如何发生的。

12

只需将级联设置为MERGE即可。

2

我同意@Hemant kumar,谢谢你差异很大。根据他的解决方案,我解决了我的问题。

例如:

@Test 
public void testSavePerson() { 
    try (Session session = sessionFactory.openSession()) { 
     Transaction tx = session.beginTransaction(); 
     Person person1 = new Person(); 
     Person person2 = new Person(); 
     person1.setName("222"); 
     person2.setName("111"); 
     session.save(person1); 
     session.save(person2); 
     tx.commit(); 
    } 
} 

Person.java

public class Person { 
    private int id; 
    private String name; 

    @Id 
    @Column(name = "id") 
    public int getId() { 
     return id; 
    } 

    public void setId(int id) { 
     this.id = id; 
    } 

    @Basic 
    @Column(name = "name") 
    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

} 

该代码总是出错在我的应用程序: A different object with the same identifier value was already associated with the session ,后来,我发现我frogot 到autoincrease我首要的关键!

我的解决办法是在你的主键添加以下代码:

@GeneratedValue(strategy = GenerationType.AUTO) 
1

尝试之前将您的查询的代码。 解决了我的问题。 例如更改此设置:

query1 
query2 - get the error 
update 

这样:

query2 
query1 
update 
1

查找休眠的 “级联” 属性附加伤害,并删除它。当您设置“Cascade”可用时,它将调用其他与相关类关联的实体的其他操作(保存,更新和删除)。所以相同的身份值将会发生。 它与我合作。

0

我满足,因为主键生成的问题是错误的,当我插入一行是这样的:

public void addTerminal(String typeOfDevice,Map<Byte,Integer> map) { 
     // TODO Auto-generated method stub 
     try { 
      Set<Byte> keySet = map.keySet(); 
      for (Byte byte1 : keySet) { 
       Device device=new Device(); 
       device.setNumDevice(DeviceCount.map.get(byte1)); 
       device.setTimestamp(System.currentTimeMillis()); 
       device.setTypeDevice(byte1); 
       this.getHibernateTemplate().save(device); 
      } 
      System.out.println("hah"); 
     }catch (Exception e) { 
      // TODO: handle exception 
      logger.warn("wrong"); 
      logger.warn(e.getStackTrace()+e.getMessage()); 
     } 
} 

我改变ID生成器类身份

<id name="id" type="int"> 
    <column name="id" /> 
    <generator class="identity" /> 
</id> 
相关问题