Spring(三): 事务

来源:互联网 发布:ios和mac 编辑:程序博客网 时间:2024/05/16 18:55
包括:
一. 事务的基本概念
二. Spring事务的实现方式
三. Spring事务配置详解
四. Spring多事务语义配置及最佳实践
五. @Transactional实现事务管理
六. @Transactional配置详解


一. 事务的基本概念
        事务,是指一系列操作,要么全部做完,要么都不做。例如用户购物:
  1. 更新客户所购商品的库存信息
  2. 保存客户付款信息
  3. 生成订单保存数据库
  4. 更新用户相关信息

       正常情况下,全部做完,就是一个完整的交易,就是一个事务。但是如果第一第二步做完了,出错了,然后没保存订单,那么后果就严重了,这就不是一个事务操作。数据库事务就是保证不能出现这样的问题。


二.Spring 事务实现方式
        主要有两种实现方式:声明式和编程式声明式事务是无侵入式的,对于业务逻辑实现无影响。这是通过AOP来进行实现。大多数Spring用户选择声明式事务管理。
实现:
1. 配置 声明:
<beans xmlns="http://www.springframework.org/schema/beans"      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"      xmlns:tx="http://www.springframework.org/schema/tx"      xmlns:aop="http://www.springframework.org/schema/aop"      xsi:schemaLocation="          http://www.springframework.org/schema/beans          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd          http://www.springframework.org/schema/tx          http://www.springframework.org/schema/tx/spring-tx-3.0.xsd          http://www.springframework.org/schema/aop           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">  

2.配置业务逻辑
<bean id="userService" class="cn.javass.spring.chapter9.service.impl.ConfigUserServiceImpl">      <property name="userDao" ref="userDao"/>      <property name="addressService" ref="addressService"/>  </bean>  <bean id="addressService" class="cn.javass.spring.chapter9.service.impl.ConfigAddressServiceImpl">      <property name="addressDao" ref="addressDao"/>  </bean>  

3.事务相关配置:
<tx:advice id="txAdvice" transaction-manager="txManager">      <tx:attributes>          <tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED"/>          <tx:method name="*" propagation="REQUIRED" isolation="READ_COMMITTED" read-only="true"/>      </tx:attributes>  </tx:advice>  

4. AOP配置
<aop:config>      <aop:pointcut id="serviceMethod" expression="execution(* cn..chapter9.service..*.*(..))"/>      <aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice"/>  </aop:config>  

解释:
  • <tx:advice>:事务通知定义,用于指定事务属性,其中"transaction-manager"属性指定事务管理器,并通过 <tx:attributes> 指定具体需要拦截的方法;
  • <tx:method name = "save*">:用于将拦截以save开头的方法,被拦截的方法将应用配置的事务属性:propagation="REQUIRED"表示传播行为是Required,isolation="READ_COMMITTED"表示隔离级别是提交读
  • <tx:method name = "*">:表示将拦截其他所有方法,被拦截的方法将应用配置的事务属性:propagation="REQUIRED"表示传播行为是Required,isolation="READ_COMMITTED"表示隔离级别是提交读,read-only="true"表示事务只读
  • <aop:config>:AOP相关配置
  • <aop:pointcut />:切入点定义,定义名为"serviceMethod"的aspectJ切入点,切入点表达式为"execution(* cn..chapter9.service..*.*(..))" 表示拦截 cn包以及子包下的chapter9.service包以及子包下的任何类和任何方法。
  • <aop:advisor>:Advisor定义,其中切入点为serviceMethod,通知为txAdvice。从配置中可以看出,将对cn包以及子包下的chapter9.service包以及子包下的任何类的任何方法应用"txAdvice"通知指定的事务属性。

        当调用save方式时将匹配到事务通知中定义的<tx:method name = "save"> 中指定的事务属性,而调用countAll方法时将匹配到事务通知中定义的"<tx:method name = "*">"中定义的事务属性。

        声明式事务管理同样是通过AOP代理方式实现。利用环绕通知 TransactionInterceptor实现事务的开启和关闭。


三. Spring 事务配置详解
声明式事务管理通过配置<tx:advice /> 来定义事务属性,配置方式如下所示:
<tx:advice id="……" transaction-manager="……">      <tx:attributes>          <tx:method name="……"              propagation=" REQUIRED"              isolation="READ_COMMITTED"              timeout="-1"              read-only="false"              no-rollback-for=""               rollback-for=""/>              ……      </tx:attributes>  </tx:advice>
  • <tx:advice>: id用于指定此通知的名字,transaction-manager 用于指定事务管理器,默认的事务管理器名字为 "transactionManager"。
  • <tx:method>:用于定义事务属性即相关联的方法名。

