欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

spring的事务管理

程序员文章站 2024-01-13 15:11:10
...

引言: 在Spring中@Transactional提供一种控制事务管理的快捷手段,但是很多人都只是@Transactional简单使用,并未深入了解,其各个配置项的使用方法,本文将深入讲解各个配置项的使用。

1.  @Transactional的定义

    Spring中的@Transactional基于动态代理的机制,提供了一种透明的事务管理机制,方便快捷解决在开发中碰到的问题。在现实中,实际的问题往往比我们预期的要复杂很多,这就要求对@Transactional有深入的了解,以来应对复杂问题。

   首先我们来看看@Transactional的代码定义:

[html] view plain copy
  1. @Target({ElementType.METHOD, ElementType.TYPE})  
  2. @Retention(RetentionPolicy.RUNTIME)  
  3. @Inherited  
  4. @Documented  
  5. public @interface Transactional {  
  6.   
  7.     /**  
  8.      * A qualifier value for the specified transaction.  
  9.      * <p>May be used to determine the target transaction manager,  
  10.      * matching the qualifier value (or the bean name) of a specific  
  11.      * {@link org.springframework.transaction.PlatformTransactionManager}  
  12.      * bean definition.  
  13.      */  
  14.     String value() default "";  
  15.   
  16.     /**  
  17.      * The transaction propagation type.  
  18.      * Defaults to {@link Propagation#REQUIRED}.  
  19.      * @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior()  
  20.      */  
  21.     Propagation propagation() default Propagation.REQUIRED;  
  22.   
  23.     /**  
  24.      * The transaction isolation level.  
  25.      * Defaults to {@link Isolation#DEFAULT}.  
  26.      * @see org.springframework.transaction.interceptor.TransactionAttribute#getIsolationLevel()  
  27.      */  
  28.     Isolation isolation() default Isolation.DEFAULT;  
  29.   
  30.     /**  
  31.      * The timeout for this transaction.  
  32.      * Defaults to the default timeout of the underlying transaction system.  
  33.      * @see org.springframework.transaction.interceptor.TransactionAttribute#getTimeout()  
  34.      */  
  35.     int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;  
  36.   
  37.     /**  
  38.      * {@code true} if the transaction is read-only.  
  39.      * Defaults to {@code false}.  
  40.      * <p>This just serves as a hint for the actual transaction subsystem;  
  41.      * it will <i>not necessarily</i> cause failure of write access attempts.  
  42.      * A transaction manager which cannot interpret the read-only hint will  
  43.      * <i>not</i> throw an exception when asked for a read-only transaction.  
  44.      * @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly()  
  45.      */  
  46.     boolean readOnly() default false;  
  47.   
  48.     /**  
  49.      * Defines zero (0) or more exception {@link Class classes}, which must be a  
  50.      * subclass of {@link Throwable}, indicating which exception types must cause  
  51.      * a transaction rollback.  
  52.      * <p>This is the preferred way to construct a rollback rule, matching the  
  53.      * exception class and subclasses.  
  54.      * <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}  
  55.      */  
  56.     Class<? extends Throwable>[] rollbackFor() default {};  
  57.   
  58.     /**  
  59.      * Defines zero (0) or more exception names (for exceptions which must be a  
  60.      * subclass of {@link Throwable}), indicating which exception types must cause  
  61.      * a transaction rollback.  
  62.      * <p>This can be a substring, with no wildcard support at present.  
  63.      * A value of "ServletException" would match  
  64.      * {@link javax.servlet.ServletException} and subclasses, for example.  
  65.      * <p><b>NB: </b>Consider carefully how specific the pattern is, and whether  
  66.      * to include package information (which isn't mandatory). For example,  
  67.      * "Exception" will match nearly anything, and will probably hide other rules.  
  68.      * "java.lang.Exception" would be correct if "Exception" was meant to define  
  69.      * a rule for all checked exceptions. With more unusual {@link Exception}  
  70.      * names such as "BaseBusinessException" there is no need to use a FQN.  
  71.      * <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(String exceptionName)}  
  72.      */  
  73.     String[] rollbackForClassName() default {};  
  74.   
  75.     /**  
  76.      * Defines zero (0) or more exception {@link Class Classes}, which must be a  
  77.      * subclass of {@link Throwable}, indicating which exception types must <b>not</b>  
  78.      * cause a transaction rollback.  
  79.      * <p>This is the preferred way to construct a rollback rule, matching the  
  80.      * exception class and subclasses.  
  81.      * <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(Class clazz)}  
  82.      */  
  83.     Class<? extends Throwable>[] noRollbackFor() default {};  
  84.   
  85.     /**  
  86.      * Defines zero (0) or more exception names (for exceptions which must be a  
  87.      * subclass of {@link Throwable}) indicating which exception types must <b>not</b>  
  88.      * cause a transaction rollback.  
  89.      * <p>See the description of {@link #rollbackForClassName()} for more info on how  
  90.      * the specified names are treated.  
  91.      * <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(String exceptionName)}  
  92.      */  
  93.     String[] noRollbackForClassName() default {};  
  94.   
  95. }  
  基于源代码,我们可以发现在@Transactional,原来有这么多的属性可以进行配置,从而达到复杂应用控制的目的。具体各个属性的用法和作用,将在本文的后面逐一进行讲解和说明。

