Spring事务——使用TransactionProxyFactoryBean创建事务代理

    Spring同时支持编程式事务策略和声明式事务策略,大部分时候,我们都推荐采用声明式事务策略。使用声明式事务策略的优势十分明显:

  • 声明式事务能大大降低开发者的代码书写量,而且声明式事务几乎不影响应用的代码。因此,不论底层事务策略如何变化,应用程序都无需任何改变
  • 应用程序代码无需任何事务处理代码,可以更专注于业务逻辑的实现
  • Spring可对任何POJO的方法提供事务管理,而且Spring的声明式事务管理无需容器的支持,可在任何环境下使用
  • EJB的CMT无法提供声明式回滚规则;而通过配置文件,Spring可指定事务在遇到特定异常时自动回滚。Spring不仅可在代码中使用setRollbackOnly回滚事务,也可在配置文件中配置回滚规则
  • 由于Spring采用AOP的方式管理事务,因此,可以在事务回滚动作中插入用户自己的动作,而不仅仅是执行系统默认的回滚

    本文主要介绍Spring中声明式事务管理的使用。

    在Spring1.X中,声明式事务使用TransactionProxyFactoryBean来配置事务代理Bean。正如它的类名所暗示的,它是一个专门为目标Bean生成事务代理的工厂Bean。既然TransactionProxyFactoryBean产生的是事务代理Bean,可见Spring的声明式事务策略是基于Spring AOP的。

    每个TransactionProxyFactoryBean为一个目标Bean生成一个事务代理Bean,事务代理的方法改写了目标Bean的方法,就是在目标Bean的方法执行之前加入开始事务,在目标Bean的方法正常结束之前提交事务,如果遇到特定异常则回滚。

    TransactionProxyFactoryBean创建事务代理时,需要了解当前事务所处的环境,该环境属性通过PlatformTransactionManager实例(其实现类的实例)传入,而相关事务规则则在该Bean定义中给出。下面是一个简单的持久化测试程序,该程序插入两条数据,这两条数据完全相同,将违反唯一键约束:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

package com.abc.dao.impl;

 

public class NewsDaoImpl implements NewsDao {

    private DataSource dataSource;

    public void setDataSource(DataSrouce dataSource) {

        this.dataSource = dataSource;

    }

    public void insert(String title, String content) {

        JdbcTemplate template = new JdbcTemplate(dataSource);

        template.update("insert into news_table values (....)");

        //两次相同的操作,将违反主键约束

        template.update("insert into news_table values (....)");

    }

}

    上面的程序中,两次update语句将会违反主键约束——该行代码将会引发异常,如果在没有事务的环境下,前一条代码会向数据库中插入一条记录;但如果在增加了事务控制的环境下,则这两条语句是一个整体,因为第二条语句插入失败将导致第一条插入的记录也被回滚。下面是在Spring配置文件中配置该测试程序,并使用TransactionProxyFactoryBean为它们配置事务代理:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

<!-- 使用C3P0数据库连接池作为数据源 -->

<bean id="dataSource" 

    class="com.mchange.v2.c3p0.ComboPolledDataSource" destroy-method="close">

    <property name="driverClass" value="com.mysql.jdbc.Driver" />

    <property name="jdbcUrl" value="jdbc:mysql://localhost/test" />

    <property name="user" value="root" />

    <property name="password" value="root" />

    <property name="maxPoolSize" value="40" />

    <property name="minPoolSize" value="4" />

    <property name="initialPoolSize" value="10" />

    <!-- 指定数据库连接池的连接的最大空闲时间 -->

    <property name="maxIdleTime" value="20" />

</bean>

 

<!-- 配置JDBC数据源的局部事务管理器,使用DataSourceTransactionManager类,该类实现了

PlatformTransactionManager接口,是针对采用数据源连接的特定实现 -->

<bean id="transactionManager" 

    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    <!-- 配置TransactionManager时需要注入数据源引用 -->

    <property name="dataSource" ref="dataSource" />

</bean>

 

<!-- 下面这个是前面定义的业务Bean -->

<bean id="newsDao" class="com.abc.dao.impl.NewsDaoImpl">

    <!-- 为业务Bean注入属性 -->

    <property name="dataSource" ref="dataSource" />

