文档章节

Spring声明式事务管理与配置详解

只想一个人静一静
 只想一个人静一静
发布于 2014/02/27 17:23
字数 2552
阅读 4759
收藏 18

1、Spring声明式事务配置的五种方式

  前段时间对Spring的事务配置做了比较深入的研究,在此之前对Spring的事务配置虽说也配置过,但是一直没有一个清楚的认识。通过这次的学习发觉Spring的事务配置只要把思路理清,还是比较好掌握的。

  总结如下:

  Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。

  DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问时,DataSource实际为SessionFactory,TransactionManager的实现为HibernateTransactionManager。

  具体如下图:

  根据代理机制的不同,总结了五种Spring事务的配置方式,配置文件如下:

第一种方式:每个Bean都有一个代理

复制代码

<?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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="configLocation" value="classpath:hibernate.cfg.xml" />
        <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
    </bean>

    <!-- 定义事务管理器(声明式的事务) -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <!-- 配置DAO -->
    <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <bean id="userDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <!-- 配置事务管理器 -->
        <property name="transactionManager" ref="transactionManager" />
        <property name="target" ref="userDaoTarget" />
        <property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" />
        <!-- 配置事务属性 -->
        <property name="transactionAttributes">
            <props>
                <prop key="*">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean></beans>

复制代码

第二种方式:所有Bean共享一个代理基类

复制代码

    
         
         
    

    
    
         
    

    
        
         
        
        
            
                PROPAGATION_REQUIRED
            
        
    

    
    
         
    

    
         

复制代码

第三种方式:使用拦截器

复制代码

    
         
         
    

    
    
         
    

    
         
        
        
            
                PROPAGATION_REQUIRED
            
        
    

    
        
            
                *Dao
            
        
        
            
                transactionInterceptor
            
        
    

    
    
         

复制代码

第四种方式:使用tx标签配置的拦截器

复制代码

<?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:context="http://www.springframework.org/schema/context"
    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-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">    
     

    
         
         
    

    
    
         
    

    
        
             
        
    

    
         
         

复制代码

第五种方式:全注解

复制代码

    
     

     

    
         
         
    

    
    
         

复制代码

  此时在DAO上需加上@Transactional注解,如下:

复制代码

@Transactional
@Component("userDao")public class UserDaoImpl extends HibernateDaoSupport implements UserDao {    public List<User> listUsers() {        return this.getSession().createQuery("from User").list();
    }
    ......
}

复制代码

  注:以上部分内容转载自http://www.blogjava.net/robbie.html

2、事务的传播属性(Propagation)

  Propagation :key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。有以下选项可供使用:PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。

  PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
  PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
  PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
  PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
  PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。

  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标准中定义。

3、Spring事务的隔离级别(Isolation level)

  由隔离级别从低到高: 

  1. ISOLATION_DEFAULT:这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。
    另外四个与JDBC的隔离级别相对应
  2. ISOLATION_READ_UNCOMMITTED:这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。
    这种隔离级别会产生脏读,不可重复读和幻像读。
  3. ISOLATION_READ_COMMITTED: 保证一个事务不能读到另一个并行事务已修改但未提交的数据。数据提交后才能被读取。

    避免了脏数据。该级别适应于大多数系统。大多数主流数据库默认的级别。
  4. ISOLATION_REPEATABLE_READ:它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
        避免了脏读,不可重复读。但是可能出现幻像读。但是也带来更多的性能损失。
  5. ISOLATION_SERIALIZABLE 事务被处理为顺序执行。
        除了防止脏读,不可重复读外,还避免了幻像读。这是花费最高代价但是最可靠的事务隔离级别。

  什么是脏数据,脏读,不可重复读,幻觉读?  

  脏读: 指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。
    
  不可重复读: 指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。 那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
            
  幻觉读: 指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

  不可重复读与幻觉读区别:幻觉读与不可重复读有点相似,但是不可重复读读取的数据不一致是因为他所要取的数据集被改变了。但是phantom reads所要读的数据的不一致却不是他所要读的数据集改变,而是他的条件数据集改变。比如Select account.id where account.name="ppgogo*",第一次读去了6个符合条件的id,第二次读取的时候,由于事务b把一个帐号的名字由"dd"改成"ppgogo1",结果取出来了7个数据。

