锁定老帖子 主题:spring事务探索
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2005-03-06
要正确使用spring的事务,首先需要了解spring在事务设计上的一些概念 统观spring事务,围绕着两个核心PlatformTransactionManager和TransactionStatus PlatformTransactionManager直译过来就是平台相关事务,这里的平台指的是“事务源”,包括刚才我说的DataSource,jta等等。这些无一不是一个事务源。广义的说,凡是可以完成事务性操作的对象,都可以设计出相对应的PlatformTransactionManager,只要这个事务源支持commit,rollback和getTransaction语意。 查看spring代码,可以发现这些manager实现事务,就是调用事务源的事务操作方法 比如 HibernateTransactionManager protected void doCommit(DefaultTransactionStatus status); { HibernateTransactionObject txObject = (HibernateTransactionObject); status.getTransaction();; if (status.isDebug();); { logger.debug("Committing Hibernate transaction on session [" + txObject.getSessionHolder();.getSession(); + "]");; } try { txObject.getSessionHolder();.getTransaction();.commit();; } ... } jdbc 的DataSourceTransactionManager protected void doCommit(DefaultTransactionStatus status); { DataSourceTransactionObject txObject = (DataSourceTransactionObject); status.getTransaction();; Connection con = txObject.getConnectionHolder();.getConnection();; if (status.isDebug();); { logger.debug("Committing JDBC transaction on connection [" + con + "]");; } try { con.commit();; } ... } 那么PlatformTransactionManager以什么依据处理事务呢? 是TransactionStatus 查看api发现这个接口有三个方法 isNewTransaction() ,isRollbackOnly(),setRollbackOnly() PlatformTransactionManager就是根据前两个方法决定是否要创建一个新事务,是要递交还是回滚。至于第三个方法是改变事务当前状态的,很多地方都要用到,偏偏PlatformTransactionManager自身好像不怎么用,毕竟事务状态的改变是由程序员代码决定的,不需要一个manager多管闲事。 总结上面所说的,spring的事务由PlatformTransactionManager管理,manager最后调用事务源的方法来实现一个事务过程。而manager通过TransactionStatus 来决定如何实现。 接下去说spring事务中的TransactionTemplate和TransactionInterceptor TransactionTemplate其实和spring中其他的template的作用类似,起到化简代码的作用,不要被它那么长的名字吓倒了,事实上这个template并不是什么非常核心的对象。如果比较学究派的,可以去看看template设计模式,在此就不再对此赘述了。 为什么要有TransactionTemplate?先来看看如果没有TransactionTemplate,我们的代码该怎么写 先来看看spring reference中的一段代码 DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);; TransactionStatus status = transactionManager.getTransaction(def);; try { // execute your business logic here } catch (MyException ex); { transactionManager.rollback(status);; throw ex; } transactionManager.commit(status);; 这是直接使用transactionManager的例子,可以看到真正执行business logic 的地方是在try当中那段,前后的代码都是为了完成事务管理的。如果每个business logic都要写上那么一段,我肯定是疯了。我们翻出TransactionTemplate的代码看看他怎么化简了我们的代码 public Object execute(TransactionCallback action); throws TransactionException { TransactionStatus status = this.transactionManager.getTransaction(this);; Object result = null; try { result = action.doInTransaction(status);; } catch (RuntimeException ex); { // transactional code threw application exception -> rollback rollbackOnException(status, ex);; throw ex; } catch (Error err); { // transactional code threw error -> rollback rollbackOnException(status, err);; throw err; } this.transactionManager.commit(status);; return result; } 同上面的代码如出一辙,前后是事务处理代码,当中那段result = action.doInTransaction(status);是我们的应用代码。至于action是什么,全看各位的需要了。但是有一点要主要,如果利用TransactionTemplate,那么他不管你扔出什么异常都会回滚事务,但是回滚的是哪个事务呢?继续挖代码 private void rollbackOnException(TransactionStatus status, Throwable ex); throws TransactionException { if (logger.isDebugEnabled();); { logger.debug("Initiating transaction rollback on application exception", ex);; } try { this.transactionManager.rollback(status);; } catch (RuntimeException ex2); { logger.error("Application exception overridden by rollback exception", ex);; throw ex2; } catch (Error err); { logger.error("Application exception overridden by rollback error", ex);; throw err; } } 真相大白,是对template所持有的某个transactionManager进行回滚。所以如果你的应用代码用的是事务源a的一些资源,比如到服务器a的一个datasource,但是你的transactionManager管理的是另一些资源,比如服务器b的一个datasource,代码铁定不会正常运行 特别是在一些多事务源的程序里,这点千万不能搞错。如果多个事务源之间要完成全局事务,还是老老实实用分布式事务管理服务吧(jta) 那么TransactionInterceptor是干什么的?这个是spring 的声明式事务的支持方式。因为用TransactionTemplate要硬编码,而且调整事务策略很麻烦(不是说不能调。举个例子原来程序抛出异常A需要回滚,现在不需要要,我就可以把a catch吃掉。这时候template就不会回滚了。但是每次调整都要重写编码。)而用TransactionInterceptor就可以将这些调整写在配置中。我们再来挖TransactionInterceptor的代码 public Object invoke(MethodInvocation invocation); throws Throwable { // Work out the target class: may be null. // The TransactionAttributeSource should be passed the target class // as well as the method, which may be from an interface Class targetClass = (invocation.getThis(); != null); ? invocation.getThis();.getClass(); : null; // Create transaction if necessary TransactionInfo txInfo = createTransactionIfNecessary(invocation.getMethod();, targetClass);; Object retVal = null; try { // This is an around advice. // Invoke the next interceptor in the chain. // This will normally result in a target object being invoked. retVal = invocation.proceed();; } catch (Throwable ex); { // target invocation exception doCloseTransactionAfterThrowing(txInfo, ex);; throw ex; } finally { doFinally(txInfo);; } doCommitTransactionAfterReturning(txInfo);; return retVal; } 万变不离其宗。 所以使用spring的事务管理需要作这些事 1,设置好事务源,比如DataSource,hibernate的session。如果有多个事务源要考虑他们之间是否有全局事务,如果有,老老实实用jta,否则就需要自己写一个manager了 2,设置manager,根据你的事务源选择对应的PlatformTransactionManager 3,选择实现事物的方式,用template还是interceptor。用template代码直观点,但是template所管辖的manager和你应用代码所用的事务源要一致。如果用interceptor千万注意,一定要调用interceptor那个bean,而不是原始的那个target。在坛子上我已经看到至少有两个朋友说spring事物不起作用,从配置和代码上看都正确,这时要好好查查,调用的bean是哪一个。 4,这个是设计问题了,推荐事务处于一个较高层次,比如service上的某个函数,而底层的dao可以不考虑事务,否则可能会出现事务嵌套,增加程序复杂度。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2005-03-09
好文好文,分析得比较全面透彻
对于第四点有个想法,事务做到service这一层了,覆盖的范围就很广了,时间也长,但是实际上,并不是每个操作都能回滚的,只有那些有事务性的操作可以回滚。 最简单的说,在事务中,发了email,修改了硬盘文件,那么这些操作都是不可以回滚的。 而即使是修改数据库的操作,如果数据库不支持事务或者由于代码写得不支持事务,这部分的代码也是不能回滚的,那么也不会回滚。 这个看起来有点废话,想说明的只是,事务不是什么神奇的东西,不要误会一回滚,什么东西都回复原来的现状了。包括工作流那些东东,不要以为是理所当然的,要记住,只有支持事务的操作,才是可以回滚的。 |
|
返回顶楼 | |
发表时间:2005-03-22
楼上的,对于你的这句话我没有很好的理解,能否举例说明:
如果用interceptor千万注意,一定要调用interceptor那个bean,而不是原始的那个target。在坛子上我已经看到至少有两个朋友说spring事物不起作用,从配置和代码上看都正确,这时要好好查查,调用的bean是哪一个。 谢谢!!! |
|
返回顶楼 | |
发表时间:2005-03-22
如果想用自己的jdbc封装类,而不用JDBCTemplate,怎么配置事务哪?
我的类不是线程安全的,而JDBCTemplate是线程安全的. 但是我觉得JDBCTemplate不好用,而且也不想用. 谁有经验? TransactionProxyFactoryBean是单态的...如果有一个不是单态的也许可以满足我的要求了... |
|
返回顶楼 | |
发表时间:2005-09-14
看过之后受益非浅呀,支持!
|
|
返回顶楼 | |
发表时间:2005-12-14
写得太好了,顶一下!!~!~可否在深入写其他的,期待阿。
|
|
返回顶楼 | |
发表时间:2006-01-18
lyz0726 写道 楼上的,对于你的这句话我没有很好的理解,能否举例说明:
如果用interceptor千万注意,一定要调用interceptor那个bean,而不是原始的那个target。在坛子上我已经看到至少有两个朋友说spring事物不起作用,从配置和代码上看都正确,这时要好好查查,调用的bean是哪一个。 谢谢!!! 比如有两个bean; <bean id="clinic" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="target" ref="essoImpl"/> .... </bean> <bean id="essoImpl" class="com.esso.domain.logic.ESSOImpl" /> 应该得调用 clinic,而不是essoImpl。 我曾经被这个问题搞得苦闷之际,害我连晚饭都没吃成。 |
|
返回顶楼 | |
发表时间:2006-01-19
在Yan里面,这个问题不是问题:
<props id="txprops"> <entry key="insert*" val="PROPAGATION_REQUIRED"/> <entry key="update*" val="PROPAGATION_REQUIRED"/> <entry key="*" val="PROPAGATION_REQUIRED,readOnly"/> </props> <bean id="clinic" class="com.esso.domain.logic.ESSOImpl" transactionAttributes="$txprops"/> 或者用"hide"把私有bean藏起来: <module name="test" hide="*Impl"> <body> <bean id="clinic" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" props="{target=$essoImpl}"> .... </bean> <bean id="essoImpl" class="com.esso.domain.logic.ESSOImpl" /> </body> </module> |
|
返回顶楼 | |
发表时间:2006-01-19
scud 写道 如果想用自己的jdbc封装类,而不用JDBCTemplate,怎么配置事务哪?
我的类不是线程安全的,而JDBCTemplate是线程安全的. 但是我觉得JDBCTemplate不好用,而且也不想用. 谁有经验? TransactionProxyFactoryBean是单态的...如果有一个不是单态的也许可以满足我的要求了... 好像你修改一下org.springframework.jdbc.core.support.jdbcDaoSupport或者继承这个类就可以了吧。 与JdbcTemplate没什么关系吧! JdbcTemplate我用了一下还是不错的阿。 我没有运行代码试过。只是从代码级别看了一下。 |
|
返回顶楼 | |
发表时间:2006-03-30
看到这一段
TransactionInterceptor : finally { doFinally(txInfo); } doCommitTransactionAfterReturning(txInfo); ------------------ 如果一个方法,配置的事务为REQUIRED, 呢是不是意味着 1) 如果调用这个方法的时候没有事务,这个方法会自动启动一个事务。 2)这个方法结束的时候会将事务commit对么。 我想问的关键是第二点,也就是一个方法启动的事务,是不是在这个方法结束的时候,自动结束这个事务? 如果不是它自己启动的事务,就不会自己结束这个事务,如果没有异常发生的话。 |
|
返回顶楼 | |