</bean>

 

<bean id="newsDaoTransProxy" 

    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

    <!-- 为事务代理工厂Bean注入事务管理器 -->

    <property name="transactionManager" ref="transactionManager" />

    <!-- 要在哪个Bean上面创建事务代理对象 -->

    <property name="target" ref="newsDao" />

    <!-- 指定事务属性 -->

    <property name="transactionAttributes">

        <props>

            <prop key="*">PROPAGATION_REQUIRED</prop>

        </props>

    </property>

</bean>

    上面的配置文件中定义了一个事务管理器transactionManager,该事务管理器是针对JDBC局部事务的特定实现类。配置事务代理(如上面的newsDaoTransProxy)时需要传入一个事务管理器,一个目标Bean,并指定该事务代理的事务属性。事务属性由transactionAttributes属性指定。上面事务属性只有一条事务传播规则,该规则制定对于所有方法都使用PROPAGATION_REQUIRED的传播规则。Spring支持的事务传播规则如下:

  • PROPAGATION_MANDATORY:要求调用该方法的线程必须处于事务环境中,否则抛出异常
  • PROPAGATION_NESTED:如果执行该方法的线程已处于事务环境下,依然启动新的事务,方法在嵌套的事务里执行。如果执行方法的线程为处于事务中,也启动新的事务,然后执行该方法,此时与PROPAGATION_REQUIRED相同
  • PROPAGATION_NEVER:不允许调用该方法的线程处于事务环境下,如果调用该方法的线程处于事务环境下,则抛出异常
  • PROPAGATION_NOT_SUPPORTED:如果调用该方法的线程处在事务中,则暂停当前事务,然后执行该方法
  • PROPAGATION_REQUIRED:要求在事务环境中执行该方法,如果当前执行的线程已处于事务中,则直接调用;如果当前执行线程不处于事务中,则启动新的事务后执行该方法
  • PROPAGATION_REQUIRES_NEW:要求在事务环境中执行该方法,如果当前执行的线程已处于事务中,则暂停当前事务,启动新事务后执行该方法;如果当前执行线程不处于事务中,则启动新的事务后执行该方法
  • PROPAGATION_SUPPORTS:如果当前执行线程处于事务中,则使用当前事务;不过不在事务中,则不使用事务

    主程序中主要获取了定义的NewsDao类型的Bean,并调用其insert方法,下面是主程序:

?


1

2

3

4

5

6

7

public class SpringTest {

    public static void main(String[] arg) {

        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        NewsDao dao = (NewsDao)context.getBean("newsDaoTransProxy",NewsDao.class);

        dao.insert("新闻标题","新闻内容");

    }

}

    上面的第4行中获取了newsDaoTransProxy Bean,该Bean已经不在是NewsDaoImpl类的实例了,它只是Spring容器创建的事务代理,该事务代理以NewsDaoImpl实例为目标对象,且该目标对象也实现了NewsDao接口(与NewsDaoImpl实现了相同的接口),故代理对象也可以当成NewsDao实例来使用。运行上面的程序,将出现一个异常,而且insert方法所执行的两条SQL语句全部回滚——因为事务控制的缘故。

    当我们使用TransactionProxyFactoryBean为目标Bean配置了事务代理以后,SpringAOP将会把负责事务操作的增强处理织入目标Bean的业务方法当中。事实上,Spring不仅支持对接口的代理,整合CGLIB后,Spring甚至可以对具体类生成代理,只要设置proxyTargetClass属性为true即可。如果目标Bean没有实现任何接口,proxyTargetClass属性默认被设为true,此时Spring会对具体类生成代理。当然通常建议面向接口编程,而不要面向具体的实现类编程。

时间: 2016-05-09
Tags: 代理, spring, 事务

Spring事务——使用TransactionProxyFactoryBean创建事务代理的相关文章

Spring AOP实现声明式事务代码分析

 众所周知,Spring的声明式事务是利用AOP手段实现的,所谓"深入一点,你会更快乐",本文试图给出相关代码分析.   AOP联盟为增强定义了org.aopalliance.aop.Advice接口,Spring由Advice接口扩展了5中类型的增强(接口),AOP联盟自身提供了IntroductionInterceptor->MethodInterceptor->Interceptor->Advice,而MethodInterceptor就代表环绕增强,表示在目标