<tx:method>参数;

  • name:定义与事务属性相关联的方法名,将对匹配的方法应用定义的事务属性,可以使用"*"通配符来匹配一组或者所有方法,如"save*" 将匹配以save开头的方法,而"*"将匹配所有方法。
  • propagation:事务传播行为定义,默认为"REQUIRED",表示Required,其值可以通过TransactionDefinition的静态传播行为变量的"PROPAGATIOIN_"后边部分指定。如"TransactionDefinition.PROPAGATION_REQUIRED" 可以使用 "REQUIRED"指定。
  • isolation:事务隔离级别定义;默认为"DEFAULT",其值可以通过TransactionDefinition的静态隔离级别变量的"ISOLATION_"后边部分指定,如"TransactionDefinition.ISOLATION_DEFAULT"可以使用"DEFAULT"指定。
  • timeout:事务超时时间设置,单位为秒,默认 -1,表示事务超时将依赖于底层事务系统。
  • read-only:事务只读设置,默认为false,表示不是只读。
  • rollback-for:需要出发回滚的异常定义,以","分割,默认任何RuntimeException将导致事务回滚,而任何CheckedException 将不导致事务回滚;异常名字定义和TransactionProxyFactoryBean中含义一样。
四. Spring 多事务语义配置以及 最佳实践
    
   什么是多事务语义?说白了就是为不同的Bean配置不同的事务属性,因为我们项目中不可能就几个Bean,而可能很多,这可能需要为Bean分组,为不同组的Bean配置不同的事务语义。在Spring中,可以通过配置多切入点和多事务通知并通过不同方式组合使用即可。
        例如:
<tx:advice id="txAdvice" transaction-manager="txManager">      <tx:attributes>          <tx:method name="save*" propagation="REQUIRED" />          <tx:method name="add*" propagation="REQUIRED" />          <tx:method name="create*" propagation="REQUIRED" />          <tx:method name="insert*" propagation="REQUIRED" />          <tx:method name="update*" propagation="REQUIRED" />          <tx:method name="merge*" propagation="REQUIRED" />          <tx:method name="del*" propagation="REQUIRED" />          <tx:method name="remove*" propagation="REQUIRED" />          <tx:method name="put*" propagation="REQUIRED" />          <tx:method name="get*" propagation="SUPPORTS" read-only="true" />          <tx:method name="count*" propagation="SUPPORTS" read-only="true" />          <tx:method name="find*" propagation="SUPPORTS" read-only="true" />          <tx:method name="list*" propagation="SUPPORTS" read-only="true" />          <tx:method name="*" propagation="SUPPORTS" read-only="true" />      </tx:attributes>  </tx:advice>  <aop:config>      <aop:pointcut id="txPointcut" expression="execution(* cn.javass..service.*.*(..))" />      <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />  </aop:config>  

       该声明式事务配置可以应付常见的CRUD接口定义,并实现事务管理,我们只需修改切入点表达式来拦截我们的业务实现从而对其应用事务属性就可以了,如果还有更复杂的事务属性直接添加即可,即:

如果我们有一个batchSaveOrUpdate方法需要“REQUIRES_NEW”事务传播行为,则直接添加如下配置即可:

<tx:method name="batchSaveOrUpdate" propagation="REQUIRES_NEW" />  

       接下来看一下多事务语义配置,声明式事务最佳实践中已经配置了通用事务属性,因此可以针对需要其他事务属性的业务方法进行特例化配置:
<tx:advice id="noTxAdvice" transaction-manager="txManager">      <tx:attributes>          <tx:method name="*" propagation="NEVER" />      </tx:attributes>  </tx:advice>  <aop:config>      <aop:pointcut id="noTxPointcut" expression="execution(* cn.javass..util.*.*())" />      <aop:advisor advice-ref="noTxPointcut" pointcut-ref="noTxAdvice" />  </aop:config>  
该声明将对切入点匹配的方法所在事务应用"Never"传播行为。多事务语义配置时,切入点一定不要叠加,否则将应用两次事务属性,造成不必要的麻烦和错误。

五. @Transactional实现事务管理

        对声明式事务管理,Spring提供基于@Transactional注解方式来实现,但需要Java 5+。注解方式是最简单的事务配置方式,可以直接在Java源代码中声明事务属性,且对于每一个业务类或方法如果需要事务都必须使用此注解。接下来学习一下注解事务的使用吧:

1、定义业务逻辑实现:

package cn.javass.spring.chapter9.service.impl;  //省略import  public class AnnotationUserServiceImpl implements IUserService {      private IUserDao userDao;      private IAddressService addressService;      public void setUserDao(IUserDao userDao) {          this.userDao = userDao;      }      public void setAddressService(IAddressService addressService) {          this.addressService = addressService;      }      @Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED)      @Override      public void save(final UserModel user) {          userDao.save(user);          user.getAddress().setUserId(user.getId());          addressService.save(user.getAddress());      }      @Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED, readOnly=true)      @Override      public int countAll() {          return userDao.countAll();      }  }  

package cn.javass.spring.chapter9.service.impl;  //省略import  public class AnnotationUserServiceImpl implements IUserService {      private IUserDao userDao;      private IAddressService addressService;      public void setUserDao(IUserDao userDao) {          this.userDao = userDao;      }      public void setAddressService(IAddressService addressService) {          this.addressService = addressService;      }      @Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED)      @Override      public void save(final UserModel user) {          userDao.save(user);          user.getAddress().setUserId(user.getId());          addressService.save(user.getAddress());      }      @Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED, readOnly=true)      @Override      public int countAll() {          return userDao.countAll();      }  }  

