Spring中的事务总结

Spring中的事务总结

  • 事务介绍

    • 事务具备4个特性:ACID ,详见 db_index.md
    • 在spring的文档中说道,spring声明式事务管理默认对非检查型异常和运行时异常进行事务回滚,而对检查型异常则不进行回滚操作
  • Spring 支持的事务策略,包括两种

    • 全局事务: 由应用服务器管理,需要Web服务器支持 JTA(java transaction api)技术。全局事务可以跨越多个事务性资源(支持跨越多个数据库资源)

      <!-- 配置Jta 全局事务管理-->
      <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
      
      <!-- 注意:无论使用 hibernate, mybatis还是JDBC, JTA 配置都一样 -->
      <!-- 当然,使用JTA全局事务时还需要Web服务器的支持,不同应用服务器所提供的JTA全局事务也存在细节上的差异,因此实际配置时可能会用到JtaTransactionManager的子类,如: OC4JtaTransactionManager(Oracle提供的Web服务器),WebLogicJtaTransactionManager (Bee 提供的Web服务器) 等 -->
      
    • 局部事务:和采用的持久化技术有关,当使用 JDBC 时,需要使用connection对象来操作事务;当使用Hibernate时,需要使用Session对象来操作事务;当使用MyBatis时,和JDBC配置事务方式一样;

      <!-- 配置JDBC数据源的局部事务管理 -->
      <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dataSource" />
      </bean>
      
      <!-- 配置JPA 数据源的局部事务管理 -->
      <bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" >
        <!--使用Spring容器中已有的DataSource,这样就不需要使用persistence.xml文件中的数据源了-->
        <property name="dataSource" ref="dataSource" />
      </bean>
      <bean id="transactionManager" class="org.springframework.jdbc.datasource.JpaTransactionManager">
        <constructor-arg ref="emf" />
      </bean>
      
      <!-- 配置Hibernate的局部事务管理,需要使用如下配置 -->
      <!-- 要想在Spring中使用 Hibernate,需要配置 sessionFactory 类 -->
      <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mappingresource" />
        <property name="hibernateProperties" />
      </bean>
      <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
      <property name="sessionFactory" ref="sessionFactory" />
      </bean>
      
      <!-- 配置MyBatis的局部事务管理 -->
      <!-- 要想在Spring中使用 MyBatis,需要配置 sqlSessionFactory -->
      <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
      </bean>
      <!-- 事务部分,仍然使用的是JDBC数据源的局部事务 -->
      <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dataSource" />
      </bean>
      
  • Spring Boot 的事务原理

    • Spring的事务是通过PlatformTransactionManager 接口实现的,不同的持久化技术,这个接口会有不同的实现类。如果添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。如果你添加的是 spring-boot-starter-data-jpa 依赖,框架会默认注入 JpaTransactionManager 实例。
    • 通过Spring容器(IOC)技术,可以轻松注入 JTA事务或非JTA事务。应用程序不与任何实现类(具体的事务)耦合, 应用程序实际上是面向 PlatformTransactionManager 接口编程的,因此可以将应用程序和持久化技术、事务彻底解耦
    • TransactionDefinition :该接口包含与事务属性有关的方法,事务隔离级别和事务传播行为
      • TransactionDefinition.ISOLATION_READ_COMMITTED:隔离级别是指若干个并发的事务之间的隔离程度。隔离级别有4种

        1) read-uncommitted : 所有事务都可以看到其他未提交事务的执行结果

        2)READ_COMMITTED:表示一个事务只会读取已经提交的数据,该级别可以防止脏读,这也是大多数情况下的默认值。它也是 SQLServer,Oracle的默认级别

        3)REPEATABLE READ(可重复读):意思是在一个事务中第一次读和第二次读数据是一致的,不受其他事务的影响。该级别可以解决不可重复读的问题,它是MySQL的默认级别。

        4)串行化:解决幻读的问题

      • TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。

      • 所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。

      • 事务的只读属性是指,对事务性资源进行只读操作或者是读写操作。所谓事务性资源就是指那些被事务管理的资源,比如数据源、 JMS 资源,以及自定义的事务性资源等等。

      • 事务的回滚规则:我们可以控制事务在抛出某些未检查异常时仍然提交事务,或者在抛出某些已检查异常时回滚事务

  • 两种事务管理方式: 全面分析Spring的编程式事务和声明式事务

    • 编程式事务管理:应用程序可以直接获取transactionManager bean,该bean 是PlatformTransactionManager接口的实例,所以可以通过该接口提供的3个方法,开始事务处理

      • 基于 TransactionDefinition、PlatformTransactionManager、TransactionStatus 编程式事务管理是 Spring 提供的最原始的方式,通常我们不会这么写

        public class BankServiceImpl implements BankService {
          private BankDao bankDao;
          private TransactionDefinition txDefinition;
          private PlatformTransactionManager txManager;
          ......
          public boolean transfer(Long fromId, Long toId, double amount) {
            // PlatformTransactionManager.getTransaction(…) 方法返回一个 TransactionStatus 对象。返回的TransactionStatus 对象可能代表一个新的或已经存在的事务(如果在当前调用堆栈有一个符合条件的事务)
            TransactionStatus txStatus = txManager.getTransaction(txDefinition);
            boolean result = false;
            try {
              result = bankDao.transfer(fromId, toId, amount);
              txManager.commit(txStatus);
            } catch (Exception e) {
              result = false;
              txManager.rollback(txStatus);
              System.out.println("Transfer Error!");
            }
            return result;
          }
        }
        
        <bean id="bankService" class="footmark.spring.core.tx.programmatic.origin.BankServiceImpl">
          <property name="bankDao" ref="bankDao"/>
          <property name="txManager" ref="transactionManager"/>
          <property name="txDefinition">
          <bean class="org.springframework.transaction.support.DefaultTransactionDefinition">
          	<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
          </bean>
          </property>
        </bean>
        
      • 基于 TransactionTemplate 的编程式事务管理是对上一种方式的封装,使得编码更简单、清晰

        public class BankServiceImpl implements BankService {
          private BankDao bankDao;
          private TransactionTemplate transactionTemplate;
          ......
          public boolean transfer(final Long fromId, final Long toId, final double amount) {
            return (Boolean) transactionTemplate.execute(new TransactionCallback(){
              public Object doInTransaction(TransactionStatus status) {
                Object result;
                try {
                	result = bankDao.transfer(fromId, toId, amount);
                } catch (Exception e) {
                  status.setRollbackOnly();
                  result = false;
                  System.out.println("Transfer Error!");
                }
                return result;
              }
            });
          }
        }
        
        <bean id="bankService"
        class="footmark.spring.core.tx.programmatic.template.BankServiceImpl">
        	<property name="bankDao" ref="bankDao"/>
        	<property name="transactionTemplate" ref="transactionTemplate"/>
        </bean>
        
    • 声明式事务管理:不用在应用程序中编写任何事务代码,而是在XML文件中配置事务,推荐使用

      • 基于 TransactionInterceptor 的声明式事务是 Spring 声明式事务的基础,通常也不建议使用这种方式。可以使用 BeanNameAutoProxyCreator 完成自动事务代理

        <beans...>
        ......
        	<bean id="transactionInterceptor"
        class="org.springframework.transaction.interceptor.TransactionInterceptor">
            <property name="transactionManager" ref="transactionManager"/>
            <!--transactionAttributes 定义事务规则,该属性的每一个键值对中,键指定的是方法名,方法名可以使用通配符,而值就表示相应方法的所应用的事务属性-->
            <property name="transactionAttributes">
            <props>
              <!--事务属性的规则 : 传播行为 [,隔离级别] [,只读属性] [,超时属性] [不影响提交的异常] [,导致回滚的异常]-->
              <!-- 例如: PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED,TIMEOUT_20,+AbcException,+DefException,-HijException -->
            	<prop key="transfer">PROPAGATION_REQUIRED</prop>
            </props>
            </property>
          </bean>
          <bean id="bankServiceTarget"
          class="footmark.spring.core.tx.declare.origin.BankServiceImpl">
            <property name="bankDao" ref="bankDao"/>
          </bean>
          <bean id="bankService"
          class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="target" ref="bankServiceTarget"/>
            <property name="interceptorNames">
              <list>
                <idref bean="transactionInterceptor"/>
              </list>
            </property>
          </bean>
          ......
        </beans>
        
        <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> 
           <property name="beanNames"> 
             <list> 
                <value>*ServiceImpl</value> 
             </list> 
           </property> 
           <property name="interceptorNames">  
             <list>   <!-- 下面定义BeanNameAutoProxyCreator所需的事务拦截器 -->
             		<value> transactionInterceptor </value> 
             </list> 
           </property> 
        </bean> 
        
      • 基于 TransactionProxyFactoryBean 的声明式事务是上种方式的改进版本,简化的配置文件的书写,这是 Spring 早期推荐的声明式事务管理方式

        • 声明式事务使用 TransactionProxyFactoryBean ,为目标Bean生成一个事务代理bean
        • 基于 Spring AOP技术,这个事务代理bean会改写目标bean的方法,织入开始事务和结束事务的增强处理
        • 使用 XML Schema 可以简化 TransactionProxyFactoryBean 事务代理相关的配置,具体看书上
        <beans......>
          ......
          <bean id="bankServiceTarget" 
                class="footmark.spring.core.tx.declare.classic.BankServiceImpl">
          	<property name="bankDao" ref="bankDao"/>
          </bean>
          
          <bean id="bankService"
         class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
            <property name="target" ref="bankServiceTarget"/>
            <property name="transactionManager" ref="transactionManager"/>
            <property name="transactionAttributes">
              <props>
                <prop key="transfer">PROPAGATION_REQUIRED</prop>
              </props>
            </property>
          </bean>
          ......
        </beans>
        
      • 基于 和 命名空间的声明式事务管理是目前推荐的方式,其最大特点是与 Spring AOP 结合紧密,可以充分利用切点表达式的强大支持,使得管理事务更加灵活。

        <beans......>
          ......
          <bean id="bankService"
          class="footmark.spring.core.tx.declare.namespace.BankServiceImpl">
          	<property name="bankDao" ref="bankDao"/>
          </bean>
          
          <tx:advice id="bankAdvice" transaction-manager="transactionManager">
            <tx:attributes>
              <tx:method name="transfer" propagation="REQUIRED"/>
            </tx:attributes>
          </tx:advice>
          <aop:config>
            <aop:pointcut id="bankPointcut" expression="execution(* *.transfer(..))"/>
            <aop:advisor advice-ref="bankAdvice" pointcut-ref="bankPointcut"/>
          </aop:config>
          ......
        </beans>
        
      • 基于 @Transactional 的方式将声明式事务管理简化到了极致。透彻的掌握 Spring 中的 @Transactional ; @EnableTransactionManagement 的使用。Spring Boot 使用事务非常简单,首先使用注解 @EnableTransactionManagement 开启事务支持后,然后在访问数据库的Service方法上添加注解 @Transactional 便可。

        //启注解事务管理,等同于xml配置方式的 <tx:annotation-driven />
        @EnableTransactionManagement 
        @SpringBootApplication
        public class ProfiledemoApplication {
           @Bean
           public Object testBean(PlatformTransactionManager platformTransactionManager){
             System.out.println(">>>" + platformTransactionManager.getClass().getName());
             return new Object();
           }
         
           public static void main(String[] args) {
                SpringApplication.run(ProfiledemoApplication.class, args);
           }
        }
        
        // Service层
        @Transactional(propagation = Propagation.REQUIRED,readOnly=true)
        public boolean transfer(Long fromId, Long toId, double amount) {
        	return bankDao.transfer(fromId, toId, amount);
        }
        
      • @Transactional 注解的属性说明

        属性名说明
        name当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。
        propagation事务的传播行为,默认值为 REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建新的事务运行。PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
        isolation事务的隔离度,默认值采用 DEFAULT。
        timeout事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
        read-only指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
        rollback-for用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。默认情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常)或者 Error,则 Spring 将回滚事务;除此之外,Spring 不会回滚事务。如果在事务中抛出其他类型的异常,并期望 Spring 能够回滚事务,可以指定 rollbackFor
        no-rollback- for抛出 no-rollback-for 指定的异常类型,不回滚事务。
  • 常见问题

    • 私有方法是通过public方法的事务进行管理的

      • @Transactional 加载在private方法上是无效的,只有@Transactional 注解应用到 public 方法,才能进行事务管理。
      • 不过当private方法被加了@Transactional注解的public方法调用时,同样会被事务管理,即使这个private方面没有添加@Transactional注解
    • JPA 抛出异常: Transaction rolled back because it has been marked as rollback-only"

      • 原因是:一个事务方法A调用另一个事务方法B时,B如果报错事务就已经是回滚状态了,放回到A之后,A方法继续执行,提交了事务(已经是回滚状态的事务),就报错了 Transaction marked as rollback only
    • 同一类中的方法自调用问题

      • 如果同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,有@Transactional 注解的方法的事务被忽略,出现异常不会发生回滚。
    • 在spring的文档中说道,spring声明式事务管理默认对非检查型异常和运行时异常进行事务回滚,而对检查型异常则不进行回滚操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值