先簡單介紹一下問題的語境。
手頭有個(gè)開發(fā)了3年的Spring+iBATIS應(yīng)用(經(jīng)典三層架構(gòu)),最近嘗試用Hibernate實(shí)現(xiàn)原有SQLMap版的部分CRUD操作。除開混合事務(wù)和其他一些底層配置細(xì)節(jié)(如TransactionAwareDataSource、禁用lazy-load等)之外,最大的一個(gè)"pattern-mismatch"是:Model層和持久層采用了dirty flag機(jī)制,即每次INSERT和UPDATE操作,都會(huì)根據(jù)每個(gè)字段的dirty與否決定是否參加INSERT/UPDATE,而這些dirty flag可以被外部重置,所以業(yè)務(wù)層的代碼,經(jīng)常可以看到類似這樣的pattern:
1- new一個(gè)model類并setId() (或者在已有的model上重置dirty flag)
2- set需要update的字段(通常都只是部分字段)
3- 丟給DAO層去update
最終的效果是某張表某個(gè)ID的某條記錄的某些字段被更新了,而其他字段不受影響,這就是我在標(biāo)題中提到的所謂"暴力"update,雖不elegant,但卻也簡單實(shí)用,至少很"直接"。
為了讓Hibernate版的DAO(默認(rèn)除Trasient之外全體字段參加INSERT和UPDATE)繼續(xù)支持這樣的"use-pattern",除了按照Hibernate的習(xí)慣去配置映射和SessionFactory等之外,我們需要做一些額外的工作:
1- 在BO/Entity類上追加注解
@org.hibernate.annotations.Entity(dynamicInsert=true,?dynamicUpdate=true)
2- 實(shí)現(xiàn)org.hibernate.Interceptor接口的findDirty()方法,Hibernate提供了一個(gè)EmptyInterceptor可以作為起點(diǎn),方法簽名如下:
public?int[]?findDirty(
????Object?entity,?
????Serializable?id,?
????Object[]?currentState,?
????Object[]?previousState,?
????String[]?propertyNames,?
????Type[]?types
);
返回的int[]包含所有應(yīng)該被認(rèn)為是dirty的字段索引,返回null表示默認(rèn)處理,傳入的entity是持久對象,字段列表會(huì)通過propertyNames參數(shù)傳給你。
3- 注入到Spring的Application Context中,類似這樣:
<bean?id="findDirtyInterceptor"?class="gao.sean.hybrid.interceptor.FindDirtyInterceptor"/>
<bean?id="sessionFactory"
????class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
????
????<property?name="entityInterceptor"><ref?bean="findDirtyInterceptor"/></property>
????
</bean>
在這樣的配置下,業(yè)務(wù)層的代碼就可以繼續(xù)"暴力"update了。