spring事务管理配置:注解和XML

本文内容前面配置从spring3.2.8参考文档摘抄以及自己的理解,后面关于传播级别和隔离的知识点参考自 http://www.blogjava.net/freeman1984/archive/2010/04/28/319595.html

一、XML配置(推荐使用XML配置,一次配置可以多处使用,
    注解需要每个方法写  @Transactinal)

<!-- from the file 'context.xml' -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="
     http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/tx
     http://www.springframework.org/schema/tx/spring-tx.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop.xsd">

  <!-- this is the service object that we want to make transactional -->
  <bean id="fooService" class="x.y.service.DefaultFooService"/>

  <!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
  <tx:advice id="txAdvice" transaction-manager="txManager">
  <!-- the transactional semantics... -->
  <tx:attributes>
    <!-- all methods starting with 'get' are read-only -->
    <tx:method name="get*" read-only="true"/>
    <!-- other methods use the default transaction settings (see below) -->
    <tx:method name="*"/>
  </tx:attributes>
  </tx:advice>

  <!-- ensure that the above transactional advice runs for any execution
    of an operation defined by the FooService interface -->
  <aop:config>
  <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
  </aop:config>

  <!-- don't forget the DataSource -->
  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
  <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
  <property name="username" value="scott"/>
  <property name="password" value="tiger"/>
  </bean>

  <!-- similarly, don't forget the PlatformTransactionManager -->
  <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
  </bean>

  <!-- other <bean/> definitions here -->
</beans>

注:根据spring参考文档,可以对不同的bean设置不同的事务级别

关注点:
1.通过<tx:advice>标签里的<tx:method>指定需要进行事务处理的类。
2.<tx:method>包含属性如下:
  name:方法名称。如“get*”代表以get开始的方法。
  propagation:事务传播级别
  isolation:隔离级别
  timeout:事务超时时间
  read-only:只读。如配置为true,主要是spring用来优化性能,
             或者防止某些方法进行写操    作;不配置为true也能正常工作。
  rollback-for:配置事务需要回滚的异常。
  如:<tx:method name="get*" rollbackfor="NoProductInStockException"/>
  no-rollback-for:配置事务不需要回滚的异常。
  如:<tx:method name="updateStock" no-rollbackfor="InstrumentNotFoundException"/>

二、注解
<!-- from the file 'context.xml' -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xmlns:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="
     http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/tx
     http://www.springframework.org/schema/tx/spring-tx.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop.xsd">

  <!-- this is the service object that we want to make transactional -->
  <bean id="fooService" class="x.y.service.DefaultFooService"/>

  <!-- enable the configuration of transactional behavior based on annotations -->
  <tx:annotation-driven transaction-manager="txManager"/>

  <!-- a PlatformTransactionManager is still required -->
  <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <!-- (this dependency is defined somewhere else) -->
  <property name="dataSource" ref="dataSource"/>
  </bean>

  <!-- other <bean/> definitions here -->
</beans>


关注点:
1.在类或者方法上加上@Transactional,即可加入spring的事务管理,两者可以同时加,但方法上的事务优于类的事务。@Transactional的属性与基于XML配置的<tx:method>包含的属性差不多,如下两个属性新增:
rollbackForClassName:需要回滚的类名
noRollbackForClassName:不需要回滚的类名集
默认属性:
a.Propagation setting is PROPAGATION_REQUIRED.
b.Isolation level is ISOLATION_DEFAULT.
c.Transaction is read/write.
d.Transaction timeout defaults to the default timeout of the underlying transaction system, or to none if timeouts are not supported.
e.Any RuntimeException triggers rollback, and any checked Exception does not.

2.<tx:annotation-driven/>属性
  transaction-manager:事务管理器名字;如果事务管理器bean的
  名字默认为“transactionManager”,则此属性默认就会关联;
  如果名字不是“transactionManager”,则需要指定。
  mode:代理模式,默认“proxy”,直接使用spring aop方式处理;
  也可以选择“aspectj”,则用aspectj方式处理,具体参考文档。
(基于XML的配置不可以选择。)
  proxy-target-class:默认false,基于JDK动态代理,只适用于面向接口编程;
  当有些特殊情况不是面向接口编程时,可以使用CGLIB等基于继承的代理方式,
  值为true。基于XML方式的配置也可以指定此值,在<aop:config>有此属性。
  order:当有多个advise存在时,可以通过此属性指定执行顺序。
  基于XML方式的配置可以参考文档。

三、事务传播与隔离级别。(TransactionDefinition中定义)
    