2.  使用@Transactional的Spring配置

     为了使用基于@Transactional的事务管理,需要在Spring中进行如下的配置:

[html] view plain copy
  1.    <beans:bean id="transactionManager"  
  2.     class="org.springframework.orm.jpa.JpaTransactionManager">  
  3.     <beans:property name="dataSource" ref="dataSource" />  
  4.     <beans:property name="entityManagerFactory" ref="entityManagerFactory" />  
  5. </beans:bean>  
  6.   
  7. <!-- 声明使用注解式事务 -->  
  8. <tx:annotation-driven transaction-manager="transactionManager" />  

    或者这样

<!-- 对数据源进行事务管理 -->
  <bean id="transactionManager" 
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
        p:dataSource-ref="dataSource"/>
        
<tx:annotation-driven transaction-manager="transactionManager"/>



dataSource是在Spring配置文件中定义的数据源的对象实例,EntityManagerFactory是基于JPA使用的实体类管理器:org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean。这些都是用来配置与数据库的连接信息,本质上,@Transactional使用了JDBC的事务来进行事务控制的。

    <annotation-driven>标签的声明,则是在Spring内部启用@Transactional来进行事务管理,类似开关之类的声明。

3.  @Transactional之value

    value这里主要用来指定不同的事务管理器;主要用来满足在同一个系统中,存在不同的事务管理器。比如在Spring中,声明了两种事务管理器txManager1, txManager2.

然后,用户可以根据这个参数来根据需要指定特定的txManager.

   那有同学会问什么情况下会存在多个事务管理器的情况呢? 比如在一个系统中,需要访问多个数据源或者多个数据库,则必然会配置多个事务管理器的。

