声明式事务管理是spring对事务管理的最常用的方式,因为这种方式对代码的影响最小,因此也符合非侵入性的轻量级容器的概念。Spring的事务管理是通过AOP的方式来实现的,因为事务方面的代码与spring的绑定并以一种样板式结构使用。在理解spring声明式事务管理我们首先要理解他是通过AOP怎么具体实现的。其中的事务通知由元数据(目前基于xml和注解)驱动。代理对象由元数据结合产生一个新的代理对象。他使用一个PlatformTransactionManager实现配合TransactionInterceptor在方法调用之前实施事务。下面我们就通过一个图来看一下spring声明式事务管理的执行过程。

下面我们就以一个spring官方文档所给的例子来具体看一下用xml配置方式怎么来实现声明式事务管理:

首先请看下面的接口和它的实现。这个例子的意图是介绍概念:

// 我们想做成事务性的服务接口

[java] view plaincopy print?
  1. package x.y.service;
  2. public interface FooService {
  3. Foo getFoo(String fooName);
  4. Foo getFoo(String fooName, String barName);
  5. void insertFoo(Foo foo);
  6. void updateFoo(Foo foo);
  7. }

// 上述接口的一个实现

[java] view plaincopy print?
  1. package x.y.service;
  2. public class DefaultFooService implements FooService {
  3. public Foo getFoo(String fooName) {
  4. throw new UnsupportedOperationException();
  5. }
  6. public Foo getFoo(String fooName, String barName) {
  7. throw new UnsupportedOperationException();
  8. }
  9. public void insertFoo(Foo foo) {
  10. throw new UnsupportedOperationException();
  11. }
  12. public void updateFoo(Foo foo) {
  13. throw new UnsupportedOperationException();
  14. }}

首先要解释的是很多同学可能都在考虑这个事务管理到底是放在dao层还是放在service层呢。这个问题我想大多数童鞋的反应应该都是在dao层上吧,刚开始我也是这么想的。但是大家想想,如果我们要进行两个甚至多个dao层中的方法操作,并且要求放在同一个事务里时,我们该怎么来管理这个事务呢,这时我们就没办法了。所以我们应该把事务管理放在service层中,我们直接在service层中调用这两个dao层的方法就oK了。

下面我们接着往下看,我们假定,FooService的前两个方法(getFoo(String) 和getFoo(String, String))必须执行在只读事务上下文中,其他的方法(insertFoo(Foo)和 updateFoo(Foo))必须执行在可读写事务上下文中。我们根据这个要求来看一下配置文件,我们刚开始可能看不懂,不用慌,往下我们会一一解释的。

[html] view plaincopy print?
  1. <!-- from the file 'context.xml' -->
  2. <?xml version="1.0" encoding="UTF-8"?>
  3. <beans xmlns="http://www.springframework.org/schema/beans"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xmlns:aop="http://www.springframework.org/schema/aop"
  6. xmlns:tx="http://www.springframework.org/schema/tx"
  7. xsi:schemaLocation="
  8. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
  9. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
  10. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
  11. <!-- this is the service object that we want to make transactional -->
  12. <bean id="fooService" class="x.y.service.DefaultFooService"/>
  13. <!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
  14. <tx:advice id="txAdvice" transaction-manager="txManager">
  15. <!-- the transactional semantics... -->
  16. <tx:attributes>
  17. <!-- all methods starting with 'get' are read-only -->
  18. <tx:method name="get*" read-only="true"/>
  19. <!-- other methods use the default transaction settings (see below) -->
  20. <tx:method name="*"/>
  21. </tx:attributes>
  22. </tx:advice>
  23. <!-- ensure that the above transactional advice runs for any execution
  24. of an operation defined by the FooService interface -->
  25. <aop:config>
  26. <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
  27. <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
  28. </aop:config>
  29. <!-- don't forget the DataSource -->
  30. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  31. <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
  32. <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
  33. <property name="username" value="scott"/>
  34. <property name="password" value="tiger"/>
  35. </bean>
  36. <!-- similarly, don't forget the PlatformTransactionManager -->
  37. <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  38. <property name="dataSource" ref="dataSource"/>
  39. </bean>
  40. <!-- other <bean/> definitions here -->
  41. </beans>

