?? 用oracle的trigger生成主鍵的時(shí)候和hibernate沖突的討論.txt
字號:
作者:robbin (MSN:robbin_fan AT hotmail DOT com)
版權(quán)聲明:本文嚴(yán)禁轉(zhuǎn)載,如有轉(zhuǎn)載請求,請和作者聯(lián)系
oracle的trigger和hibernate沖突
capitain write:
我們的數(shù)據(jù)庫定義mapping使用了
<id name="id" column="ID" type="long">
<generator class="sequence">
<param name="sequence">seq_somename</param>
</generator>
</id>
但是這樣做 我發(fā)現(xiàn) hibernate會取得一次sequence, 同時(shí)數(shù)據(jù)庫還有一個(gè)triger會取得一次sequence, 這樣實(shí)際的sequence會加2了!! 而且create返回的對象的主鍵id也是錯(cuò)誤的, 因?yàn)檫€被triger加了1, 數(shù)據(jù)庫里面的記錄全面是雙數(shù), 2,4,6,8 create返回主鍵卻是1,3,5,7 請問 這樣問題怎么解決, 因?yàn)閠riger不能刪除, 能讓hibernate利用triger生成的取值嗎?
我查看了hibernate的文檔和源代碼, 發(fā)現(xiàn)generator 為 sequence時(shí)候
它最后會調(diào)用這個(gè)方法
dialect下面的Oracle9Dialect中的
public String getSequenceNextValString(String sequenceName) {
return "select " + sequenceName + ".nextval from dual";
}
triger再這么走一遍 sequence必然是加入2次
我使用native,發(fā)現(xiàn)工作結(jié)果和sequence一樣, 難道沒有辦法解決了嗎?
robbin write:
我有一個(gè)最簡單的解決辦法:
看Hibernate2.0.3源代碼的net.sf.hibernate.dialect.Oracle9Dialect第78行:
public String getSequenceNextValString(String sequenceName) {
return "select " + sequenceName + ".nextval from dual";
}
只要我們把它改成下面這個(gè)樣子,就行了:
public String getSequenceNextValString(String sequenceName) {
return "select " + sequenceName + ".currval + 1 from dual";
}
也就是說Hibernate去取序列值的時(shí)候,不讓序列加1,只取當(dāng)前值然后加1返回,插入的時(shí)候讓trigger來給序列加1
因此你可以這樣來做,寫一個(gè)新的Dialect:
package net.sf.hibernate.dialect;
public class OracleMyDialect extends OracleDialect {
public String getSequenceNextValString(String sequenceName) {
return "select " + sequenceName + ".currval + 1 from dual";
}
}
編譯好以后放在CLASSPATH里面,配置Hibernate
hibernate.dialect net.sf.hibernate.dialect.OracleMyDialect
這樣就行了,解決掉你那個(gè)問題了。
capitain write:
謝謝, 我對oracle很不熟悉, 不知道,這樣只加一 如果在多個(gè)連接向數(shù)據(jù)庫內(nèi)插入記錄的時(shí)候, 會不會有問題,
換句話說,不知道hibernate里面這個(gè)getSequenceNextValString()和triger發(fā)生之間時(shí)間內(nèi)如果有插入會有問題嗎? 不知道oracle的驅(qū)動能不能保證這是一個(gè)原子操作?
robbin write:
不能夠保證原子性,可能會有并發(fā)訪問的沖突。不過目前來說你也只能如此了,說實(shí)話這個(gè)辦法只能算是權(quán)宜之計(jì)。
由于session.save() 需要返回主鍵,所以當(dāng)你使用trigger來創(chuàng)建id的時(shí)候,Hibernate是無法知道trigger當(dāng)前創(chuàng)建的主鍵是什么,不論你用
select sequ.currval from dual;
還是
select max(id) from table_name;
在插入數(shù)據(jù)之后來獲得主鍵,都有并發(fā)沖突的可能性。唯一的避免并發(fā)沖突的辦法是:
insert into table_name ... returning :id;
用Oracle特有的這個(gè)sql來插入數(shù)據(jù),返回主鍵,讓Hibernate知道主鍵的值,不過Hibernate目前并不能夠支持這個(gè)sql語句,如果要讓Hibernate支持這個(gè)sql,需要對源代碼做相當(dāng)大的改動。看看以后會不會加吧。
這個(gè)問題我不是第一次和別人討論了,確實(shí)沒有好的解決辦法,除非對Hibernate進(jìn)行大面積修改。但是話說回來,我用Oracle那么多年了,從來都不使用trigger實(shí)現(xiàn)Auto increment字段,我個(gè)人認(rèn)為這種方式不好。本來Oracle已經(jīng)把sequence從字段里面解放出來了,你還要在把它放回去。不用trigger一樣可以生成主鍵,看兩個(gè)例子:
插入單條數(shù)據(jù):
insert table_name(id, name,...) values(sequ.nextval, ....);
批量插入數(shù)據(jù):
insert table_name(id, name,...) select sequ.nextval, name,... from table2_name;
richart write:
這么做的話,最好是用一下nvl()函數(shù)(oracle自帶),而且只能改動oracle相關(guān)的一個(gè)類。如果使用的是別的數(shù)據(jù)庫,不知道有沒有相似的問題存在?
robbin write:
oldma 寫道:呵呵,誰能給解釋一下?
看下面的PL/SQL:
create trigger y before insert on bob
for each row
when (new.a is null)
begin
select x.nextval into :new.a from dual;
end;
用trigger來讀sequence,插入表的主鍵字段,模擬SQL Server中的Auto Increment 字段
capitain write:
那oracle還有什么標(biāo)準(zhǔn)的辦法實(shí)現(xiàn)Auto Increment 字段? 我們目前的程序插入的時(shí)候是按照auto increment字段來的, 就是不寫id那個(gè)字段, 讓數(shù)據(jù)庫操作, 請問其他辦法可以適用嗎?
mikeho write:
是的,oracle是不必再寫sp了,我們以前是在SQL server里面寫了個(gè)sp來生成seq,沒有用自增長,因?yàn)樽栽鲩L在方法調(diào)用后返回id比較麻煩。現(xiàn)在都使用模擬UUID了(只有50位,理論上有重復(fù)建的危險(xiǎn),呵呵)。
--------------------------------------------------------------------------------
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -