原文地址:http://chouyi.iteye.com/blog/1675199

Spring对事务的解决办法其实分为2种:编程式实现事务,AOP配置声明式解决方案。
http://jinnianshilongnian.iteye.com/blog/1496953

Spring提供了许多内置事务管理器实现,常用的有以下几种:

  • DataSourceTransactionManager:位于org.springframework.jdbc.datasource包中,数据源事务管理器,提供对单个javax.sql.DataSource事务管理,用于Spring JDBC抽象框架、iBATIS框架的事务管理;
  • HibernateTransactionManager:位
    于org.springframework.orm.hibernate3或者hibernate4包中,提供对单个
    org.hibernate.SessionFactory事务支持,用于集成Hibernate框架时的事务管理;该事务管理器只支持
    Hibernate3+版本,且Spring3.0+版本只支持Hibernate 3.2+版本;
  • JtaTransactionManager:位于org.springframework.transaction.jta包中,提供对分布式事务管理的支持,并将事务管理委托给Java EE应用服务器事务管理器;

Spring不仅提供这些事务管理器,还提供对如JMS事务管理的管理器

两个不依赖于应用服务器的开源JTA事务实现组件:JOTM和Atomikos Transactions Essentials

具体用法参考http://jinnianshilongnian.iteye.com/blog/1439900

这篇博客讲解了对于JDBC和JTA分别用PlatformTransactionManager实现和使用TransactionTemplate实现编程式事务管理:http://jinnianshilongnian.iteye.com/blog/1441271

编程式实现事务

Spring提供两种编程式事务支持:直接使用PlatformTransactionManager实现和使用TransactionTemplate模板类,用于支持逻辑事务管理。

如果采用编程式事务推荐使用TransactionTemplate模板类。

Spring框架支持事务管理的核心是事务管理器抽象,对于不同的数据访问框架(如Hibernate)通过实现策略接口
PlatformTransactionManager,从而能支持各种数据访问框架的事务管理,PlatformTransactionManager
接口定义如下:

  1. public interface PlatformTransactionManager {
  2. //返回一个已经激活的事务或创建一个新的事务
  3. TransactionStatus getTransaction(TransactionDefinition definition)
  4. throws TransactionException;
  5. void commit(TransactionStatus status) throws TransactionException;
  6. void rollback(TransactionStatus status) throws TransactionException;
  7. }

关于TransactionDefinition接口和TransactionStatus接口:

http://jinnianshilongnian.iteye.com/blog/1439900

Spring声明式事务