2、定义配置文件(chapter9/service/ applicationContext-service-annotation.xml):

2.1、XML命名空间定义,定义用于事务支持的tx命名空间和AOP支持的aop命名空间:

<beans xmlns="http://www.springframework.org/schema/beans"      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"      xmlns:tx="http://www.springframework.org/schema/tx"      xmlns:aop="http://www.springframework.org/schema/aop"      xsi:schemaLocation="          http://www.springframework.org/schema/beans          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd          http://www.springframework.org/schema/tx          http://www.springframework.org/schema/tx/spring-tx-3.0.xsd          http://www.springframework.org/schema/aop          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">  

2.2、业务实现配置,非常简单,使用以前定义的非侵入式业务实现:

<bean id="userService" class="cn.javass.spring.chapter9.service.impl.ConfigUserServiceImpl">      <property name="userDao" ref="userDao"/>      <property name="addressService" ref="addressService"/>  </bean>  <bean id="addressService" class="cn.javass.spring.chapter9.service.impl.ConfigAddressServiceImpl">      <property name="addressDao" ref="addressDao"/>  </bean>  

2.3、事务相关配置:

<tx:annotation-driven transaction-manager="txManager"/>  

使用如上配置已支持声明式事务。

六. @Transactional配置详解

       Spring提供的<tx:annotation-driven/>用于开启对注解事务管理的支持,从而能识别Bean类上的@Transactional注解元数据,其具有以下属性:

  • transaction-manager:指定事务管理器名字,默认为transactionManager,当使用其他名字时需要明确指定;
  • proxy-target-class:表示将使用的代码机制,默认false表示使用JDK代理,如果为true将使用CGLIB代理
  • order:定义事务通知顺序,默认Ordered.LOWEST_PRECEDENCE,表示将顺序决定权交给AOP来处理。


        Spring使用@Transaction来指定事务属性,可以在接口、类或方法上指定,如果类和方法上都指定了@Transaction,则方法上的事务属性被优先使用,具体属性如下:

  • value:指定事务管理器名字,默认使用<tx:annotation-driven/>指定的事务管理器,用于支持多事务管理器环境;
  • propagation:指定事务传播行为,默认为Required,使用Propagation.REQUIRED指定;
  • isolation:指定事务隔离级别,默认为“DEFAULT”,使用Isolation.DEFAULT指定;
  • readOnly:指定事务是否只读,默认false表示事务非只读;
  • timeout:指定事务超时时间,以秒为单位,默认-1表示事务超时将依赖于底层事务系统;
  • rollbackFor:指定一组异常类,遇到该类异常将回滚事务;
  • rollbackForClassname:指定一组异常类名字,其含义与<tx:method>中的rollback-for属性语义完全一样;
  • noRollbackFor:指定一组异常类,即使遇到该类异常也将提交事务,即不回滚事务;
  • noRollbackForClassname:指定一组异常类名字,其含义与<tx:method>中的no-rollback-for属性语义完全一样;

 

       Spring提供的@Transaction注解事务管理内部同样利用环绕通知TransactionInterceptor实现事务的开启及关闭。

使用@Transaction注解事务管理需要特别注意以下几点:

  • 如果在接口、实现类或方法上都指定了@Transactional 注解,则优先级顺序为方法>实现类>接口;
  • 建议只在实现类或实现类的方法上使用@Transactional,而不要在接口上使用,这是因为如果使用JDK代理机制是没问题,因为其使用基于接口的代理;而使用使用CGLIB代理机制时就会遇到问题,因为其使用基于类的代理而不是接口,这是因为接口上的@Transactional注解是“不能继承的”
  • 在JDK代理机制下,“自我调用”同样不会应用相应的事务属性,其语义和<tx:tags>中一样;
  • 默认只对RuntimeException异常回滚;
  • 在使用Spring代理时,默认只有在public可见度的方法的@Transactional 注解才是有效的,其它可见度(protected、private、包可见)的方法上即使有@Transactional 注解也不会应用这些事务属性的,Spring也不会报错,如果你非要使用非公共方法注解事务管理的话,可考虑使用AspectJ。

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


Ps:混合事务

混合事务管理就是混合多种数据访问技术使用,如混合使用Spring JDBC + Hibernate,接下来让我们学习一下常见混合事务管理:

  1. Hibernate + Spring JDBC/iBATIS:使用HibernateTransactionManager即可支持;
  2. JPA + Spring JDBC/iBATIS:使用JpaTransactionManager即可支持;
  3. JDO + Spring JDBC/iBATIS:使用JtaTransactionManager即可支持;

       混合事务管理最大问题在于如果我们使用第三方ORM框架,如Hibernate,会遇到一级及二级缓存问题,尤其是二级缓存可能造成如使用Spring JDBC和Hibernate查询出来的数据不一致等。

       因此不建议使用这种混合使用和混合事务管理。




参考:http://sishuok.com/forum/blogPost/list/2508.html



0 0
原创粉丝点击