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

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               


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

                                                   

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


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

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

package x.y.service;public interface FooService Foo getFoo(String fooName)Foo getFoo(String fooName, String barName)void insertFoo(Foo foo)void updateFoo(Foo foo);}


// 上述接口的一个实现

package x.y.service;public class DefaultFooService implements FooService public Foo getFoo(String fooName) {    throw new UnsupportedOperationException();  }  public Foo getFoo(String fooName, String barName) {    throw new UnsupportedOperationException();  }  public void insertFoo(Foo foo) {    throw new UnsupportedOperationException();  }  public void updateFoo(Foo foo) {    throw new UnsupportedOperationException();  }}


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

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

<!-- from the file 'context.xml' --><?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: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/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">    <!-- this is the service object that we want to make transactional -->  <bean id="fooService" class="x.y.service.DefaultFooService"/>  <!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->  <tx:advice id="txAdvice" transaction-manager="txManager">  <!-- the transactional semantics... -->  <tx:attributes>    <!-- all methods starting with 'get' are read-only -->    <tx:method name="get*" read-only="true"/>    <!-- other methods use the default transaction settings (see below) -->    <tx:method name="*"/>  </tx:attributes>  </tx:advice>    <!-- ensure that the above transactional advice runs for any execution    of an operation defined by the FooService interface -->  <aop:config>  <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>  <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>  </aop:config>    <!-- don't forget the DataSource -->  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">  <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>  <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>  <property name="username" value="scott"/>  <property name="password" value="tiger"/>  </bean>  <!-- similarly, don't forget the PlatformTransactionManager -->  <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  <property name="dataSource" ref="dataSource"/>  </bean>   <!-- other <bean/> definitions here --></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创建一个代理对象,这个代理对象被装配了事务通知,所以当它的相应方法被调用时,一个事务将被启动、挂起、被标记为只读,或者其它(根据该方法所配置的事务语义)。

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

public final class Boot public static void main(final String[] args) throws Exception {    ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml", Boot.class);    FooService fooService = (FooService) ctx.getBean("fooService");    fooService.insertFoo (new Foo());  }}


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

- 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 注解的易用性,随后解释其中的细节。先看看其中的类定义:

// the service class that we want to make transactional@Transactionalpublic class DefaultFooService implements FooService Foo getFoo(String fooName)Foo getFoo(String fooName, String barName)void insertFoo(Foo foo)void updateFoo(Foo foo);}


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

<?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: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/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">    <bean id="fooService" class="x.y.service.DefaultFooService"/>   <tx:annotation-driven transaction-manager="txManager"/>   <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">   <property name="dataSource" ref="dataSource"/>  </bean></beans>



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


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

@Transactional(readOnly = true)public class DefaultFooService implements FooService {  public Foo getFoo(String fooName) {    // do something  }    // these settings have precedence for this method    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)    public void updateFoo(Foo foo) {        // do something           }}


@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

           

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

这里写图片描述
你好! 这是你第一次使用 **Markdown编辑器** 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计 ,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法1 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G

合理的创建标题,有助于目录的生成

直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

如何改变文本的样式

强调文本 强调文本

加粗文本 加粗文本

标记文本

删除文本

引用文本

H2O is是液体。

210 运算结果是 1024.

插入链接与图片

链接: link.

图片: Alt

带尺寸的图片: Alt

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

如何插入一段漂亮的代码片

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block var foo = 'bar'; 

生成一个适合你的列表

  • 项目
    • 项目
      • 项目
  1. 项目1
  2. 项目2
  3. 项目3
  • 计划任务
  • 完成任务

创建一个表格

一个简单的表格是这么创建的:

项目 Value
电脑 $1600
手机 $12
导管 $1

设定内容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列 第二列 第三列
第一列文本居中 第二列文本居右 第三列文本居左

SmartyPants

SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

TYPE ASCII HTML
Single backticks 'Isn't this fun?' ‘Isn’t this fun?’
Quotes "Isn't this fun?" “Isn’t this fun?”
Dashes -- is en-dash, --- is em-dash – is en-dash, — is em-dash

创建一个自定义列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何创建一个注脚

一个具有注脚的文本。2

注释也是必不可少的

Markdown将文本转换为 HTML

KaTeX数学公式

您可以使用渲染LaTeX数学表达式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n 1 ) ! n N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N 是通过欧拉积分

Γ ( z ) = 0 t z 1 e t d t &ThinSpace; . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,.

你可以找到更多关于的信息 LaTeX 数学表达式here.

新的甘特图功能,丰富你的文章

gantt
        dateFormat  YYYY-MM-DD
        title Adding GANTT diagram functionality to mermaid
        section 现有任务
        已完成               :done,    des1, 2014-01-06,2014-01-08
        进行中               :active,  des2, 2014-01-09, 3d
        计划一               :         des3, after des2, 5d
        计划二               :         des4, after des3, 5d
  • 关于 甘特图 语法,参考 这儿,

UML 图表

可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::

张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

这将产生一个流程图。:

链接
长方形
圆角长方形
菱形
  • 关于 Mermaid 语法,参考 这儿,

FLowchart流程图

我们依旧会支持flowchart的流程图:

  • 关于 Flowchart流程图 语法,参考 这儿.

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件或者.html文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. mermaid语法说明 ↩︎

  2. 注脚的解释 ↩︎

猜你喜欢

转载自blog.csdn.net/uffvhj/article/details/84098909