在日常开发中,用的最多的就是声明式事务了,下面将介绍SpringJdbc的声明式事务的配置方法:

  1. <context:component-scan base-package="com.chou.spring.jdbc"/>
  2. <!-- 配置数据源 -->
  3. <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
  4. <!-- Connection Info -->
  5. <property name="driverClass" value="${db.driverClass}" />
  6. <property name="jdbcUrl" value="${db.url}" />
  7. <property name="user" value="${db.username}" />
  8. <property name="password" value="${db.password}" />
  9. <!-- Connection Pooling Info -->
  10. <property name="initialPoolSize" value="1" />
  11. <property name="minPoolSize" value="1" />
  12. <property name="maxPoolSize" value="15" />
  13. <property name="maxIdleTime" value="1800" />
  14. <property name="maxStatements" value="0" />
  15. </bean>
  16. <bean id="jdbcTemplateDao" class="com.chou.spring.jdbc.dao.JdbcTemplateDao" >
  17. <property name="dataSource" ref="dataSource" />
  18. </bean>
  19. <!-- JDBC事务管理器 -->
  20. <bean id="jdbctTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  21. <property name="dataSource" ref="dataSource" />
  22. </bean>
  23. <tx:advice id="txAdvice" transaction-manager="jdbctTxManager">
  24. <tx:attributes>
  25. <tx:method name="domain*"/>
  26. </tx:attributes>
  27. </tx:advice>
  28. <aop:config proxy-target-class="true">
  29. <aop:advisor advice-ref="txAdvice"
  30. pointcut="execution(* com.chou.spring.jdbc.service.JdbcTemplateService.*(..))"/>
  31. </aop:config>
  1. public class JdbcTemplateDao extends JdbcDaoSupport{
  2. public void save() {
  3. String insertSql = "insert into tab_item values(?,?,?)";
  4. Assert.isTrue(getJdbcTemplate().update(insertSql, new Object[]{6, "HP", "PT540"}) == 1, "插入失败");
  5. }
  6. public void delete() {
  7. String deleteSql = "delete tab_item where id = ?";
  8. Assert.isTrue(getJdbcTemplate().update(deleteSql, new Object[]{6}) == 1, "删除失败");
  9. }
  10. public void update() {
  11. String updateSql = "update tab_item set itemno = ?, itemname = ? where id = ?";
  12. Assert.isTrue(getJdbcTemplate().update(updateSql, new Object[]{"HP", "PT555", 6}) == 1, "修改失败");
  13. }
  14. }
  1. /**
  2. *
  3. * @author Chou
  4. * @since 2012-9-9
  5. * 把事务定义在Service层是为了避免报错:
  6. * All calls to this method via a proxy will be routed directly to the proxy.
  7. * 这是是事务转移问题,你如果在控制层加入事务就不会有提示了,也没有警告,
  8. * 一般很多人在final DAO里加入事务那是有警告的,
  9. * 如果配置文件定义了AOP获取代理对象是proxy-target-class="true"即采用CGLIB方式
  10. * 而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,
  11. * 通过修改其字节码生成子类并继承你写的类,然后在你的基础上加事物管理,
  12. * 而JdbcDaoSupport中的setDataSource是final的他继承不了
  13. * 当然你可以无视它,也没有问题。
  14. */
  15. @Service
  16. public class JdbcTemplateService {
  17. @Autowired
  18. private JdbcTemplateDao jdbcTemplateDao;
  19. public void domain(){
  20. jdbcTemplateDao.save();
  21. int i = 2/0;//这里出错了,事务就会回滚,之前的save就无效了
  22. jdbcTemplateDao.update();
  23. jdbcTemplateDao.delete();
  24. }
  25. }
  26. //main方法
  27. String[] configLocations = new String[] {"applicationContext.xml"};
  28. ApplicationContext ctx = new ClassPathXmlApplicationContext(configLocations);
  29. JdbcTemplateService j = ctx.getBean(JdbcTemplateService.class);
  30. j.domain();

<tx:advice/>配置详解

  1. <tx:advice id="……" transaction-manager="……">
  2. <tx:attributes>
  3. <tx:method name="*"
  4. propagation="REQUIRED"
  5. isolation="DEFAULT"
  6. timeout="-1"
  7. read-only="true"
  8. no-rollback-for=""
  9. rollback-for="java.lang.Exception"/>
  10. </tx:attributes>
  11. </tx:advice>
  12. <!-- 最常用的配置 -->
  13. <tx:advice id="txAdvice" transaction-manager="txManager">
  14. <tx:attributes>
  15. <tx:method name="save*" propagation="REQUIRED" />
  16. <tx:method name="add*" propagation="REQUIRED" />
  17. <tx:method name="create*" propagation="REQUIRED" />
  18. <tx:method name="insert*" propagation="REQUIRED" />
  19. <tx:method name="update*" propagation="REQUIRED" />
  20. <tx:method name="merge*" propagation="REQUIRED" />
  21. <tx:method name="del*" propagation="REQUIRED" />
  22. <tx:method name="remove*" propagation="REQUIRED" />
  23. <tx:method name="put*" propagation="REQUIRED" />
  24. <tx:method name="get*" propagation="SUPPORTS" read-only="true" />
  25. <tx:method name="count*" propagation="SUPPORTS" read-only="true" />
  26. <tx:method name="find*" propagation="SUPPORTS" read-only="true" />
  27. <tx:method name="list*" propagation="SUPPORTS" read-only="true" />
  28. <tx:method name="*" propagation="SUPPORTS" read-only="true" />
  29. <tx:method name="batchSaveOrUpdate" propagation="REQUIRES_NEW" />
  30. </tx:attributes>
  31. </tx:advice>
  32. <aop:config>
  33. <aop:pointcut id="txPointcut" expression="execution(* cn.javass..service.*.*(..))" />
  34. <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
  35. </aop:config>

