spring事物配置,声明式事务管理和基于@Transactional注解的使用 [转]

spring支持编程式事务管理和声明式事务管理两种方式。

   编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。

        声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。

       显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。

         声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。

spring事务特性

spring所有的事务管理策略类都继承自org.springframework.transaction.PlatformTransactionManager接口

 

其中TransactionDefinition接口定义以下特性:

事务隔离级别

  隔离级别是指若干个并发的事务之间的隔离程度。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定义中包括了如下几个表示传播行为的常量:

  • TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

事务超时

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

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

事务只读属性

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

spring事务回滚规则

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

 

@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 注解,这将被忽略,也不会抛出任何异常。

 

各种属性的意义: 

      REQUIRED:业务方法需要在一个容器里运行。如果方法运行时,已经处在一个事务中,那么加入到这个事务,否则自己新建一个新的事务。 

      NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为他开启事务,如果方法在一个事务中被调用,该事务会被挂起,调用结束后,原先的事务会恢复执行。 

      REQUIRESNEW:不管是否存在事务,该方法总汇为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务挂起,新的事务被创建。 

      MANDATORY:该方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果在没有事务的环境下被调用,容器抛出例外。 

      SUPPORTS:该方法在某个事务范围内被调用,则方法成为该事务的一部分。如果方法在该事务范围外被调用,该方法就在没有事务的环境下执行。 

      NEVER:该方法绝对不能在事务范围内执行。如果在就抛例外。只有该方法没有关联到任何事务,才正常执行。 

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

 

@Transactional注解注意的几点:

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

2用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception("注释");)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚 要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .如果让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
如下:
@Transactional(rollbackFor=Exception.class) //指定回滚,遇到异常Exception时回滚
public void methodName() {
throw new Exception("注释");

}
@Transactional(noRollbackFor=Exception.class)//指定不回滚,遇到运行期例外(throw new RuntimeException("注释");)会回滚
public ItimDaoImpl getItemDaoImpl() {
throw new RuntimeException("注释");
}

3、@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。


4、@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的beans所使用。上面的例子中,其实正是 <tx:annotation-driven/>元素的出现 开启 了事务行为。


5、Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承 的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因 此,请接受Spring团队的建议并且在具体的类上使用 @Transactional 注解。

 

下面就简单的看如下一个例子完整的配置文件

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"    
 3         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
 4         xmlns:p="http://www.springframework.org/schema/p"  
 5         xmlns:aop="http://www.springframework.org/schema/aop"   
 6         xmlns:context="http://www.springframework.org/schema/context"  
 7         xmlns:jee="http://www.springframework.org/schema/jee"  
 8         xmlns:tx="http://www.springframework.org/schema/tx"  
 9         xsi:schemaLocation="    
