在配置Hibernat的事务传播特性比较常用就是REQUIRED,read-only,REQUIRES_NEW,示例代码如下:
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="get*" read-only="true" /> <tx:method name="query*" read-only="true" /> <tx:method name="find*" read-only="true" /> <tx:method name="load*" read-only="true" /> <tx:method name="*REQUIRES_NEW" propagation="REQUIRES_NEW"/> <tx:method name="*" propagation="REQUIRED" /> </tx:attributes> </tx:advice>
1、假设有UserService中有代码如下:
public void save(User user){ String code =null; try { code=codeGenerateService.generateUserCode_REQUIRES_NEW(); } catch (Exception e) { LOG.error(e.getMessage(),e); } user.setUserCode(code); baseDao.save(user); }
在执行过程中后台打印的日志如下:
2015-11-11 10:17:03:DEBUG main org.springframework.orm.hibernate3.HibernateTransactionManager - Creating new transaction with name [com.lyl.system.service.UserService.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2015-11-11 10:17:03:DEBUG main org.hibernate.impl.SessionImpl - opened session at timestamp: 5927941831421952
org.springframework.orm.hibernate3.HibernateTransactionManager - Opened new Session [org.hibernate.impl.SessionImpl@86d1da] for Hibernate transaction
org.springframework.orm.hibernate3.HibernateTransactionManager - Preparing JDBC Connection of Hibernate Session [org.hibernate.impl.SessionImpl@86d1da]
org.hibernate.transaction.JDBCTransaction - begin
org.hibernate.jdbc.ConnectionManager - opening JDBC connection
org.hibernate.transaction.JDBCTransaction - current autocommit status: true
org.hibernate.transaction.JDBCTransaction - disabling autocommit
org.springframework.orm.hibernate3.HibernateTransactionManager - Exposing Hibernate transaction as JDBC transaction [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8, UserName=root@localhost, MySQL Connector Java]
org.springframework.orm.hibernate3.HibernateTransactionManager - Found thread-bound Session [org.hibernate.impl.SessionImpl@86d1da] for Hibernate transaction
org.springframework.orm.hibernate3.HibernateTransactionManager - Suspending current transaction, creating new transaction with name [com.lyl.system.service.CodeGenerateService.generateUserCode_REQUIRES_NEW]
org.hibernate.impl.SessionImpl - opened session at timestamp: 5927941831806976
org.springframework.orm.hibernate3.HibernateTransactionManager - Opened new Session [org.hibernate.impl.SessionImpl@19977bd] for Hibernate transaction
org.springframework.orm.hibernate3.HibernateTransactionManager - Preparing JDBC Connection of Hibernate Session [org.hibernate.impl.SessionImpl@19977bd]
org.hibernate.transaction.JDBCTransaction - begin
org.hibernate.jdbc.ConnectionManager - opening JDBC connection
net.sf.ehcache.CacheManager@6691da net.sf.ehcache.util.UpdateChecker - Checking for update...
org.hibernate.transaction.JDBCTransaction - current autocommit status: true
org.hibernate.transaction.JDBCTransaction - disabling autocommit
org.springframework.orm.hibernate3.HibernateTransactionManager - Exposing Hibernate transaction as JDBC transaction [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8, UserName=root@localhost, MySQL Connector Java]
org.springframework.orm.hibernate3.HibernateTemplate - Found thread-bound Session for HibernateTemplate
org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
org.hibernate.SQL -
select
codegenera0_.codeType as codeType0_,
codegenera0_.value as value0_
from
demo1_code_generate codegenera0_
where
codegenera0_.codeType=?
Hibernate:
select
codegenera0_.codeType as codeType0_,
codegenera0_.value as value0_
from
demo1_code_generate codegenera0_
where
codegenera0_.codeType=?
org.hibernate.jdbc.AbstractBatcher - about to open ResultSet (open ResultSets: 0, globally: 0)
org.hibernate.loader.Loader - result row: EntityKey[com.lyl.system.entity.CodeGenerate#USER_CODE]
org.hibernate.jdbc.AbstractBatcher - about to close ResultSet (open ResultSets: 1, globally: 1)
org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
org.hibernate.engine.TwoPhaseLoad - resolving associations for [com.lyl.system.entity.CodeGenerate#USER_CODE]
org.hibernate.engine.TwoPhaseLoad - done materializing entity [com.lyl.system.entity.CodeGenerate#USER_CODE]
org.hibernate.engine.StatefulPersistenceContext - initializing non-lazy collections
org.springframework.orm.hibernate3.HibernateTemplate - Not closing pre-bound Hibernate Session after HibernateTemplate
org.springframework.orm.hibernate3.HibernateTemplate - Found thread-bound Session for HibernateTemplate
org.springframework.orm.hibernate3.HibernateTemplate - Not closing pre-bound Hibernate Session after HibernateTemplate
org.springframework.orm.hibernate3.HibernateTransactionManager - Initiating transaction commit
org.springframework.orm.hibernate3.HibernateTransactionManager - Committing Hibernate transaction on Session [org.hibernate.impl.SessionImpl@19977bd]
org.hibernate.transaction.JDBCTransaction - commit
org.hibernate.event.def.AbstractFlushingEventListener - processing flush-time cascades
org.hibernate.event.def.AbstractFlushingEventListener - dirty checking collections
org.hibernate.event.def.AbstractFlushingEventListener - Flushed: 0 insertions, 1 updates, 0 deletions to 1 objects
org.hibernate.event.def.AbstractFlushingEventListener - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
org.hibernate.pretty.Printer - listing entities:
org.hibernate.pretty.Printer - com.lyl.system.entity.CodeGenerate{value=8, codeType=USER_CODE}
org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
org.hibernate.SQL -
update
demo1_code_generate
set
value=?
where
codeType=?
Hibernate:
update
demo1_code_generate
set
value=?
where
codeType=?
org.hibernate.cache.UpdateTimestampsCache - Pre-invalidating space [demo1_code_generate]
org.hibernate.jdbc.AbstractBatcher - Executing batch size: 1
org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
org.hibernate.transaction.JDBCTransaction - re-enabling autocommit
org.hibernate.transaction.JDBCTransaction - committed JDBC Connection
org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
org.hibernate.cache.UpdateTimestampsCache - Invalidating space [demo1_code_generate], timestamp: 5927941833535488
org.springframework.orm.hibernate3.HibernateTransactionManager - Closing Hibernate Session [org.hibernate.impl.SessionImpl@19977bd] after transaction
org.springframework.orm.hibernate3.SessionFactoryUtils - Closing Hibernate Session
org.hibernate.jdbc.ConnectionManager - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
org.springframework.orm.hibernate3.HibernateTransactionManager - Resuming suspended transaction after completion of inner transaction
org.springframework.orm.hibernate3.HibernateTemplate - Found thread-bound Session for HibernateTemplate
org.hibernate.event.def.AbstractSaveEventListener - generated identifier: 402883e750f6e82e0150f6e833cb0000, using strategy: org.hibernate.id.UUIDHexGenerator
org.springframework.orm.hibernate3.HibernateTemplate - Not closing pre-bound Hibernate Session after HibernateTemplate
org.springframework.orm.hibernate3.HibernateTransactionManager - Initiating transaction commit
org.springframework.orm.hibernate3.HibernateTransactionManager - Committing Hibernate transaction on Session [org.hibernate.impl.SessionImpl@86d1da]
org.hibernate.transaction.JDBCTransaction - commit
org.hibernate.event.def.AbstractFlushingEventListener - processing flush-time cascades
org.hibernate.event.def.AbstractFlushingEventListener - dirty checking collections
org.hibernate.event.def.AbstractFlushingEventListener - Flushed: 1 insertions, 0 updates, 0 deletions to 1 objects
org.hibernate.event.def.AbstractFlushingEventListener - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
org.hibernate.pretty.Printer - listing entities:
org.hibernate.pretty.Printer - com.lyl.system.entity.User{sex=null, userCode=000008, password=m123, updateDate=Wed Nov 11 22:17:04 CST 2015, tephone=13145453211, id=402883e750f6e82e0150f6e833cb0000, createUser=1, enableFlag=false, address=上海市松江区, [email protected], userName=zhangsan, createDate=Wed Nov 11 22:17:04 CST 2015, updateUser=1}
org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
org.hibernate.SQL -
insert
into
demo1_user
(createDate, createUser, enableFlag, updateDate, updateUser, address, email, password, sex, tephone, userCode, userName, id)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate:
insert
into
demo1_user
(createDate, createUser, enableFlag, updateDate, updateUser, address, email, password, sex, tephone, userCode, userName, id)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
org.hibernate.cache.UpdateTimestampsCache - Pre-invalidating space [demo1_user]
org.hibernate.jdbc.AbstractBatcher - Executing batch size: 1
org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
org.hibernate.transaction.JDBCTransaction - re-enabling autocommit
org.hibernate.transaction.JDBCTransaction - committed JDBC Connection
org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
org.hibernate.cache.UpdateTimestampsCache - Invalidating space [demo1_user], timestamp: 5927941833596929
org.springframework.orm.hibernate3.HibernateTransactionManager - Closing Hibernate Session [org.hibernate.impl.SessionImpl@86d1da] after transaction
org.springframework.orm.hibernate3.SessionFactoryUtils - Closing Hibernate Session
org.hibernate.jdbc.ConnectionManager - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
有上面日志可以看出,首先从spring bean工厂里面取得单例userService,进入save方法后开启事务,并打开session ,在执行到调用方法codeGenerateService.generateUserCode_REQUIRES_NEW()获取用户代码时,在codeGenerateService的方法generateUserCode_REQUIRES_NEW()时根据配置,方法内又开启了一个新事务,当generateUserCode_REQUIRES_NEW()调用完成后,将新开启的事务就提交了,因此我们可以通过try catch捕获执行运行时异常(包括数据库执行异常),如果不捕获发生的异常,导致UserService的save方法事务回滚,保存失败。
假设我们有UserService方法中,同样声明了一个生成用户代码的方法如下:
public String generateUserCode_REQUIRES_NEW(){ Map<String, Object> paramMap = new HashMap<String, Object>(); paramMap.put("codeType", SystemEnum.CODE_TYPE.USER_CODE); List<CodeGenerate> codeList = baseDao.findByHQL( "from CodeGenerate where codeType=:codeType", paramMap); CodeGenerate cg = codeList.get(0); String value = cg.getValue(); Integer i = Integer.valueOf(value); i = i + 1; cg.setValue(i.toString()); baseDao.update(cg); Double d = new Double(Math.pow(10, 6)); int a = d.intValue() + i; String str = new Integer(a).toString(); str = str.substring(1); return str; }
在UserService的调用本类声明的方法generateUserCode_REQUIRES_NEW()会怎么样呢?看下面的日志就知道了。
2015-11-11 10:34:36:DEBUG main org.springframework.orm.hibernate3.HibernateTransactionManager - Creating new transaction with name [com.lyl.system.service.UserService.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2015-11-11 10:34:36:DEBUG main org.hibernate.impl.SessionImpl - opened session at timestamp: 5927946143420416
2015-11-11 10:34:36:DEBUG main org.springframework.orm.hibernate3.HibernateTransactionManager - Opened new Session [org.hibernate.impl.SessionImpl@86d1da] for Hibernate transaction
2015-11-11 10:34:36:DEBUG main org.springframework.orm.hibernate3.HibernateTransactionManager - Preparing JDBC Connection of Hibernate Session [org.hibernate.impl.SessionImpl@86d1da]
2015-11-11 10:34:36:DEBUG net.sf.ehcache.CacheManager@6691da net.sf.ehcache.util.UpdateChecker - Checking for update...
2015-11-11 10:34:36:DEBUG main org.hibernate.transaction.JDBCTransaction - begin
2015-11-11 10:34:36:DEBUG main org.hibernate.jdbc.ConnectionManager - opening JDBC connection
2015-11-11 10:34:36:DEBUG main org.hibernate.transaction.JDBCTransaction - current autocommit status: true
2015-11-11 10:34:36:DEBUG main org.hibernate.transaction.JDBCTransaction - disabling autocommit
2015-11-11 10:34:36:DEBUG main org.springframework.orm.hibernate3.HibernateTransactionManager - Exposing Hibernate transaction as JDBC transaction [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8, UserName=root@localhost, MySQL Connector Java]
2015-11-11 10:34:36:DEBUG main org.springframework.orm.hibernate3.HibernateTemplate - Found thread-bound Session for HibernateTemplate
2015-11-11 10:34:36:DEBUG main org.hibernate.jdbc.AbstractBatcher - about to open ResultSet (open ResultSets: 0, globally: 0)
2015-11-11 10:34:36:DEBUG main org.hibernate.loader.Loader - result row: EntityKey[com.lyl.system.entity.CodeGenerate#USER_CODE]
2015-11-11 10:34:36:DEBUG main org.hibernate.jdbc.AbstractBatcher - about to close ResultSet (open ResultSets: 1, globally: 1)
2015-11-11 10:34:36:DEBUG main org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
2015-11-11 10:34:36:DEBUG main org.hibernate.engine.TwoPhaseLoad - resolving associations for [com.lyl.system.entity.CodeGenerate#USER_CODE]
2015-11-11 10:34:36:DEBUG main org.hibernate.engine.TwoPhaseLoad - done materializing entity [com.lyl.system.entity.CodeGenerate#USER_CODE]
2015-11-11 10:34:36:DEBUG main org.hibernate.engine.StatefulPersistenceContext - initializing non-lazy collections
2015-11-11 10:34:36:DEBUG main org.springframework.orm.hibernate3.HibernateTemplate - Not closing pre-bound Hibernate Session after HibernateTemplate
2015-11-11 10:34:36:DEBUG main org.springframework.orm.hibernate3.HibernateTemplate - Found thread-bound Session for HibernateTemplate
2015-11-11 10:34:36:DEBUG main org.springframework.orm.hibernate3.HibernateTemplate - Not closing pre-bound Hibernate Session after HibernateTemplate
2015-11-11 10:34:36:DEBUG main org.springframework.orm.hibernate3.HibernateTemplate - Found thread-bound Session for HibernateTemplate
2015-11-11 10:34:36:DEBUG main org.hibernate.event.def.AbstractSaveEventListener - generated identifier: 402883e750f6f83e0150f6f8438c0000, using strategy: org.hibernate.id.UUIDHexGenerator
2015-11-11 10:34:36:DEBUG main org.springframework.orm.hibernate3.HibernateTemplate - Not closing pre-bound Hibernate Session after HibernateTemplate
2015-11-11 10:34:36:DEBUG main org.springframework.orm.hibernate3.HibernateTransactionManager - Initiating transaction commit
2015-11-11 10:34:36:DEBUG main org.springframework.orm.hibernate3.HibernateTransactionManager - Committing Hibernate transaction on Session [org.hibernate.impl.SessionImpl@86d1da]
2015-11-11 10:34:36:DEBUG main org.hibernate.transaction.JDBCTransaction - commit
2015-11-11 10:34:36:DEBUG main org.hibernate.event.def.AbstractFlushingEventListener - processing flush-time cascades
2015-11-11 10:34:36:DEBUG main org.hibernate.event.def.AbstractFlushingEventListener - dirty checking collections
2015-11-11 10:34:36:DEBUG main org.hibernate.event.def.AbstractFlushingEventListener - Flushed: 1 insertions, 1 updates, 0 deletions to 2 objects
2015-11-11 10:34:36:DEBUG main org.hibernate.event.def.AbstractFlushingEventListener - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
2015-11-11 10:34:36:DEBUG main org.hibernate.pretty.Printer - listing entities:
2015-11-11 10:34:36:DEBUG main org.hibernate.pretty.Printer - com.lyl.system.entity.User{sex=null, userCode=000010, password=m123, updateDate=Wed Nov 11 22:34:36 CST 2015, tephone=13145453211, id=402883e750f6f83e0150f6f8438c0000, createUser=1, enableFlag=false, address=上海市松江区, [email protected], userName=zhangsan, createDate=Wed Nov 11 22:34:36 CST 2015, updateUser=1}
2015-11-11 10:34:36:DEBUG main org.hibernate.pretty.Printer - com.lyl.system.entity.CodeGenerate{value=10, codeType=USER_CODE}
2015-11-11 10:34:36:DEBUG main org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
2015-11-11 10:34:36:DEBUG main org.hibernate.SQL -
Hibernate:
insert
into
demo1_user
(createDate, createUser, enableFlag, updateDate, updateUser, address, email, password, sex, tephone, userCode, userName, id)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2015-11-11 10:34:36:DEBUG main org.hibernate.cache.UpdateTimestampsCache - Pre-invalidating space [demo1_user]
2015-11-11 10:34:36:DEBUG main org.hibernate.jdbc.AbstractBatcher - Executing batch size: 1
2015-11-11 10:34:36:DEBUG main org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
2015-11-11 10:34:36:DEBUG main org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
2015-11-11 10:34:36:DEBUG main org.hibernate.SQL -
Hibernate:
update
demo1_code_generate
set
value=?
where
codeType=?
2015-11-11 10:34:36:DEBUG main org.hibernate.cache.UpdateTimestampsCache - Pre-invalidating space [demo1_code_generate]
2015-11-11 10:34:36:DEBUG main org.hibernate.jdbc.AbstractBatcher - Executing batch size: 1
2015-11-11 10:34:36:DEBUG main org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
2015-11-11 10:34:36:DEBUG main org.hibernate.transaction.JDBCTransaction - re-enabling autocommit
2015-11-11 10:34:36:DEBUG main org.hibernate.transaction.JDBCTransaction - committed JDBC Connection
2015-11-11 10:34:36:DEBUG main org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
2015-11-11 10:34:36:DEBUG main org.hibernate.cache.UpdateTimestampsCache - Invalidating space [demo1_code_generate], timestamp: 5927946145214465
2015-11-11 10:34:36:DEBUG main org.hibernate.cache.UpdateTimestampsCache - Invalidating space [demo1_user], timestamp: 5927946145214465
2015-11-11 10:34:36:DEBUG main org.springframework.orm.hibernate3.HibernateTransactionManager - Closing Hibernate Session [org.hibernate.impl.SessionImpl@86d1da] after transaction
2015-11-11 10:34:36:DEBUG main org.springframework.orm.hibernate3.SessionFactoryUtils - Closing Hibernate Session
2015-11-11 10:34:36:DEBUG main org.hibernate.jdbc.ConnectionManager - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
2015-11-11 10:34:36:DEBUG main org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
UserService调用用本类中的EQUIRES_NEW结尾的方法并没有 开启事务,也就是说时它们属于同一个事务,而且执行是顺序我我们注意到打印save中的insert语句,再打印generateUserCode_REQUIRES_NEW()方法的update语句,说明调用方法比被调方法中的(影响数据)语句先执行。
总结:当我们需要保证执行某个操作时单独事务的时候,需要注意两点:
1、注意方法名声明为事务传播特性为REQUIRES_NEW的方法。
2、方法不要在本类中调用。
另外,为捕获数据库执行异常,必须是在事务配置的外层才行。