XML形式的事务配置<tx:method >的属性详解

属性
类型
默认值
说明
propagation Propagation枚举 REQUIRED 事务传播属性
isolation isolation枚举 DEFAULT(所用数据库默认级别) 事务隔离级别
readOnly boolean false 是否才用优化的只读事务
timeout int -1 超时(秒)
rollbackFor Class[] {} 需要回滚的异常类
rollbackForClassName String[] {} 需要回滚的异常类名
noRollbackFor Class[] {} 不需要回滚的异常类
noRollbackForClassName String[] {} 不需要回滚的异常类名

readOnly

事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务。如果值为true就会告诉Spring我这个方法里面没有insert或者update,你只需要提供只读的数据库Connection就行了,这种执行效率会比read-write的Connection高,所以这是一个最优化提示。在一些情况下,一些事务策略能够起到显著的最优化效果,例如在使用Object/Relational映射工具(如:Hibernate或TopLink)时避免dirty checking(试图“刷新”)。

timeout

在属性中还有定义“timeout”值的选项,指定事务超时为几秒。一般不会使用这个属性。在JTA中,这将被简单地传递到J2EE服务器的事务协调程序,并据此得到相应的解释。

Isolation Level(事务隔离等级)的5个枚举值

为什么事务要有Isolation Level这个属性?先回顾下数据库事务的知识:

第一类丢失更新(lost update):在完全未隔离事务的情况下,两个事物更新同一条数据资源,某一事物异常终止,回滚造成第一个完成的更新也同时丢失。

第二类丢失更新(second lost updates):是不可重复读的特殊情况,如果两个事务都读取同一行,然后两个都进行写操作,并提交,第一个事务所做的改变就会丢失。

脏读(dirty read):如果第二个事务查询到第一个事务还未提交的更新数据,形成脏读。因为第一个事务你还不知道是否提交,所以数据不一定是正确的。

虚读(phantom read):一个事务执行两次查询,第二次结果集包含第一次中没有或者某些行已被删除,造成两次结果不一致,只是另一个事务在这两次查询中间插入或者删除了数据造成的。

不可重复读(unrepeated read):一个事务两次读取同一行数据,结果得到不同状态结果,如中间正好另一个事务更新了该数据,两次结果相异,不可信任。

具体关于事务机制可以看我以前的博客:http://zhou137520.iteye.com/admin/blogs/1638574

当遇到以上这些情况时我们可以设置isolation下面这些枚举值:

DEFAULT:采用数据库默认隔离级别

SERIALIZABLE:最严格的级别,事务串行执行,资源消耗最大;

REPEATABLE_READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。

READ_COMMITTED:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。

READ_UNCOMMITTED:保证了读取过程中不会读取到非法数据。隔离级别在于处理多事务的并发问题。

关于propagation属性的7个传播行为

REQUIRED:指定当前方法必需在事务环境中运行,如果当前有事务环境就加入当前正在执行的事务环境,如果当前没有事务,就新建一个事务。这是默认值。

SUPPORTS:指定当前方法加入当前事务环境,如果当前没有事务,就以非事务方式执行。

MANDATORY:指定当前方法必须加入当前事务环境,如果当前没有事务,就抛出异常。

REQUIRES_NEW:指定当前方法总是会为自己发起一个新的事务,如果发现当前方法已运行在一个事务中,则原有事务被挂起,我自己创建一个属于自己的事务,直我自己这个方法commit结束,原先的事务才会恢复执行。