10             http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd  
11             http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd  
12             http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
13             http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.2.xsd  
14             http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
15     <!-- 引入外部properties属性文件 -->
16     <context:property-placeholder location="classpath:jdbc.properties"/>
17     
18     <!-- 原理:自动注入processor解析器,用来解析注解 -->
19     <!-- 
20     <context:annotation-config/>
21      -->
22      
23     <!-- 自动扫描包,也会自动注入解释器,所以不需要 context:annotation-config-->
24     <context:component-scan base-package="news"/>
25     
26     <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
27         <!-- 注入连接池,包含了数据库用户名,密码等等信息 -->
28         <property name="dataSource" ref="myDataSource"/>
29         
30         <!-- 配置Hibernate的其他的属性 -->
31         <property name="hibernateProperties">
32             <props>
33                 <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
34                 <prop key="hibernate.show_sql">true</prop>
35                 <prop key="hibernate.format_sql">true</prop>
36                 <prop key="hibernate.connection.autocommit">false</prop>
37                 <!-- 开机自动生成表 -->
38                 <prop key="hibernate.hbm2ddl.auto">update</prop>
39             </props>
40         </property>
41         <property name="packagesToScan">
42             <list>
43                 <value>news.entity</value>
44             </list>
45         </property>
46         <!-- 
47         <property name="mappingResources">
48             <list>
49                 <value>news/entity/News.hbm.xml</value>
50             </list>
51         </property>
52          -->
53     </bean>    
54      
55     <bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
56         <property name="driverClass" value="${jdbc_MySQL.driver}"/>
57         <property name="jdbcUrl" value="${jdbc_MySQL.url}"/>
58         <property name="user" value="${jdbc_MySQL.user}"/>
59         <property name="password" value="${jdbc_MySQL.password}"/>
60         <!-- 每300秒检查所有连接池中的空闲连接 -->
61         <property name="idleConnectionTestPeriod" value="300"></property>
62         <!-- 最大空闲时间,900秒内未使用则连接被丢弃。若为0则永不丢弃 -->
63         <property name="maxIdleTime" value="900"></property>
64         <!-- 最大连接数 -->
65         <property name="maxPoolSize" value="2"></property>
66     </bean>
67     
68     <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
69         <property name="sessionFactory" ref="sessionFactory"/>
70     </bean> 
71     
72     <!--启动spring事务注解功能-->
73     <tx:annotation-driven transaction-manager="transactionManager"/>
74     
75     <!-- 
76     <tx:advice id="txAdvice" transaction-manager="transactionManager">
77         <tx:attributes>
78             <tx:method name="add*" propagation="REQUIRED"/>
79             <tx:method name="del*" propagation="REQUIRED"/>
80             <tx:method name="mod*" propagation="REQUIRED"/>
81             <tx:method name="*" propagation="REQUIRED" read-only="true"/>
82         </tx:attributes>
83     </tx:advice>
84     
85     <aop:config>
86         <aop:pointcut expression="execution(* news.dao.*.*(..))" id="intercePointCuts"/>
87         <aop:advisor advice-ref="txAdvice" pointcut-ref="intercePointCuts"/>
88     </aop:config>
89     -->
90 </beans>
91             

现在就用注解方法来实现去掉一份实体类的映射文件

需要在applicationContext.xml这份配置文件里自动扫描具体实体类的包

 

实体类的注解配置如下所示:

使用Spring的AOP注解功能,必须导入如下几个包。aspectjrt.jar,aspectjweaver.jar,cglib-nodep.jar.

使用springAOP管理事务之后,在之前的DAO中需要把openSession获取会话的方式改为getCurrentSession

//Session session=sessionFactory.openSession();
Session session=sessionFactory.getCurrentSession();

openSession与getCurrentSession两者的区别:

1.getCurrentSession的方式会在事务结束时自动关闭连接,而openSession则需要手动关闭连接。

2.采用getCurrentSession创建的session会绑定带当前的线程中去,但openSession则不会。

采用@Transactional注解方式如下:

@Transactional    //放在这里表示类的所有方法都加入事务管理  
public class NewsServiceImpl implements NewsService{
  ······
} 
//也可以在单个方法上加注解
@Transactional(readOnly=true)    
public List showAllNews() {
  ······
}  

事务管理声明注解配置如下

以上xml文件配置的方法换为注解来实现,这里需要这样来配置:

还要配置一下实现注解功能

还可以是在具体的实现方法上加实现注解功能

 

下面来说一下spring事务注解和xml文件实现注解的区别所在

这两种实现事务注解各有优势,只是看程序员的编码风格喜好罢了,其中有的程序员喜好spring事务注解编码风格,而有的程序员就偏好于xml文件实现注解功能当然我还是比较偏好于xml文件来实现注解功能。

对于这两种都是实现事务注解来说,spring事务注解编码风格相对就比较简单,可是它与spring框架耦合在一起了,对于之后的可维护性和可读性没那么好;而对于xml文件实现注解是与spring框架没有耦合在一起,之后的可维护性和可读性相对就好一点,可是编写的代码有点麻烦,还有就是实现的方法命名规范不能随意进行更改。

 

有关注解就写到这里,如果还要想深入的去学习实现注解功能,希望你们还要多点去

搜索网上关于注解的详细讲解,好好珍惜当今互联网的资源学习。 谢谢!

 

 

 

posted on 2016-10-20 19:22  随缘相识-(志同道合)  阅读(786)  评论(0编辑  收藏  举报

导航