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