好了,配置一大片,什么东西,我也看不懂,呵呵,没关系,一会大家就明白了,我们先来看一下官方给的解释,然后我在根据我自己的理解给大家通俗的解释一下这里的内容。

    我们要把一个服务对象('fooService' bean)做成事务性的。 我们想施加的事务语义封装在<tx:advice/>定义中。<tx:advice/> “把所有以 'get' 开头的方法看做执行在只读事务上下文中, 其余的方法执行在默认语义的事务上下文中”。 其中的 'transaction-manager' 属性被设置为一个指向 PlatformTransactionManager bean的名字(这里指 'txManager'), 该bean将会真正管理事务。配置中最后一段是 <aop:config/> 的定义, 它确保由 'txAdvice' bean定义的事务通知在应用中合适的点被执行。 首先我们定义了 一个切面,它匹配 FooService 接口定义的所有操作, 我们把该切面叫做 'fooServiceOperation'。然后我们用一个通知器(advisor)把这个切面与 'txAdvice' 绑定在一起, 表示当 'fooServiceOperation' 执行时,'txAdvice' 定义的通知逻辑将被执行。


好了,上面就是官方文档给出的这个配置文件的解释,不知道大家有没有看懂,反正对于初学者我的时候,我是真没看懂,不太容易懂,当然了,大牛们是一定能看懂的。下面我就根据我自己的理解来通俗的讲解一下。

首先我们应该要把服务对象'fooService' 声明成一个bean,我们要把一个服务对象('fooService' bean)做成事务性的。我们就应该首先在声明一个事务管理的建议,用什么来管理,spring给我们提供了事务封装,这个就封装在了<tx:advice/>中,这个事务建议给我们提供了一个transaction-manager属性,用他可以指定我们用谁来管理我们的事务。我们上边的例子用的为一个指向 PlatformTransactionManager bean的名字(这里指 'txManager'), 该bean将会真正管理事务。上面用的事务管理类是用的jdbc中提供的事务管理,当然这里也可以指定为hibernate管理。当然了,不管用那个类来管理我们的事务,都不要忘记了提供我们的datasource属性,因为事务管理也需要这里面的信息。我们声明好事务建议,也指定好了具体用哪个类来管理了,下面我们的任务就是要把我们定义好的这些利用AOP把我们的事务管理织入到我们的业务逻辑里面了。<aop:config/> 的定义, 它确保由 'txAdvice'  bean定义的事务通知在应用中合适的点被执行。 首先我们定义了 一个切面,它匹配 FooService 接口定义的所有操作, 我们把该切面叫做 'fooServiceOperation'。<aop:pointcut/> 元素定义是AspectJ的切面表示法,上述表示x.y.service.FooService包下的任意方法。然后我们用一个通知器(advisor)把这个切面与 'txAdvice' 绑定在一起, 表示当 'fooServiceOperation' 执行时,'txAdvice' 定义的通知逻辑将被执行。大体流程就是这样的了。

上面的配置将为'fooService' bean创建一个代理对象,这个代理对象被装配了事务通知,所以当它的相应方法被调用时,一个事务将被启动、挂起、被标记为只读,或者其它(根据该方法所配置的事务语义)。

我们来看看下面的例子,测试一下上面的配置。

[java] view plaincopy print?
  1. public final class Boot {
  2. public static void main(final String[] args) throws Exception {
  3. ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml", Boot.class);
  4. FooService fooService = (FooService) ctx.getBean("fooService");
  5. fooService.insertFoo (new Foo());
  6. }}

运行可以清楚的看到如下结果:

- Invoking rollback for transaction on x.y.service.FooService.insertFoo

due to throwable [Java.lang.UnsupportedOperationException]

<tx:advice/> 有关的设置

通过 <tx:advice/> 标签来指定不同的事务性设置。默认的 <tx:advice/> 设置如下:

事务传播设置是 REQUIRED

隔离级别是DEFAULT

事务是 读/写

事务超时默认是依赖于事务系统的,或者事务超时没有被支持。

任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚

这些默认的设置当然也是可以被改变的。 <tx:advice/> 和 <tx:attributes/> 标签里的 <tx:method/> 各种属性设置总结如下:

Table 9.1. <tx:method/> 有关的设置

属性

是否需要?

默认值

描述

name

与事务属性关联的方法名。通配符(*)可以用来指定一批关联到相同的事务属性的方法。 如:'get*'、'handle*'、'on*Event'等等。

propagation

REQUIRED

事务传播行为

isolation

DEFAULT

事务隔离级别

timeout

-1

事务超时的时间(以秒为单位)

read-only

false

事务是否只读?

rollback-for

将被触发进行回滚的 Exception(s);以逗号分开。 如:'com.foo.MyBusinessException,ServletException'

no-rollback-for

不 被触发进行回滚的 Exception(s);以逗号分开。 如:'com.foo.MyBusinessException,ServletException'

下面我们具体来看一下事务的传播性的几个值:

