Hibernate與數據庫觸發(fā)器協(xié)同工作
數據庫觸發(fā)器是數據庫的一種監(jiān)聽機制,這種機制會監(jiān)視數據庫中的某種特定的操作,當這種操作發(fā)生時,觸發(fā)器就會執(zhí)行,并且會完成一些特定的邏輯,在數據庫中能夠激發(fā)觸發(fā)器的操作有:insert,update,delete三種,當這三種操作發(fā)生在一條記錄或者某個特殊字段上,如果在該記錄或者該字段上有觸發(fā)器,那么觸發(fā)器便會被激發(fā)執(zhí)行。但是當Hibernate與觸發(fā)器協(xié)同執(zhí)行時,會造成兩個問題。
第一:觸發(fā)器使得Session中的數據與數據庫中的數據不一致;
第二:Session的update操作會盲目觸發(fā)觸發(fā)器;
下面我們就分別討論這兩個問題的原因和解決辦法。
A、觸發(fā)器使得Session中的數據與數據庫中的數據不一致:
造成這個問題的根本原因,是因為觸發(fā)器對數據庫的操作對Hibernate Session是透明的,觸發(fā)器對數據庫的某些更改,無法被Session感知,因此無法被同步到緩存之中,我們看下面的代碼,假設在數庫中的user表中的registerd_time字段上設置了一個觸發(fā)器,當有新的user對象被保存時,數據庫就自動將數據庫當前時間之值,插入到該字段中:
tx=session.beginTransaction();
session.save(user);
System.out.println(user.getRegisterdTime());
tx.commit();
當執(zhí)行完save()操作后,打印注冊時間字段,這時發(fā)現(xiàn)該字段值為空。這是因為此時真正的insert SQL語句還沒有真正執(zhí)行,所以觸發(fā)器還沒有被執(zhí)行。我們修改上面的代碼如下:
tx=session.beginTransaction();
session.save(user);
session.flush();
System.out.println(user.getRegisterdTime());
user=session.load(User.class,”1”);
System.out.println(user.getRegisterdTime());
tx.commit();
我們增加了強制清空緩存并且強制提交的操作,這個操作會激發(fā)insert SQL語句的執(zhí)行,并且激發(fā)觸發(fā)器的執(zhí)行,此時打印注冊時間發(fā)現(xiàn)已經變成了數據庫當前時間,但是當再次加載剛剛被保存的user對象后打印注冊時間,發(fā)現(xiàn)還是空值,這是因為由于觸發(fā)器會自動填寫注冊時間值,所以在構造user對象時沒有設置該屬性值,這個屬性值被觸發(fā)器在數據庫端自動設置,但是由于觸發(fā)器對數據庫的操作對Hibernate Session是透明的,觸發(fā)器對該字段的更改,無法被Session感知,所以在緩存中該字段仍然是null,當加載這個對象時,由于save操作此對象被置于緩存中,所以load方法從緩存中將該對象獲得,當調用user.getRegisterdTime()時會得到緩存中的空值。那么我們怎樣才能避免出現(xiàn)這個問題呢?看下面的代碼:
tx=session.beginTransaction();
session.save(user);
session.flush();
System.out.println(user.getRegisterdTime());
session.refresh(user);
user=session.load(User.class,”1”);
System.out.println(user.getRegisterdTime());
tx.commit();
這里我們調用了refresh方法,該方法會重新從數據庫中加載剛剛被保存的user對象到緩存中,這樣就同步了緩存與數據庫數據,所以當再次調用load方法時,從緩存中獲得的數據就會同數據庫中的數據保持同步,所以再次打印注冊時間時,就會打印出數據庫當前時間。
B、Session的update操作會盲目觸發(fā)觸發(fā)器:
如果在數據庫中定義了針對update操作的觸發(fā)器,那么必須要謹慎的使用Session的update和saveOrUpdate(),因為這兩個方法能夠使一個游離對象再次變成持久化對象,因為有可能在Session的緩存中,還不存在要更新對象的快照,所以就無法判斷游離對象的屬性是否與數據庫中保持一致,為了保險起見,Hibernate默認的操作是,不管實體對象的屬性是否發(fā)生了變化,都要發(fā)起一條update SQL語句的執(zhí)行,來同步實體對象屬性值與數據庫字段值,即同步數據庫字段值與緩存數據屬性值。這時如果對應的數據庫表中有update觸發(fā)器,那么就會觸發(fā)該觸發(fā)器的執(zhí)行,但是如果此時這個對象的屬性值并沒有發(fā)生改變,也就是說實體對象的屬性值與數據庫中對應表的對應記錄保持一致,那么,這個觸發(fā)器的執(zhí)行就是沒有必要的,此時Session的update操作就觸發(fā)了多余的觸發(fā)器。那么我們怎樣才能避免這個問題呢?我們可以開啟實體對象配置文件中<class>元素的,“select-before-update”屬性選項,我們可以如下進行配置:
<class name=”com.neusoft.entity.User” table=”user” select-before-update=”true”>
……
</class>
這時當執(zhí)行Session.update()/saveOrUpdate()操作時,會首先執(zhí)行select SQL,查詢出該對象所有
屬性值,然后與要進行更新的實體對象屬性值進行比較,如果都相同,那么就不會執(zhí)行update
作,這時就不會由于執(zhí)行了沒必要的update SQL語句,而激發(fā)多余的update觸發(fā)器操作了。
|
|
來自: feimishiwo > 《hibernate》