4、一个完整的常用的配置方便的事务配置例子

复制代码

    <!-- 配置dataSource -->
    <bean id="dataSource"  class="com.mchange.v2.c3p0.ComboPooledDataSource"  destroy-method="close">  
        <property name="driverClass" value="${driverClass}"></property>
        <property name="jdbcUrl" value="${jdbcUrl}"></property>
        <property name="user" value="${user}"></property>
        <property name="password" value="${password}"></property>
    </bean>
    
    <!-- 配置transactionManager -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
    
    <!-- 定义事务处理规则 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- 事务传播属性、隔离级别、最优化为只读事务、事务超时 -->
            <tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" read-only="true" timeout="-1"/>
        </tx:attributes>
    </tx:advice>
    
    <aop:config>
        <!-- 定义切面 -->
        <aop:pointcut id="pointcutId" expression="execution(* com.google.code..*.*Service.*(..))"/>
        <!-- 定义织入点规则 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcutId"/>
    </aop:config>

本文转载自:http://www.cnblogs.com/hellojava/archive/2012/11/21/2780694.html

共有 人打赏支持
只想一个人静一静
粉丝 15
博文 80
码字总数 148967
作品 0
其他
个人站长
私信 提问
Spring编程式和声明式事务实例讲解

Java面试通关手册(Java学习指南):https://github.com/Snailclimb/JavaGuide 历史回顾: 可能是最漂亮的Spring事务管理详解 Spring事务管理 Spring支持两种方式的事务管理: 编程式事务管理...

snailclimb
05/23
0
0
Spring声明式事务配置管理方法

环境配置 项目使用SSH架构,现在要添加Spring事务管理功能,针对当前环境,只需要添加Spring 2.0 AOP类库即可。添加方法: 点击项目右键->Build Path->Add librarys: 打开Add Libraries对话框...

思悟修
2015/04/08
0
0
分析 Spring 的编程式事务管理及声明式事务管理(转)

开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务。通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之。 先决条件...

君辰
2015/07/27
0
0
Spring编程式和声明式事务

1.编程式事务 1.1 编程式和声明式事务的区别 Spring提供了对编程式事务和声明式事务的支持,编程式事务允许用户在代码中精确定义事务的边界,而声明式事务(基于AOP)有助于用户将操作与事务...

梨加橙
06/19
0
0
Spring中的@Transactional(rollbackFor = Exception.class)属性详解

序言 今天我在写代码的时候,看到了。一个注解@Transactional(rollbackFor = Exception.class),今天就和大家分享一下,这个注解的用法; 异常 如下图所示,我们都知道Exception分为运行时异...

村里唯一的架构师
07/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

IC-CAD Methodology企业实战之openlava

在云计算解决安全问题并成为IC界主流运算平台之前,私有的服务器集群系统仍然是各大IC公司的计算资源平台首选。 现在主流的服务器集群管理系统包括lsf,openlava,SkyForm,三者都属于lsf一系...

李艳青1987
24分钟前
0
0
http response stream 字节流 接收与解码

在接收图片、音频、视频的时候,需要用到二进制流。 浏览器会发给客户端 字节Byte流,一串串的发过来_int8格式 -128~127(十进制),也就是8bit(位)。 客户端接收的时候,对接收到的字节收集,...

大灰狼wow
24分钟前
0
0
配置Tomcat监听80端口...

12月13日任务 16.4 配置Tomcat监听80端口 16.5/16.6/16.7 配置Tomcat虚拟主机 16.8 Tomcat日志 1.配置Tomcat监听80端口 示例一:自定义监听端口 vim /usr/local/tomcat/conf/server.xml 编辑...

hhpuppy
24分钟前
0
0
在ubuntu中配置java环境

先在官网下载一个jdk 进入root权限,避免之后出现创建文件失败或者修改文本失败的问题 sudo i 创建一个文件夹来放置jdk解压后的文件 mkdir 文件夹mv jdk1.9(你下载的jdk文件) 你创建 的文...

无极之岚
25分钟前
1
0
程序中设置MySQL的默认值

import com.alibaba.fastjson.JSON;import java.beans.PropertyDescriptor;import java.lang.annotation.*;import java.lang.reflect.Field;import java.lang.reflect.Method;impo......

laolin23
48分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部