休眠中的自然身份证
自然ID是可以唯一標(biāo)識(shí)一個(gè)實(shí)體的一個(gè)或一組屬性。 我們最多可以為一個(gè)實(shí)體定義一個(gè)自然ID。 當(dāng)Hibernate在實(shí)體映射文件中看到natural-id標(biāo)記時(shí),它會(huì)自動(dòng)在構(gòu)成natural-id的屬性上創(chuàng)建唯一且非空的約束。 首先,讓我們看一下簡(jiǎn)單和復(fù)合自然ID的示例。
簡(jiǎn)單的自然ID:一個(gè)人可以通過其選民ID進(jìn)行唯一標(biāo)識(shí)。 因此,可以說這可能來自他的自然身份。
復(fù)合自然ID:電話號(hào)碼,即標(biāo)準(zhǔn)代碼和固定電話號(hào)碼的組合,可以形成個(gè)人實(shí)體的自然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上創(chuàng)建了一個(gè)非空約束。 這些屬性一起對(duì)于個(gè)人實(shí)體應(yīng)該是唯一的。
默認(rèn)情況下,自然ID是不可變的。 因此,假設(shè)您嘗試從數(shù)據(jù)庫中加載人員實(shí)體并更改構(gòu)成自然ID的任何屬性,則Hibernate將引發(fā)異常。 例如,我們已加載Person并嘗試在活動(dòng)會(huì)話中修改其landlineNumber / stdcode,這是我們會(huì)得到的例外:
org.hibernate.HibernateException:: An immutable natural identifier of entity com.pramati.model.Person was altered from abc to xyzHibernate 4.1提出了通過bean的natural-id加載實(shí)體的功能。 到目前為止,會(huì)話緩存將緩存通過當(dāng)前會(huì)話中的get / load加載的對(duì)象。 現(xiàn)在,默認(rèn)情況下還將緩存使用natural-id加載的對(duì)象。 以下是會(huì)話API的最新功能:
public NaturalIdLoadAccess byNaturalId(String entityName); public NaturalIdLoadAccess byNaturalId(Class entityClass);public SimpleNaturalIdLoadAccess bySimpleNaturalId(String entityName); public SimpleNaturalIdLoadAccess bySimpleNaturalId(Class entityClass);我們可以通過自然ID加載類的實(shí)例,如下所示:
// 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();請(qǐng)注意,負(fù)載返回的實(shí)體不僅是代理,而且是實(shí)際實(shí)體本身。 如果要獲取代理,則必須使用getReference()代替load(),如下所示:
session.byNaturalId(Person.class ) .using("stdCode", "040") .using("landlineNumber","2345678") .getReference();為了保持一致性,新方法也可用于基于標(biāo)識(shí)符的加載。
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)
當(dāng)我們使用查詢緩存時(shí),自然ID也很有用。 查詢緩存通常沒有那么有用,因?yàn)樗?jīng)常變得無效。 假設(shè)事件序列如下:
方案1:
1.使用實(shí)體natural-id中的屬性進(jìn)行HQL查詢以加載人員A。 查詢也被緩存,即query.setCacheable(true)
2.將另一個(gè)人B插入到人表中。
3.現(xiàn)在,使用與步驟1中相同的查詢?cè)俅渭虞dA。 問題是:在步驟3中,將執(zhí)行新的數(shù)據(jù)庫調(diào)用以從“人”表中獲取A。 是還是不是?
答案是肯定的。 發(fā)生的事情是Hibernate在內(nèi)部維護(hù)一個(gè)時(shí)間戳緩存。 這個(gè)時(shí)間戳緩存記錄特定的Hibernate受管表被修改的時(shí)間。 現(xiàn)在在步驟(3),Hibernate看到它是一個(gè)緩存的查詢。 但是在返回存在于緩存中的實(shí)體之前,它會(huì)驗(yàn)證緩存的結(jié)果相對(duì)于表修改時(shí)間是否較舊。 現(xiàn)在,在緩存后修改表之后,Hibernate再次進(jìn)行新查詢。
為了進(jìn)一步了解這一點(diǎn),讓我們考慮以下情形:讓我們只在名稱為Rama的Person表中進(jìn)行記錄
方案2:
一個(gè)。 執(zhí)行緩存的查詢以獲取名稱與“ Rama”匹配的人員列表:“來自人員名稱為“ Rama”的人員”
b。 也將記錄插入名稱也為“ Rama”的“個(gè)人”中。 這不是問題,因?yàn)槊Q未定義為唯一屬性 C。 現(xiàn)在,再次執(zhí)行步驟(a)中的查詢。
最初在步驟(a),我們僅獲得記錄。 但是在步驟(c)中,即使結(jié)果被緩存,休眠也會(huì)再次命中數(shù)據(jù)庫。 這是由于時(shí)間戳緩存無效而發(fā)生的。 Hibernate只是在從緩存返回實(shí)體之前檢查表是否已被修改。 但是,無論是更新,插入還是后續(xù)操作,都不會(huì)影響表的更新方式。
但是在我們看過的前一種情況中,此驗(yàn)證檢查似乎完全不相關(guān),因?yàn)椴迦氲挠涗浥c加載的實(shí)體無關(guān)。 如果我們使用自然ID來獲取實(shí)體,則可以繞過此檢查。 使用natural-id時(shí),可以保證即使修改數(shù)據(jù)庫后結(jié)果也不會(huì)改變。 早些時(shí)候,當(dāng)我們不支持使用自然ID加載實(shí)體時(shí),我們?cè)贑riteria API中提供了使用自然ID的規(guī)定。 我們可以在方案1的步驟(1)和(3)中使用以下內(nèi)容
session.createCriteria(Person.class).add(Restrictions.naturalId().set("stdCode", person.getStdCode()).set("landlineNumber", person.getLandlineNumber())).setCacheable(true).uniqueResult(); 當(dāng)使用自然ID來獲取實(shí)體時(shí),時(shí)間戳緩存檢查將被繞過。 因此,現(xiàn)在如果我用此條件而不是查詢替換第一種情況的步驟(1)和(3),則數(shù)據(jù)庫只會(huì)被命中一次。 如果我們使用Restrictions.eq而不是Restrictions.naturalId,則數(shù)據(jù)庫將被命中兩次。 另外,如果您使用的是最新版本的Hibernate,我們可以使用新的API代替構(gòu)建標(biāo)準(zhǔn)。
翻譯自: https://www.javacodegeeks.com/2013/10/natural-ids-in-hibernate.html
總結(jié)
- 上一篇: 手机gps定位不插卡能定位吗
- 下一篇: 什么是可重入锁?