HibernateTemplate中HibernateCallback的事务

系统 1940 0

HibernateTemplate中HibernateCallback的事务

目的:使用HibernateTemplate执行execute(new HibernateCallback())方法,从HibernateCallback中得到session,在此session中做多个操作,并希望这些操作位于同一个事务中。
      如果你这样写(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 " ); //  在数据库中,name字段不允许为null
                session.save(stu1);
                session.flush();//实际上,如果不是程序员"手痒"来调用这个flush(),HibernateTemplate中session的事务处理还是很方便的

                Student stu2 
=   new  Student();
                session.save(stu2);
//  没有设置name字段,预期会报出例外
                session.flush();
                
return   null ;
            }

        }
);

    }
      你期望spring在执行完execute回调后,在关闭session的时候提交事务,想法是很好的,但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中,执行了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 ); // 把自动提交设为了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时,要在事务结束的时候,写上一句: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();//重点代码,它的作用是提交事务,并把connection的autocommit属性恢复为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();//这段的作用是恢复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,并手动开启事务关闭事务的话,完全可以保证你的事务(好像完全是废话).
      但是,如果你用的是HibernateTemplate,如同源代码1一样,则不要指望spring在关闭session的时候为你提交事务(罪魁祸首就是在代码1中调用了session.flush())。因为在使用代码1时,spring中得到session的方式如下:
Session session  =  (entityInterceptor  !=   null   ?  sessionFactory.openSession(entityInterceptor) : sessionFactory
                .openSession());
简单地说它就是得到了一个session,而没有对connection的autocommit()作任何操作,spring管理范围内的session所持有的connection是autocommit=true的,spring借助这个属性,在它关闭session时,提交数据库事务。,因此如果你在源代码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());
// 打印一下事务提交方式
                
//  保存stu1
                Student stu1  =   new  Student();
                stu1.setName(
" aaaa " ); //  在数据库中,name字段不允许为null
                session.save(stu1);
                session.flush();

                Student stu2 
=   new  Student();
                session.save(stu2);
//  没有设置name字段,预期会报出例外
                session.flush();
                
return   null ;
            }

        }
);

    }
     运行后,它打出的结果是true,也就是说,虽然保存stu2时会报出例外,但如果commit属性为true,则每一个到达数据库的sql语句会立即被提交。换句话说,在调用完session.save(stu1)后,调用session.flush(),会发送sql语句到数据库,再根据commit属性为true,则保存stu1的操作已经被持久到数据库了,尽管后面的一条insert语句出了问题。
     因此,如果你想在HibernateCallback中使用session的事务,需要如下写:
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 " ); // 在数据库中,name字段不允许为null
                session.save(stu1);
                session.flush();
                
                Student stu2 
=   new  Student();
                session.save(stu2);
// 没有设置name字段,预期会报出例外
                   session.flush();
                session.connection().commit();
                
// 至于session的关闭就不用我们操心了
                 return   null ;
            }

        }
);

    }
运行上述代码,没问题了。至此,可能有些读者早就对代码1不满意了:为什么每次save()以后要调用flush()?这是有原因的。下面我们来看看把session.flush()去掉后会出什么问题。改掉后的代码如下:
public   static   void  main(String ss[]) 

 

 

 (转载

HibernateTemplate中HibernateCallback的事务


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论