NOT_SUPPORTED:指定当前方法以非事务方式执行操作,如果当前存在事务,就把当前事务挂起,等我以非事务的状态运行完,再继续原来的事务。

NEVER:指定当前方法绝对不能在事务范围内执行,如果方法在某个事务范围内执行,容器就抛异常,只有没关联到事务,才正常执行。

NESTED:指定当前方法执行时,
如果已经有一个事务存在,则运行在这个嵌套的事务中.如果当前环境没有运行的事务,就新建一个事务,并与父事务相互独立,这个事务拥有多个可以回滚的保证
点。就是指我自己内部事务回滚不会对外部事务造成影响,只对DataSourceTransactionManager事务管理器起效。

注解形式@Transactional实现事务管理

注意@Transactional只能被应用到public方法上,对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能。

默认情况下,一个有事务的方法,遇到RuntiomeException时会回滚。遇到受检查的异常是不会回滚的,要想所有异常都回滚,要加上属性rollbackFor={Exception.class}

  1. <!-- 事务管理器配置 -->
  2. <bean id="txManager"     class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  3. <property name="sessionFactory" ref="sessionFactory" />
  4. </bean>
  5. <!-- 使用annotation定义事务 -->
  6. <tx:annotation-driven transaction-manager="txManager" proxy-target-class="true" />

transaction-manager:指定事务管理器名字,默认为transactionManager,当使用其他名字时需要明确指定;

proxy-target-class:默认false表示使用JDK代理,如果为true将使用CGLIB代理

order:定义事务通知顺序,默认Ordered.LOWEST_PRECEDENCE,表示将顺序决定权交给AOP来处理。

建议只在实现类或实现类的方法上使用@Transactional,而不要在接口上使用,这是因为如果使用JDK代理机制是没问题,因为其使用基于接口的代理;而使用使用CGLIB代理机制时就会遇到问题,因为其使用基于类的代理而不是接口,这是因为接口上的@Transactional注解是“不能继承的”。

http://jinnianshilongnian.iteye.com/blog/1508018这篇博客讲解了基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。

  1. @Transactional//放在这里表示所有方法都加入事务管理
  2. public class AnnotationUserServiceImpl implements IUserService {
  3. private IUserDao userDao;
  4. private IAddressService addressService;
  5. @Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED)
  6. public void save(UserModel user) {
  7. userDao.save(user);
  8. user.getAddress().setUserId(user.getId());
  9. addressService.save(user.getAddress());
  10. }
  11. @Transactional(propagation=Propagation.REQUIRED, readOnly=true,
  12. isolation=Isolation.READ_COMMITTED)
  13. public int countAll() {
  14. return userDao.countAll();
  15. }
  16. //setter...
  17. }
总结

编程式事务是不推荐的,即使有很少事务操作,Spring发展到现在,没有理由使用编程式事务,只有在为了深入理解Spring事务管理才需要学习编程式事务使用。

       推荐使用声明式事务,而且强烈推荐使用<tx:tags>方式的声明式事务,因为其是无侵入代码的,可以配置模板化的事务属性并运用到多个项目中。

       而@Transaction注解事务,不过如果一个项目模块太多,service方法太多导致每个方法都要手动去加注解,是不是很麻烦,也容易出错。如果一个项目结构清晰,分层明确,那么标签形式的配置将是最直观和方便的办法。

       总之,能保证项目正常工作的事务配置就是最好的。 