REQUIRED:业务方法需要在一个容器里运行。如果方法运行时,已经处在一个事务中,那么加入到这个事务,否则自己新建一个新的事务。
NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为他开启事务,如果方法在一个事务中被调用,该事务会被挂起,调用结束后,原先的事务会恢复执行。
REQUIRESNEW:不管是否存在事务,该方法总汇为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务挂起,新的事务被创建。
MANDATORY:该方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果在没有事务的环境下被调用,容器抛出例外。
SUPPORTS:该方法在某个事务范围内被调用,则方法成为该事务的一部分。如果方法在该事务范围外被调用,该方法就在没有事务的环境下执行。
NEVER:该方法绝对不能在事务范围内执行。如果在就抛例外。只有该方法没有关联到任何事务,才正常执行。
NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务 拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。

使用 @Transactional

除了基于XML文件的声明式事务配置外,你也可以采用基于注解式的事务配置方法。直接在Java源代码中声明事务语义的做法让事务声明和将受其影响的代码距离更近了,而且一般来说不会有不恰当的耦合的风险,因为,使用事务性的代码几乎总是被部署在事务环境中。

下面的例子很好地演示了 @Transactional 注解的易用性,随后解释其中的细节。先看看其中的类定义:

[java] view plaincopy print?
  1. // the service class that we want to make transactional
  2. @Transactional
  3. public class DefaultFooService implements FooService {
  4. Foo getFoo(String fooName);
  5. Foo getFoo(String fooName, String barName);
  6. void insertFoo(Foo foo);
  7. void updateFoo(Foo foo);
  8. }

当上述的POJO定义在Spring IoC容器里时,上述bean实例仅仅通过一 行xml配置就可以使它具有事务性的。如下:

[html] view plaincopy print?
  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:aop="http://www.springframework.org/schema/aop"
  5. xmlns:tx="http://www.springframework.org/schema/tx"
  6. xsi:schemaLocation="
  7. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
  8. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
  9. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
  10. <bean id="fooService" class="x.y.service.DefaultFooService"/>
  11. <tx:annotation-driven transaction-manager="txManager"/>
  12. <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  13. <property name="dataSource" ref="dataSource"/>
  14. </bean>
  15. </beans>

注意:  实际上,如果你用 'transactionManager' 来定义 PlatformTransactionManager bean的名字的话,你就可以忽略 <tx:annotation-driven/> 标签里的 'transaction-manager' 属性。 如果 PlatformTransactionManager bean你要通过其它名称来注入的话,你必须用 'transaction-manager' 属性来指定它。

在多数情形下,方法的事务设置将被优先执行。在下列情况下,例如: DefaultFooService 类在类的级别上被注解为只读事务,但是,这个类中的 updateFoo(Foo) 方法的 @Transactional 注解的事务设置将优先于类级别注解的事务设置。

[html] view plaincopy print?
  1. @Transactional(readOnly = true)
  2. public class DefaultFooService implements FooService {
  3. public Foo getFoo(String fooName) {
  4. // do something
  5. }
  6. // these settings have precedence for this method
  7. @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
  8. public void updateFoo(Foo foo) {
  9. // do something
  10. }
  11. }

@Transactional 有关的设置

@Transactional 注解是用来指定接口、类或方法必须拥有事务语义的元数据。 如:“当一个方法开始调用时就开启一个新的只读事务,并停止掉任何现存的事务”。 默认的 @Transactional 设置如下:

事务传播设置是 PROPAGATION_REQUIRED

事务隔离级别是 ISOLATION_DEFAULT

事务是 读/写

事务超时默认是依赖于事务系统的,或者事务超时没有被支持。

任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚

这些默认的设置当然也是可以被改变的。 @Transactional 注解的各种属性设置总结如下:

@Transactional 注解的属性

属性

类型

描述

propagation

枚举型:Propagation

可选的传播性设置

isolation

枚举型:Isolation

可选的隔离性级别(默认值:ISOLATION_DEFAULT)

readOnly

布尔型

读写型事务 vs. 只读型事务

timeout

int型(以秒为单位)

事务超时

rollbackFor

一组 Class 类的实例,必须是Throwable 的子类

一组异常类,遇到时 必须 进行回滚。默认情况下checked exceptions不进行回滚,仅unchecked exceptions(即RuntimeException的子类)才进行事务回滚。

rollbackForClassname

一组 Class 类的名字,必须是Throwable的子类

一组异常类名,遇到时 必须 进行回滚

noRollbackFor

一组 Class 类的实例,必须是Throwable 的子类

一组异常类,遇到时 必须不 回滚。

noRollbackForClassname

一组 Class 类的名字,必须是Throwable 的子类

一组异常类,遇到时 必须不 回滚

在写代码的时候,不可能对事务的名字有个很清晰的认识,这里的名字是指会在事务监视器(比如WebLogic的事务管理器)或者日志输出中显示的名字, 对于声明式的事务设置,事务名字总是全限定名+"."+事务通知的类的方法名。比如BusinessService类的handlePayment(..)方法启动了一个事务,事务的名称是:

