2013-04-26 128 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> 
相關問題