[转]Spring3核心技术之事务管理机制的更多相关文章

  1. Spring入门5.事务管理机制

    Spring入门5.事务管理机制 20131126 代码下载 : 链接: http://pan.baidu.com/s/1kYc6c 密码: 233t 回顾之前的知识,Spring 最为核心的两个部分 ...

  2. 《深入理解mybatis原理》 MyBatis事务管理机制

    MyBatis作为Java语言的数据库框架,对数据库的事务管理是其很重要的一个方面.本文将讲述MyBatis的事务管理的实现机制. 首先介绍MyBatis的事务Transaction的接口设计以及其不 ...

  3. mybatis事务管理机制详解

    1.mybatis事务的配置和使用 mybatis事务有两种使用方式: (a):使用JDBC的事务管理机制:即使用java.Sql.Connection对象完成对事务的提交,回滚和关闭操作. (b): ...

  4. Spring事务管理机制的实现原理-动态代理

    之前在做项目中遇到spring无法进行事务代理问题,最后发现是因为没有写接口,原因当时明白了,看到这篇文章写的清楚些,转过来 我们先来分析一下Spring事务管理机制的实现原理.由于Spring内置A ...

  5. spring 事务管理机制

    1. spring 事务管理抽象 spring 的事务策略机制的核心就是 org.springframework.transaction.PlatformTransactionManager 接口. ...

  6. spring3-spring的事务管理机制

    1. Spring的事务管理机制 Spring事务管理高层抽象主要包括3个接口,Spring的事务主要是由他们共同完成的: PlatformTransactionManager:事务管理器—主要用于平 ...

  7. Spring入门6事务管理2 基于Annotation方式的声明式事务管理机制

    Spring入门6事务管理2 基于Annotation方式的声明式事务管理机制 201311.27 代码下载 链接: http://pan.baidu.com/s/1kYc6c 密码: 233t 前言 ...

  8. MyBatis事务管理机制

    MyBatis作为Java语言的数据库框架,对数据库的事务管理是其非常重要的一个方面.   本文将讲述MyBatis的事务管理的实现机制,首先介绍MyBatis的事务Transaction的接口设计以 ...

  9. Spring 框架基础(05):事务管理机制,和实现方式

    本文源码:GitHub·点这里 || GitEE·点这里 一.Spring事务管理 1.基础描述 Spring事务管理的本质就是封装了数据库对事务支持的操作,使用JDBC的事务管理机制,就是利用jav ...

随机推荐

  1. Nodejs 请求转发代理

    var sendPromise = function (res, callback) { var options = { hostname: settings.Ip, port: settings.P ...

  2. AC日记——字符串的展开 openjudge 1.7 35

    35:字符串的展开 总时间限制:  1000ms 内存限制:  65536kB 描述 在初赛普及组的“阅读程序写结果”的问题中,我们曾给出一个字符串展开的例子:如果在输入的字符串中,含有类似于“d-h ...

  3. MySQL 5.7.x 配置教程

    软件环境 操作系统:windows 10 x64 企业版 MySQL:mysql-5.7.11-winx64 MySQL官网下载:http://downloads.mysql.com/archives ...

  4. JS客户端判断

    <script language="javascript" type="text/javascript"> function browserDete ...

  5. java (基本语法)

    2.五大内存区 方法区就是存储共享数据的地方 3.一个实体多处引用 只有还有实体被指向,这个实体就不能消失.当所有的指向都消失之后,这个实体被视为垃圾,被垃圾回收机制不定期的回收. 堆里的实体能存储多 ...

  6. 全球首发免费的MySql for Entity Framework Core

    from:http://www.1234.sh/post/pomelo-data-mysql?utm_source=tuicool&utm_medium=referral Source 源代码 ...

  7. 10 Things Every Java Programmer Should Know about String

    String in Java is very special class and most frequently used class as well. There are lot many thin ...

  8. Linux commands frequently used

    touch <filename>.sh gedit <filename>.sh bash <filename>.sh & ps auxw|grep < ...

  9. Java 集合系列16之 HashSet详细介绍(源码解析)和使用示例

    概要 这一章,我们对HashSet进行学习.我们先对HashSet有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashSet.内容包括:第1部分 HashSet介绍第2部分 HashSe ...

  10. AngularJS中的digest循环$apply

    欢迎大家指导与讨论 : ) 前言 Angular会拓展这个标准的浏览器流程,创建一个Angular上下文.这个Angular上下文指的是运行在Angular事件循环内的特定代码,该Angular事件循 ...