休眠中的自然身份证
自然ID是可以唯一標識一個實體的一個或一組屬性。 我們最多可以為一個實體定義一個自然ID。 當Hibernate在實體映射文件中看到natural-id標記時,它會自動在構成natural-id的屬性上創建唯一且非空的約束。 首先,讓我們看一下簡單和復合自然ID的示例。
簡單的自然ID:一個人可以通過其選民ID進行唯一標識。 因此,可以說這可能來自他的自然身份。
復合自然ID:電話號碼,即標準代碼和固定電話號碼的組合,可以形成個人實體的自然ID。
<!-- Version 2 --> <hibernate-mapping package="com.pramati.model"><class name="Person" table="PERSON"><id name="id" column="ID"><generator class="native"/></id><natural-id><property name="stdCode" type="string" column="STD_CODE"/><property name="landlineNumber" type="string" column="LANDLINE_NUMBER"/></natural-id><property name="name" type="string" column="NAME"/><!-- Other properties --> </class> </hibernate-mapping>因此,Hibernate在stdCode和landlineNumber上創建了一個非空約束。 這些屬性一起對于個人實體應該是唯一的。
默認情況下,自然ID是不可變的。 因此,假設您嘗試從數據庫中加載人員實體并更改構成自然ID的任何屬性,則Hibernate將引發異常。 例如,我們已加載Person并嘗試在活動會話中修改其landlineNumber / stdcode,這是我們會得到的例外:
org.hibernate.HibernateException:: An immutable natural identifier of entity com.pramati.model.Person was altered from abc to xyzHibernate 4.1提出了通過bean的natural-id加載實體的功能。 到目前為止,會話緩存將緩存通過當前會話中的get / load加載的對象。 現在,默認情況下還將緩存使用natural-id加載的對象。 以下是會話API的最新功能:
public NaturalIdLoadAccess byNaturalId(String entityName); public NaturalIdLoadAccess byNaturalId(Class entityClass);public SimpleNaturalIdLoadAccess bySimpleNaturalId(String entityName); public SimpleNaturalIdLoadAccess bySimpleNaturalId(Class entityClass);我們可以通過自然ID加載類的實例,如下所示:
// In case of version 1 defined above: Person person = (Person)session.byNaturalId(Person.class ).using( "voterID", "ZAAXDFT435" ).load();// For Version 1, this can be simplified as: Person person = (Person)session.bySimpleNaturalId(Person.class ).load("ZAAXDFT435");// In case of version 2 defined above: Person person = (Person)session.byNaturalId(Person.class ).using("stdCode", "040").using("landlineNumber","2345678").load();請注意,負載返回的實體不僅是代理,而且是實際實體本身。 如果要獲取代理,則必須使用getReference()代替load(),如下所示:
session.byNaturalId(Person.class ) .using("stdCode", "040") .using("landlineNumber","2345678") .getReference();為了保持一致性,新方法也可用于基于標識符的加載。
public IdentifierLoadAccess byId(String entityName); public IdentifierLoadAccess byId(Class entityClass);因此,我們可以使用session.byId(Person.class).getReference(id)代替session.load(Person.class,id)。 而不是session.get(Person.class,id)我們可以使用session.byId(Person.class).load(id)
當我們使用查詢緩存時,自然ID也很有用。 查詢緩存通常沒有那么有用,因為它經常變得無效。 假設事件序列如下:
方案1:
1.使用實體natural-id中的屬性進行HQL查詢以加載人員A。 查詢也被緩存,即query.setCacheable(true)
2.將另一個人B插入到人表中。
3.現在,使用與步驟1中相同的查詢再次加載A。 問題是:在步驟3中,將執行新的數據庫調用以從“人”表中獲取A。 是還是不是?
答案是肯定的。 發生的事情是Hibernate在內部維護一個時間戳緩存。 這個時間戳緩存記錄特定的Hibernate受管表被修改的時間。 現在在步驟(3),Hibernate看到它是一個緩存的查詢。 但是在返回存在于緩存中的實體之前,它會驗證緩存的結果相對于表修改時間是否較舊。 現在,在緩存后修改表之后,Hibernate再次進行新查詢。
為了進一步了解這一點,讓我們考慮以下情形:讓我們只在名稱為Rama的Person表中進行記錄
方案2:
一個。 執行緩存的查詢以獲取名稱與“ Rama”匹配的人員列表:“來自人員名稱為“ Rama”的人員”
b。 也將記錄插入名稱也為“ Rama”的“個人”中。 這不是問題,因為名稱未定義為唯一屬性 C。 現在,再次執行步驟(a)中的查詢。
最初在步驟(a),我們僅獲得記錄。 但是在步驟(c)中,即使結果被緩存,休眠也會再次命中數據庫。 這是由于時間戳緩存無效而發生的。 Hibernate只是在從緩存返回實體之前檢查表是否已被修改。 但是,無論是更新,插入還是后續操作,都不會影響表的更新方式。
但是在我們看過的前一種情況中,此驗證檢查似乎完全不相關,因為插入的記錄與加載的實體無關。 如果我們使用自然ID來獲取實體,則可以繞過此檢查。 使用natural-id時,可以保證即使修改數據庫后結果也不會改變。 早些時候,當我們不支持使用自然ID加載實體時,我們在Criteria API中提供了使用自然ID的規定。 我們可以在方案1的步驟(1)和(3)中使用以下內容
session.createCriteria(Person.class).add(Restrictions.naturalId().set("stdCode", person.getStdCode()).set("landlineNumber", person.getLandlineNumber())).setCacheable(true).uniqueResult(); 當使用自然ID來獲取實體時,時間戳緩存檢查將被繞過。 因此,現在如果我用此條件而不是查詢替換第一種情況的步驟(1)和(3),則數據庫只會被命中一次。 如果我們使用Restrictions.eq而不是Restrictions.naturalId,則數據庫將被命中兩次。 另外,如果您使用的是最新版本的Hibernate,我們可以使用新的API代替構建標準。
翻譯自: https://www.javacodegeeks.com/2013/10/natural-ids-in-hibernate.html
總結
- 上一篇: 手机gps定位不插卡能定位吗
- 下一篇: 手机会自动返回怎么解决