4.   @Transactional之propagation

      Propagation支持7种不同的传播机制:

  •  REQUIRED
               业务方法需要在一个事务中运行,如果方法运行时,已处在一个事务中,那么就加入该事务,否则自己创建一个新的事务.这是spring默认的传播行为.。 
  •  SUPPORTS: 
               如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分,如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。
  •  MANDATORY:
               只能在一个已存在事务中执行,业务方法不能发起自己的事务,如果业务方法在没有事务的环境下调用,就抛异常
  •  REQUIRES_NEW
             业务方法总是会为自己发起一个新的事务,如果方法已运行在一个事务中,则原有事务被挂起,新的事务被创建,直到方法结束,新事务才结束,原先的事务才会恢复执行.
  •  NOT_SUPPORTED
           声明方法需要事务,如果方法没有关联到一个事务,容器不会为它开启事务.如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行.
  • NEVER:
              声明方法绝对不能在事务范围内执行,如果方法在某个事务范围内执行,容器就抛异常.只有没关联到事务,才正常执行.
  •  NESTED:

          如果一个活动的事务存在,则运行在一个嵌套的事务中.如果没有活动的事务,则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保证点.内部事务回滚不会对外部事务造成影响, 它只对DataSourceTransactionManager 事务管理器起效.

     其实大家最感到困惑的是REQUIRED_NEW和NESTED两种不同的传播机制,功能类似,都涉及到了事务嵌套的问题,那两者有何区别呢?该如何正确使用这两种模式呢?

        以下是摘自Spring的文档:
          PROPAGATION_REQUIRES_NEW : uses a completely independent transaction for each affected transaction scope. In that case, the underlying physical transactions are different and hence can commit or roll back independently, with an outer transaction not affected by an inner transaction's rollback status.
         内部的事务独立运行,在各自的作用域中,可以独立的回滚或者提交;而外部的事务将不受内部事务的回滚状态影响。 
        ROPAGATION_NESTED : uses a single physical transaction with multiple savepoints that it can roll back to. Such partial rollbacks allow an inner transaction scope to trigger a rollback for its scope, with the outer transaction being able to continue the physical transaction despite some operations having been rolled back. This setting is typically mapped onto JDBC savepoints, so will only work with JDBC resource transactions.
       NESTED的事务,基于单一的事务来管理,提供了多个保存点。这种多个保存点的机制允许内部事务的变更触发外部事务的回滚。而外部事务在混滚之后,仍能继续进行事务处理,即使部分操作已经被混滚。 由于这个设置基于JDBC的保存点,所以只能工作在JDBC的机制智商。
       由此可知, 两者都是事务嵌套,不同之处在于,内外事务之间是否存在彼此之间的影响;NESTED之间会受到影响,而产生部分回滚,而REQUIRED_NEW则是独立的。



以上为事务的传播特性,下面是事务的隔离级别


事务隔离级别

  隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:

  • TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。
  • TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
  • TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
  • TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

事务超时

      所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。

 默认设置为底层事务系统的超时值,如果底层数据库事务系统没有设置超时值,那么就是none,没有超时限制。

@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT,timeout=5)


事务只读属性

      只读事务用于客户代码只读但不修改数据的情形,只读事务用于特定情景下的优化,比如使用Hibernate的时候。
默认为读写事务。


        “只读事务”并不是一个强制选项,它只是一个“暗示”,提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。 

但是你非要在“只读事务”里面修改数据,也并非不可以,只不过对于数据一致性的保护不像“读写事务”那样保险而已。 

因此,“只读事务”仅仅是一个性能优化的推荐配置而已,并非强制你要这样做不可



spring事务回滚规则

     指示spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。

        默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。可以明确的配置在抛出那些异常时回滚事务,包括checked异常。也可以明确定义那些异常抛出时不回滚事务。还可以编程性的通过setRollbackOnly()方法来指示一个事务必须回滚,在调用完setRollbackOnly()后你所能执行的唯一操作就是回滚。


添加tx名字空间
[html] view plain copy
  1. <span style="background-color: rgb(255, 255, 255);"><span style="background-color: rgb(255, 204, 153);">xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"  
  2.   
  3. xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
  4.     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"</span></span>  

        MyBatis自动参与到spring事务管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源与DataSourceTransactionManager引用的数据源一致即可,否则事务管理会不起作用。


@Transactional注解

@Transactional属性 

属性 类型 描述
value String 可选的限定描述符,指定使用的事务管理器
propagation enum: Propagation 可选的事务传播行为设置
isolation enum: Isolation 可选的事务隔离级别设置
readOnly boolean 读写或只读事务,默认读写
timeout int (in seconds granularity) 事务超时时间设置
rollbackFor Class对象数组,必须继承自Throwable 导致事务回滚的异常类数组
rollbackForClassName 类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组
noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组
noRollbackForClassName 类名数组,必须继承自Throwable 不会导致事务回滚的异常类名字数组

用法

       @Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

         虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

        默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。