com.foo.BusinessService.handlePayment

from: http://blog.csdn.net/csh624366188/article/details/7661909

Java程序员从笨鸟到菜鸟之(八十)细谈Spring(九)spring+hibernate声明式事务管理详解相关推荐

  1. Java程序员从笨鸟到菜鸟之(十)枚举,泛型详解

    一:首先从枚举开始说起 枚举类型是JDK5.0的新特征.Sun引进了一个全新的关键字enum来定义一个枚举类.下面就是一个典型枚举类型的定义: public enum Color{ RED,BLUE, ...

  2. java程序员从笨鸟到菜鸟(八)泛型

    1. 使用场景:一般是集合用的较多. 2. 使用规则: public static <E> void printArr(E [] inputArray) 所有泛型方法声明都有一个类型参数声 ...

  3. Java程序员从笨鸟到菜鸟全部博客目录

    本文来自:曹胜欢博客专栏.转载请注明出处:http://blog.csdn.NET/csh624366188 欢迎关注微信账号:java那些事:csh624366188.每天一篇java相关的文章 大 ...

  4. Java程序员从笨鸟到菜鸟之(序言)+全部链接

    http://blog.csdn.net/csh624366188 大学上了一年半,接触java也一年半了,虽然中间也有其他东西的学习,但是还是以java为主路线,想想这一年半,除去前半年几乎全玩了, ...

  5. Java程序员由笨鸟到菜鸟 电子版书正式发布 欢迎大家下载

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 欢迎关注 ...

  6. Java程序员从笨鸟到菜鸟之——总结和声明

    前段时间经过大约二十天的时间把javaSE的内容总结了一下,最近由于个人原因和一些乱七八糟的事情,一直没在更新,首先感谢那些一如既往支持我的朋友.最近不知道为什么,一直很懒,做东西也一直是效率很低,生 ...

  7. Java程序员从笨鸟到菜鸟之(五)java开发常用类(包装,数字处理集合等)(下)...

     本文来自:曹胜欢博客专栏.转载请注明出处:http://blog.csdn.net/csh624366188 写在前面:由于前天项目老师建设局的项目快到验收阶段,所以,前天晚上通宵,昨天睡了大半天, ...

  8. Java程序员从笨鸟到菜鸟之(一百零九)一步一步学习webservice(三)开发第一个基于XFire的webservice

    在日常开发中,常用的webservice引擎主要有Axis,axis2,Xfire以及cxf(Xfire的升级版).现在只有axis2和cxf官方有更新.今天我们先以一个比较老的引擎为例.来讲诉一下w ...

  9. Java程序员从笨鸟到菜鸟之(一百零八)一步一步学习webservice(二)webservice基本原理

    本来这第二篇打算讲解"开发第一个基于XFire的webservice"的内容来着.但是想想.开发实例只是局限于了会用的层面上.如果想真正的理解webservice还是需要挖掘其原理 ...

最新文章

  1. 败家玩意儿!Redis 竟然浪费了这么多内存!
  2. 笔面集锦:判断单链表里面是否有环及相关扩展题
  3. Android性能优化面试题集锦,终局之战
  4. 编译openwrt时报错:g++: internal compiler error: Killed (program cc1plus)
  5. 大龄开发人员如何破局
  6. Codeforces Round #722 (Div. 2)
  7. 树莓派Raspberry 操作GPIO--LED
  8. CentOS7 设置防火墙端口
  9. 《Java并发编程的艺术》笔记
  10. python7.2抛出自定义异常
  11. 曼昆微观经济学--十大原理
  12. mac定时备份mysql_定时备份mysql数据库
  13. flink读取不到文件_Flink读取本地文件
  14. 封装win7系统,安装系统时提示:windows无法完成系统配置,若要尝试恢复配置,请重新启动计算机?
  15. SQL——查询和1002号的同学学习的课程完全相同的其他同学的学号和姓名
  16. 设计并编写代码自动格斗类游戏
  17. properties文件乱码问题
  18. [ 工具 ] ___ VT : VMware Tools
  19. 已解决FileNotFoundError: [WinError 2] 系统找不到指定的文件。
  20. head first java勘误_《深入解析Oracle》一书勘误表

热门文章

  1. iOS 不要用文本编辑pod file
  2. 这家获得谷歌投资的企业想把亚马逊挑下神坛
  3. 深度学习之四:常用模型和方法
  4. 常见Web技术之间的关系,你了解多少?
  5. 使用Apriori算法进行关联分析
  6. 每日一博 - 使用环形队列实现高效的延时消息
  7. 深入理解分布式技术 - 配置中心
  8. Android日期分组,按查询分组在列表视图android中显示一些意...
  9. php 处理html,PHP解析HTML代码
  10. python学习笔记(六)——函数的作用域和装饰器