2011-05-19 58 views
6

我是新來的Hibernate和數據庫一般,所以請原諒基本問題。休眠自然ID重複問題

我正在與DIS protocol合作,特別是DIS的Open-DIS實現。在DIS中,每個EntityStatePdu(包含模擬中實體的狀態)都有一個EntityId對象,一個由3個整數組成的元組。我想將此對象用作自然ID,並且還維護標準的代理ID。我的問題是我無法弄清楚如何確保數據庫確定給定的EntityId已經存在,並使用該EntityId的主鍵作爲EntityStatePdu中的外鍵。換句話說,我說有兩個EntityStatePdus,EntityID(1,2,3);如果我有兩個EntityStatePdus,EntityID(1,2,3);即我們有來自同一個實體的兩個更新。我希望像下面這樣:

表:

entity_id 
pk site app entity 
0 1  2  3 


entity_state_pdu 
pk entity_id_fk timestamp 
0 0    1 
1 0    2 

這裏是簡化類我與測試:

@Entity 
public class TestEntity { 

    @Id 
    @GeneratedValue(strategy=GenerationType.AUTO) 
    private Long id; 

    @NaturalId 
    @ManyToOne(cascade = CascadeType.ALL) 
    private TestId naturalId; 

    public Long getId() { 
     return id; 
    } 

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

    public TestId getNaturalId() { 
     return naturalId; 
    } 

    public void setNaturalId(TestId naturalId) { 
     this.naturalId = naturalId; 
    } 
} 

@Entity 
public class TestId { 

    @Id 
    @GeneratedValue(strategy=GenerationType.AUTO) 
    private Long id; 

    @NaturalId 
    private int site; 
    @NaturalId 
    private int app; 
    @NaturalId 
    private int entity; 

    public TestId(int site, int app, int entity) { 
     this.site = site; 
     this.app = app; 
     this.entity = entity; 
    } 

    public Long getId() { 
     return id; 
    } 
    public void setId(Long id) { 
     this.id = id; 
    } 
    public int getSite() { 
     return site; 
    } 
    public void setSite(int site) { 
     this.site = site; 
    } 
    public int getApp() { 
     return app; 
    } 
    public void setApp(int app) { 
     this.app = app; 
    } 
    public int getEntity() { 
     return entity; 
    } 
    public void setEntity(int entity) { 
     this.entity = entity; 
    } 

    @Override 
    public int hashCode() { 
     final int prime = 31; 
     int result = 1; 
     result = prime * result + app; 
     result = prime * result + entity; 
     result = prime * result + site; 
     return result; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (this == obj) 
      return true; 
     if (obj == null) 
      return false; 
     if (getClass() != obj.getClass()) 
      return false; 
     TestId other = (TestId) obj; 
     if (app != other.app) 
      return false; 
     if (entity != other.entity) 
      return false; 
     if (site != other.site) 
      return false; 
     return true; 
    } 
} 

我嘗試存儲將兩個TestEntity對象分成兩個獨立的TestId對象(這些對象具有相同的站點) ,應用程序和實體)的方式如下:

public static void main(String[] args) { 

    SessionFactory factory = createFactory(); 

    Session session = factory.openSession(); 
    Transaction tx = session.beginTransaction(); 
    TestId id1 = new TestId(1,2,3); 
    TestEntity entity1 = new TestEntity(); 
    entity1.setNaturalId(id1); 
    session.save(entity1); 
    tx.commit(); 
    session.close(); 

    Session session2 = factory.openSession(); 
    Transaction tx2 = session2.beginTransaction(); 
    TestId id2 = new TestId(1,2,3); 
    TestEntity entity2 = new TestEntity(); 
    entity2.setNaturalId(id2); 
    session2.save(entity2); 
    tx2.commit(); 
    session2.close(); 
} 

我弄了半天堆棧跟蹤的session2.save(ENTITY2)線,以突出線是

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '2-3-1' for key 'app' 

我希望這是足夠清楚。謝謝。

回答

1

當您將某些字段標記爲自然ID時,這意味着在該上下文中這些字段的組合將是唯一的,例如,如果您有一個名爲人名的名稱爲FirstName且姓名爲自然ID的類,則只能有一個實體。有約翰作爲它的名字和史密斯作爲它的名字(它類似於一個唯一索引)

在您的代碼:

TestId id1 = new TestId(1,2,3); 

TestId id2 = new TestId(1,2,3); 

指的是具有相同自然ID的兩個不同實體,因此它們不能同時存在於數據庫中。

+0

在這個例子中,ID對象是一個接一個地創建的。在真正的應用程序中,我們反序列化一個字節流,並在該反序列化過程中爲每個EntityStatePdu實例化一個新的EntityId對象。但從邏輯上講,它代表着與數據庫中已有的EntityId完全相同的EntityId,即具有相同的(site,app,entityId)三元組。 – I82Much 2011-05-19 13:03:51

2

正如披頭士所說,當你告訴Hibernate你有一個自然鍵,它告訴數據庫強制唯一性。這種情況下的唯一性是通過引發你看到的類型的異常(MySQLIntegrityConstraintViolationException)來強制執行。

我知道的唯一方法是首先嚐試並首先獲取與您的業務身份(TestId等於)匹配的對象,然後與該實例一起工作(如果找到或使用新實例)if不是。 Hibernate不會自動爲你做。

+0

謝謝 - 在我設計的測試中使用Map ,並存儲ID,ID對。 (當你有一個id時,在序列化之前檢查它是否已經存在於map中,如果是,則用存儲的替換它)。不得不這樣做的刺激性,但如果這是讓它工作的唯一方法,我想我已經辭職了。奇怪的是,相同的技術不適用於我的更大的使用場景...不知道有什麼不同。 – I82Much 2011-05-19 14:59:11

+2

我剛剛花了大約30秒,試圖找出你指的是哪首披頭士歌詞,然後纔看到@ Beatles1692的回答:-)。 – 2011-05-19 15:36:40

+3

@Matt>這是八達通花園中的原始經文之一,因錄製前被錄音室政治裁掉。他們用「自然鑰匙」在海底度過了一段簡單的時間押韻,但洋子讓他們試圖通過將其押韻爲「公主」來「強制獨特」來對英國王室進行評論。這並不漂亮。 – Paul 2011-05-19 15:44:19