HibernateTemplate中HibernateCallback的事務(wù)
目的:使用HibernateTemplate執(zhí)行execute(new HibernateCallback())方法,從HibernateCallback中得到session,在此session中做多個操作,并希望這些操作位于同一個事務(wù)中。
??????如果你這樣寫(1):
??????
public
?
static
?
void
?main(String?ss[])?
{
????????CtxUtil.getBaseManager().getHibernateTemplate().execute(
new
?HibernateCallback()?
{
????????????
public
?Object?doInHibernate(Session?session)?
throws
?HibernateException,?SQLException?
{
????????????????
//
?保存stu1
????????????????Student?stu1?
=
?
new
?Student();
????????????????stu1.setName(
"
aaaa
"
);
//
?在數(shù)據(jù)庫中,name字段不允許為null
????????????????session.save(stu1);
????????????????session.flush();//實際上,如果不是程序員"手癢"來調(diào)用這個flush(),HibernateTemplate中session的事務(wù)處理還是很方便的
????????????????Student?stu2?
=
?
new
?Student();
????????????????session.save(stu2);
//
?沒有設(shè)置name字段,預(yù)期會報出例外
????????????????session.flush();
????????????????
return
?
null
;
????????????}
????????}
);
????}
????? 你期望spring在執(zhí)行完execute回調(diào)后,在關(guān)閉session的時候提交事務(wù),想法是很好的,但spring并不會這么做.讓我們來看看在Hibernate的源代碼中,session.beginTransation()做了什么事。看如下代碼(2):
public
?Transaction?beginTransaction()?
throws
?HibernateException?
{
????????errorIfClosed();
????????
if
?(?rootSession?
!=
?
null
?)?
{
????????????
//
?todo?:?should?seriously?consider?not?allowing?a?txn?to?begin?from?a?child?session
????????????
//
??????can?always?route?the?request?to?the?root?session
????????????log.warn(?
"
Transaction?started?on?non-root?session
"
?);
????????}
????????Transaction?result?
=
?getTransaction();
????????result.begin();
????????
return
?result;
????}
這個方法中的result是一個org.hibernate.transaction.JDBCTransaction實例,而方法中的getTransaction()方法源代碼為(3):
public
?Transaction?getTransaction()?
throws
?HibernateException?
{
????????
if
?(hibernateTransaction
==
null
)?
{
????????????log.error(owner.getFactory().getSettings()
????????????????????.getTransactionFactory().getClass());
????????????hibernateTransaction?
=
?owner.getFactory().getSettings()
????????????????????.getTransactionFactory()
????????????????????.createTransaction(?
this
,?owner?);
????????}
????????
return
?hibernateTransaction;
????}
再次追蹤,owner.getFactory().getSettings()?.getTransactionFactory()的createTransaction()方法源代碼如下(4):
public
?Transaction?createTransaction(JDBCContext?jdbcContext,?Context?transactionContext)
????
throws
?HibernateException?
{
????????
return
?
new
?JDBCTransaction(?jdbcContext,?transactionContext?);
????}
它返回了一個JDBCTransaction,沒什么特別的。
在代碼2中,執(zhí)行了result.begin(),其實也就是JDBCTransaction實例的begin()方法,來看看(5):
public
?
void
?begin()?
throws
?HibernateException?
{
????????
if
?(begun)?
{
????????????
return
;
????????}
????????
if
?(commitFailed)?
{
????????????
throw
?
new
?TransactionException(
"
cannot?re-start?transaction?after?failed?commit
"
);
????????}
????????log.debug(
"
begin
"
);
????????
try
?
{
????????????toggleAutoCommit?
=
?jdbcContext.connection().getAutoCommit();
????????????
if
?(log.isDebugEnabled())?
{
????????????????log.debug(
"
current?autocommit?status:?
"
?
+
?toggleAutoCommit);
????????????}
????????????
if
?(toggleAutoCommit)?
{
????????????????log.debug(
"
disabling?autocommit
"
);
????????????????jdbcContext.connection().setAutoCommit(
false
);
//
把自動提交設(shè)為了false
????????????}
????????}
?
catch
?(SQLException?e)?
{
????????????log.error(
"
JDBC?begin?failed
"
,?e);
????????????
throw
?
new
?TransactionException(
"
JDBC?begin?failed:?
"
,?e);
????????}
????????callback?
=
?jdbcContext.registerCallbackIfNecessary();
????????begun?
=
?
true
;
????????committed?
=
?
false
;
????????rolledBack?
=
?
false
;
????????
if
?(timeout?
>
?
0
)?
{
????????????jdbcContext.getConnectionManager().getBatcher().setTransactionTimeout(timeout);
????????}
????????jdbcContext.afterTransactionBegin(
this
);
????}
在直接使用Hibernate時,要在事務(wù)結(jié)束的時候,寫上一句:tx.commit(),這個commit()的源碼為:
public
?
void
?commit()?
throws
?HibernateException?
{
????????
if
?(
!
begun)?
{
????????????
throw
?
new
?TransactionException(
"
Transaction?not?successfully?started
"
);
????????}
????????log.debug(
"
commit
"
);
????????
if
?(
!
transactionContext.isFlushModeNever()?
&&
?callback)?
{
????????????transactionContext.managedFlush();?
//
?if?an?exception?occurs?during
????????????
//
?flush,?user?must?call
????????????
//
?rollback()
????????}
????????notifyLocalSynchsBeforeTransactionCompletion();
????????
if
?(callback)?
{
????????????jdbcContext.beforeTransactionCompletion(
this
);
????????}
????????
try
?
{
????????????commitAndResetAutoCommit();//重點代碼,它的作用是提交事務(wù),并把connection的autocommit屬性恢復(fù)為true
????????????log.debug(
"
committed?JDBC?Connection
"
);
????????????committed?
=
?
true
;
????????????
if
?(callback)?
{
????????????????jdbcContext.afterTransactionCompletion(
true
,?
this
);
????????????}
????????????notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_COMMITTED);
????????}
?
catch
?(SQLException?e)?
{
????????????log.error(
"
JDBC?commit?failed
"
,?e);
????????????commitFailed?
=
?
true
;
????????????
if
?(callback)?
{
????????????????jdbcContext.afterTransactionCompletion(
false
,?
this
);
????????????}
????????????notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_UNKNOWN);
????????????
throw
?
new
?TransactionException(
"
JDBC?commit?failed
"
,?e);
????????}
?
finally
?
{
????????????closeIfRequired();
????????}
????}
上面代碼中,commitAndResetAutoCommit()方法的源碼如下:
private
?
void
?commitAndResetAutoCommit()?
throws
?SQLException?
{
????????
try
?
{
????????????jdbcContext.connection().commit();//這段不用說也能理解了
????????}
?
finally
?
{
????????????toggleAutoCommit();//這段的作用是恢復(fù)connection的autocommit屬性為true
????????}
????}
上述代碼的toggleAutoCommit()源代碼如下:
????
private
?
void
?toggleAutoCommit()?
{
????????
try
?
{
????????????
if
?(toggleAutoCommit)?
{
????????????????log.debug(
"
re-enabling?autocommit
"
);
????????????????jdbcContext.connection().setAutoCommit(
true
);//這行代碼的意義很明白了吧
????????????}
????????}
?
catch
?(Exception?sqle)?
{
????????????log.error(
"
Could?not?toggle?autocommit
"
,?sqle);
????????}
????}
????? 因此,如果你是直接使用hibernate,并手動管理它的session,并手動開啟事務(wù)關(guān)閉事務(wù)的話,完全可以保證你的事務(wù)(好像完全是廢話).
????? 但是,如果你用的是HibernateTemplate,如同源代碼1一樣,則不要指望spring在關(guān)閉session的時候為你提交事務(wù)(罪魁禍?zhǔn)拙褪窃诖a1中調(diào)用了session.flush())。因為在使用代碼1時,spring中得到session的方式如下:
Session?session?
=
?(entityInterceptor?
!=
?
null
?
?
?sessionFactory.openSession(entityInterceptor)?:?sessionFactory
????????????????.openSession());
簡單地說它就是得到了一個session,而沒有對connection的autocommit()作任何操作,spring管理范圍內(nèi)的session所持有的connection是autocommit=true的,spring借助這個屬性,在它關(guān)閉session時,提交數(shù)據(jù)庫事務(wù)。,因此如果你在源代碼1中加上一句話:
public
?
static
?
void
?main(String?ss[])?
{
????????CtxUtil.getBaseManager().getHibernateTemplate().execute(
new
?HibernateCallback()?
{
????????????
public
?Object?doInHibernate(Session?session)?
throws
?HibernateException,?SQLException?
{
????????????????log.info(session.connection().getAutoCommit());
//
打印一下事務(wù)提交方式
????????????????
//
?保存stu1
????????????????Student?stu1?
=
?
new
?Student();
????????????????stu1.setName(
"
aaaa
"
);
//
?在數(shù)據(jù)庫中,name字段不允許為null
????????????????session.save(stu1);
????????????????session.flush();
????????????????Student?stu2?
=
?
new
?Student();
????????????????session.save(stu2);
//
?沒有設(shè)置name字段,預(yù)期會報出例外
????????????????session.flush();
????????????????
return
?
null
;
????????????}
????????}
);
????}
???? 運(yùn)行后,它打出的結(jié)果是true,也就是說,雖然保存stu2時會報出例外,但如果commit屬性為true,則每一個到達(dá)數(shù)據(jù)庫的sql語句會立即被提交。換句話說,在調(diào)用完session.save(stu1)后,調(diào)用session.flush(),會發(fā)送sql語句到數(shù)據(jù)庫,再根據(jù)commit屬性為true,則保存stu1的操作已經(jīng)被持久到數(shù)據(jù)庫了,盡管后面的一條insert語句出了問題。
???? 因此,如果你想在HibernateCallback中使用session的事務(wù),需要如下寫:
public
?
static
?
void
?main(String?ss[])?
{
????????CtxUtil.getBaseManager().getHibernateTemplate().execute(
new
?HibernateCallback()?
{
????????????
public
?Object?doInHibernate(Session?session)?
throws
?HibernateException,?SQLException?
{
????????????????session.connection().setAutoCommit(
false
);
????????????????
//
保存stu1
????????????????Student?stu1
=
new
?Student();
????????????????stu1.setName(
"
aaaa
"
);
//
在數(shù)據(jù)庫中,name字段不允許為null
????????????????session.save(stu1);
????????????????session.flush();
????????????????
????????????????Student?stu2?
=
?
new
?Student();
????????????????session.save(stu2);
//
沒有設(shè)置name字段,預(yù)期會報出例外
?????????????????? session.flush();
????????????????session.connection().commit();
????????????????
//
至于session的關(guān)閉就不用我們操心了
????????????????
return
?
null
;
????????????}
????????}
);
????}
運(yùn)行上述代碼,沒問題了。至此,可能有些讀者早就對代碼1不滿意了:為什么每次save()以后要調(diào)用flush()?這是有原因的。下面我們來看看把session.flush()去掉后會出什么問題。改掉后的代碼如下:
public
?
static
?
void
?main(String?ss[])?
??????如果你這樣寫(1):
??????











































在代碼2中,執(zhí)行了result.begin(),其實也就是JDBCTransaction實例的begin()方法,來看看(5):
































在直接使用Hibernate時,要在事務(wù)結(jié)束的時候,寫上一句:tx.commit(),這個commit()的源碼為:






































上面代碼中,commitAndResetAutoCommit()方法的源碼如下:







上述代碼的toggleAutoCommit()源代碼如下:










????? 但是,如果你用的是HibernateTemplate,如同源代碼1一樣,則不要指望spring在關(guān)閉session的時候為你提交事務(wù)(罪魁禍?zhǔn)拙褪窃诖a1中調(diào)用了session.flush())。因為在使用代碼1時,spring中得到session的方式如下:




















???? 因此,如果你想在HibernateCallback中使用session的事務(wù),需要如下寫:












?????????????????? session.flush();








?
?
?(轉(zhuǎn)載
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