Spring的事务和Hibernate的事务有什么区别

问题描述 想了解一下Spring的事务和Hibernate的事务有什么区别?Hibernate的crud都用到了Transaction,在方法开始时,打开事务,然后结束时提交事务,当Hb与Sp协作时,对于一个运行在服务层且在spring事务中的方法A,调用了运行在Dao层的方法B和C,B,C的操作是通过hb实现的,我就是想不通如下两点:1A方法调用B后,Hb事务不是提交了吗,也就是事务上下文在这里就中断了,如果再调用C,怎么能保证b和c在一个事务环境中呢?2我知道hb的事务控制是通过jdbc的,

深入解析Java的Spring框架中的混合事务与bean的区分_java

混合事务在ORM框架的事务管理器的事务内,使用JdbcTemplate执行SQL是不会纳入事务管理的. 下面进行源码分析,看为什么必须要在DataSourceTransactionManager的事务内使用JdbcTemplate. 1.开启事务DataSourceTransactionManager protected void doBegin(Object transaction,TransactionDefinition definition) { DataSourceTransactio

mybatis spring 整合 junit测试。 事务不起作用,不提交。删除无效???

问题描述 mybatis spring 整合 junit测试. 事务不起作用,不提交.删除无效??? applicationContext.xml 中的数据库和 sessionFactory以及事务配置 Xml代码 <bean id="bssDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- Connec

sql2000数据库-msssql2000创建事务发布时,很多表无法选择发布,这是什么原因?应该怎么解决。

问题描述 msssql2000创建事务发布时,很多表无法选择发布,这是什么原因?应该怎么解决. msssql2000创建事务发布时,很多表无法选择发布.但是快照发布和合并发布都可以全选.

spring学习笔记(20)数据库事务并发与锁详解

多事务运行并发问题 在实际应用中,往往是一台(或多台)服务器向无数客户程序提供服务,当服务器查询数据库获取数据时,如果没有采用必要的隔离机制,可能会存在数据库事务的并发问题,下面是一些常见的并发问题分类: 1. 第一类丢失更新:撤销一个事务,其他事务已提交的更新数据覆盖 2. 第二类丢失更新:一个事务覆盖另一个事务已提交的更新数据 3. 脏读:一个事务读到另一个事务未提交的数据 4. 虚读:一个事物读到另一个已提交的新插入数据 5. 不可重复读:事务读到另一个事务已提交的更新数据 下面对这几类并

软件事务内存导论(四)创建事务

创建事务 我们创建事务的目的是为了协调针对多个托管引用的变更.事务将会保证这些变更是原子的,也就是说,所有的托管引用要么全部被提交要么全部被丢弃,所以在事务之外我们将不会看到有任何局部变更(partial changes)出现.此外,我们也可以用创建事务的方式来解决对单个ref先读后写所引发的相关问题. Akka是用Scala开发出来的,所以如果我们工作中用的是Scala的话,就可以直接幸福地享用Akka简洁明了的API了.对于那些日常工作中不能使用Scala开发的程序员,Akka同样也提供了一

spring mvc+spring3+hibernate4的事务不会自动清楚session缓存

问题描述 spring mvc+spring3+hibernate4的事务不会自动清楚session缓存 没有手动清缓存,就不执行插入SQL了手动清缓存的话,就能添加成功. 有知道原因的大神吗!!! 解决方案 没遇到过这种情况,tomcat么 解决方案二: 你这个没有加上事务,save时不会自动flush,加了事务之后commit时会自动flush.你配置事务了吗? 解决方案三: 贴出事务来看看,会不会没起作用

调用XA操作异常,无法重新创建事务参与者的 XAResource

问题描述 --在Was后台日志,频繁报下述错误,求各位大神分析下,谢谢![14-12-189:52:15:262CST]00000013XARecoveryDatAWTRN0151I:正在准备对XAResource:cells/SHH-HQ-PMS01SCell01/clusters/CmdpmsCluster/resources.xml#J2CResourceAdapter_1331453023382调用XA恢复操作[14-12-189:52:15:286CST]00000037SystemO