當對象持久化到數據庫中時對象的標識符總時很難被恰當的實現盡管如此問題其實完全是由存在著在保存之前不持有ID的對象的現象衍生而來的我們可以通過從諸如Hibernate這樣的對象—關系映像框架手中取走指派對象ID的職責來解決這個問題相對的一旦對象被實例化它就應該被指派一個ID這使對象標識符變成簡單而不易出錯也減少了領域模型中需要的代碼量
企業級Java應用程序常常把數據在java對象和關系型數據庫之間來回移動從手動編寫SQL代碼到使用諸如hibernate這樣的成熟的對象——關系映像(ORM)解決方案有很多種方法可以實現這個過程無論你采用什麼樣的技術一旦你開始將java對象持久化到數據庫中對象標識符都將成為一個復雜而且難以管理的課題可能出現的情況是你實例化了兩個不同的對象而它們卻代表了數據庫中的同一行為了解決這個問題你可能采取的措施是在你的持久化對象中實現equals() 和hashCode()這兩個方法可是要恰當的實現這兩個方法比乍看之下要有技巧一些讓問題更糟糕的是那些傳統的思路(包括hibernate官方文檔所提倡的那些)對於新的工程並不一定能提出最實用的解決方案
對象標識在虛擬機(VM)中和在數據庫中的差異是問題滋生的溫床在虛擬機中你並不會得到對象的id你只是簡單的持有對象的直接引用而在幕後虛擬機確實給每個對象指派了一個字節大小的id這個id才是對象的真實引用當你將對象持久化到數據庫中的時候問題開始產生了假定你創建了一個Person對象並將它存入數據庫(我們可以叫它person)而你的其它某段代碼從數據庫中讀取了這個Person對象的數據並將它實例化為另一個新的Person對象(我們可以叫它Person)現在你的內存中有了兩個映像到數據庫中同一行的對象一個對象引用只能指向它們倆的其中一個可是我們需要一種方法來表示這兩個對象實際上表示著同一個實體這就是(在虛擬機中)引入對象標識符的原因
在java語言中對象標識符是由每個對象都持有的equals()方法(以及相關的hashCode()方法)來定義的無論兩個對象(引用)是否為同一個實例equals()方法都應該能夠判別出它們是否表示同一個實體hashCode()方法和equals()方法有關聯是因為所有被判斷等價(equal)的對象都應該返回相同的哈希值(hashCode)在缺省實現中equals()方法僅僅比較對象的引用一個對象和它自身是等價的而和其它任何實例都不等價對於持久化對象來說重寫這兩個方法讓代表著數據庫中同一行的兩個對象被判為等價是很重要的而這對於java中的Collection數據結構(SetMap和List)的正確工作更是尤為重要
為了闡明實現equal()和hashCode()的不同途徑讓我們一起考慮一個准備持久化到數據庫中的簡單對象Person
public class Person {
private Long id;
private Integer version;
public Long getId() { return id; }
public void setId(Long id) {
this
id = id;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this
version = version;
}
// person
specific properties and behavior
}
在這個例子中我們遵循了同時持有id字段和version字段的最佳實踐Id字段保存了在數據庫中作為主鍵使用的值而version字段則是一個從開始增長的增量隨著對象的每次更新而變化(它幫助我們避免並發更新的問題)為了看的更清楚我們也一起看一下Hibernate把這個對象持久化到數據庫的映像文件
<?XML version=?>
<hibernatemapping package=mypackage>
<class name=Person table=PERSON>
<id name=id column=ID unsavedvalue=null>
<generator class=sequence>
<param name=sequence>PERSON_SEQ</param>
</generator>
</id>
<version name=version column=VERSION />
<! Map Personspecific properties here >
</class>
</hibernatemapping>
[] [] [] [] []
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28970.html