/**
      * Support a current transaction; create a new one if none exists.
      */
     int PROPAGATION_REQUIRED = 0;

     /**
      * Support a current transaction; 
      * execute non-transactionally if none exists.
      */
     int PROPAGATION_SUPPORTS = 1;

     /**
      * Support a current transaction; 
      * throw an exception if no current transaction.
      */
     int PROPAGATION_MANDATORY = 2;

     /**
      * Create a new transaction, 
      * suspending the current transaction if one exists.
      */
     int PROPAGATION_REQUIRES_NEW = 3;

     /**
      * Do not support a current transaction; 
      * rather always execute non-transactionally.
      */
     int PROPAGATION_NOT_SUPPORTED = 4;

     /**
      * Do not support a current transaction; 
      * throw an exception if a current transaction
      */
     int PROPAGATION_NEVER = 5;

     /**
      * Execute within a nested transaction if a current transaction
      */
     int PROPAGATION_NESTED = 6;


     /**
      * Use the default isolation level of the underlying datastore.
      */
     int ISOLATION_DEFAULT = -1;

     /**
      * Indicates that dirty reads, non-repeatable reads and phantom reads
      * can occur.
      */
     int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;

     /**
      * Indicates that dirty reads are prevented; non-repeatable reads and
      * phantom reads can occur.
      */
      int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
 
     /**
      * Indicates that dirty reads and non-repeatable reads are prevented;
      * phantom reads can occur.
      */
     int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;

     /**
      * Indicates that dirty reads, non-repeatable reads and phantom reads
      * are prevented.
      */
     int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;



四、事务传播实例
  
ServiceA {
     /** 
      * 事务属性配置为 PROPAGATION_REQUIRED 
      */ 
     void methodA() { 
         ServiceB.methodB(); 
     } 
   } 
  
   ServiceB { 
     /** 
      * 事务属性配置为 PROPAGATION_REQUIRED 
      */ 
     void methodB() { 
     } 
   } 


   1: PROPAGATION_REQUIRED
   加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务 比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行  ServiceA.methodA的时候,ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA 的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事  务中,他就会为自己分配一个事务。这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被   回滚。即使ServiceB.methodB的事务已经被 提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚

   2: PROPAGATION_SUPPORTS
   如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的  形式运行

   3: PROPAGATION_MANDATORY
   必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常

    4: PROPAGATION_REQUIRES_NEW
    这个就比较绕口了。 比如我们设计ServiceA.methodA的事务级别PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后, 他才继续执行。他PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚, 如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。

    5: PROPAGATION_NOT_SUPPORTED
    当前不支持事务。比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED,那么当执行ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。

    6: PROPAGATION_NEVER
    不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER , 那么ServiceB.methodB就要抛出异常了。

    7: PROPAGATION_NESTED
    理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立, 而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。 而Nested事务的好处是他有一个savepoint。
如下所示:
  
 ServiceA { 
    /** 
     * 事务属性配置为 PROPAGATION_REQUIRED 
     */ 
     void methodA() { 
        try { 
             //savepoint 
             ServiceB.methodB(); //PROPAGATION_NESTED 级别 
        } catch (SomeException) { 
             // 执行其他业务, 如 ServiceC.methodC(); 
        } 
      } 
     } 

     也就是说ServiceB.methodB失败回滚,那么ServiceA.methodA也会回滚到savepoint点上,ServiceA.methodA可以选择另外一个分支,比如ServiceC.methodC,继续执行,来尝试完成自己的事务。 但是这个事务并没有在EJB标准中定义。

五、Isolation Level(事务隔离等级)
    1、Serializable:最严格的级别,事务串行执行,资源消耗最大;
    2、REPEATABLE READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。
    3、READ COMMITTED:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。
    4、Read Uncommitted:保证了读取过程中不会读取到非法数据。隔离级别在于处理多事务的并发问题。
    我们知道并行可以提高数据库的吞吐量和效率,但是并不是所有的并发事务都可以并发运行,这需要查看数据库教材的可串行化条件判断了。

    我们首先说并发中可能发生的3中不讨人喜欢的事情
    1: Dirty reads--读脏数据。也就是说,比如事务A的未提交(还依然缓存)的数据被事务B读走,如果事务A失败回滚,会导致事务B所读取的的数据是错误的。
    2: non-repeatable reads--数据不可重复读。比如事务A中两处读取数据-total-的值。在第一读的时候,total是100,然后事务B就把total的数据改成200,事务A再读一次,结果就发现,total竟然就变成200了,造成事务A数据混乱。
    3: phantom reads--幻象读数据,这个和non-repeatable reads相似,也是同一个事务中多次读不一致的问题。但是non-repeatable reads的不一致是因为他所要取的数据集被改变了(比如total的数据),但是phantom reads所要读的数据的不一致却不是他所要读的数据集改变,而是他的条件数据集改变。比如Select account.id where account.name="ppgogo*",第一次读去了6个符合条件的id,第二次读取的时候,由于事务b把一个帐号的名字由"dd"改成"ppgogo1",结果取出来了7个数据。

你可能感兴趣的:(spring,AOP,事务)