spring 注解事务

el/2024/7/13 10:39:12

<!-- 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"/>

<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="txManager"/>

<!-- a PlatformTransactionManager is still required -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- (this dependency is defined somewhere else) -->
<property name="dataSource" ref="dataSource"/>
</bean>

<!-- other <bean/> definitions here -->



方法的可见度和 @Transactional

在使用代理的时候,@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,系统也不会报错, 但是这个被注解的方法将不会执行已配置的事务设置。如果你非要注解非公共方法的话,请参考使用AspectJ


<tx:annotation-driven/> 设置

属性 默认值 描述
transaction-manager transactionManager 使用的事务管理器的名字。只有像在上面的例子那样,事务管理器不是 transactionManager的情况下才需要。

mode proxy 默认的模式“proxy”会用Spring的AOP框架来代理注解过的bean(就像在前面讨论过的那样, 下面代理的语义只对通过代理传递过来的方法调用起效)。 另一种可行的模式"aspectj"会使用Spring的AspectJ事务切面来编织类(通过修改目标对象的字节码应用到任何方法调用上)。 AspectJ织入需要在classpath中有spring-aspects.jar这个文件,并且启用装载时织入 (或者编译时织入)。

proxy-target-class false 只对代理模式有效。决定为那些使用了@Transactional注解的类创建何种事务代理。 如果 "proxy-target-class" 属性被设为 "true", 那么基于类的代理就会被创建。如果 "proxy-target-class"属性被设为"false" 或者没设,那么基于接口的标准JDK代理就会被创建。

order Ordered.LOWEST_PRECEDENCE 定义事务通知的顺序会作用到使用@Transactional注解的bean上。 更多的关于AOP通知顺序的定义可以在章节 请注意如果不指定任何顺序将会把决定权交给AOP子系统。



注意
在<tx:annotation-driven/>元素上的"proxy-target-class" 属性 控制了有什么类型的事务性代理会为使用@Transactional 来注解的类创建代理。 如果"proxy-target-class" 属性被设为"true",那么基于类的代理就会被创建。 如果"proxy-target-class" 属性被设为"false" 或者没设,那么会创建基于接口的标准JDK代理。

在多数情形下,方法的事务设置将被优先执行。在下列情况下,例如: 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
事务传播
请注意这部分的Spring参考文档不是 事务传播的介绍, 而是详细介绍了在Spring中与事务传播相关的一些语义。

在由Spring管理的事务中,请记住 物理 和 逻辑 事务存在的差异, 以及传播设置是如何影响到这些差异的。

required


PROPAGATION_REQUIRED


当事务传播被设置PROPAGATION_REQUIRED的时候, 会为每一个被应用到的方法创建一个逻辑事务作用域。 每一个这样的逻辑事务作用域都可以自主地决定rollback-only状态,当这样的逻辑事务作用域被外部的一个逻辑事务作用域所包含的时候, 他们在逻辑上是独立的。当然了,对于正常的 PROPAGATION_REQUIRED设置来说,他会被映射到相同的物理事务上。 所以一个标记有rollback-only的内部逻辑事务作用域的确会影响到外部的逻辑事务作用域(就像你所预料的那样)。

然而,当内部的事务作用域标记为rollback-only,同时外部的事务作用域并没有决定要回滚, 这样的回滚是意料不到的(静悄悄地由内部事务作用域触发的): 一个对应的UnexpectedRollbackException 异常会在这个时候被抛出。这是 可以预料到的行为, 只有这样,这个事务的调用者才不会被误导,在事务没有提交的情况下误以为事务已经提交。所以如果内部的事务(外部的调用者并不知情)标记该事务为 rollback-only,而外部的调用者却依旧在不知情的情况下提交后,它需要收到一个 UnexpectedRollbackException 异常来清楚的了解事务并没有提交而是发生了回滚。

RequiresNew


PROPAGATION_REQUIRES_NEW


PROPAGATION_REQUIRES_NEW,与之前相反,为每一个相关的事务作用域使用了一个完全 独立的事务。在这种情况下,物理事务也将是不同的,因此外部事务可以不受内部事务回滚状态的影响独立提交或者回滚。

Nested
PROPAGATION_NESTED 是一个完全不同的设置。它使用了一个单独的物理事务, 这个事务拥有多个可以回滚的保存点。这样部分回滚允许内部事务在它的作用域内触发一个回滚, 并且外部事务能够不受影响的继续。 这通常是对应于JDBC的保存点,所以只会在 JDBC 资源事务管理上起效 (具体请参见 Spring的DataSourceTransactionManager).

通知事务操作
考虑一下这样的情况,如果你希望 同时执行事务性通知(advice)和一些基本的剖析(profiling)通知。 那么,在<tx:annotation-driven/>环境中该怎么做?

我们调用 updateFoo(Foo) 方法时希望这样:

配置的剖析切面(profiling aspect)开始启动,

然后进入事务通知(根据配置创建一个新事务或加入一个已经存在的事务),

然后执行原始对象的方法,

然后事务提交(我们假定这里一切正常),

最后剖析切面报告整个事务方法执行过程花了多少时间。


这里有一份简单的剖析切面(profiling aspect)的代码。

package x.y;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;
import org.springframework.core.Ordered;

public class SimpleProfiler implements Ordered {

private int order;

// allows us to control the ordering of advice
public int getOrder() {
return this.order;
}

public void setOrder(int order) {
this.order = order;
}

// this method is the around advice
public Object profile(ProceedingJoinPoint call) throws Throwable {
Object returnValue;
StopWatch clock = new StopWatch(getClass().getName());
try {
clock.start(call.toShortString());
returnValue = call.proceed();
} finally {
clock.stop();
System.out.println(clock.prettyPrint());
}
return returnValue;
}
}




<?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"/>

<!-- this is the aspect -->
<bean id="profiler" class="x.y.SimpleProfiler">
<!-- execute before the transactional advice (hence the lower order number) -->
<property name="order" value="1"/>
</bean>

<tx:annotation-driven transaction-manager="txManager" order="200"/>

<aop:config>
<!-- this advice will execute around the transactional advice -->
<aop:aspect id="profilingAspect" ref="profiler">
<aop:pointcut id="serviceMethodWithReturnValue"
expression="execution(!void x.y..*Service.*(..))"/>
<aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>
</aop:aspect>
</aop:config>

<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>

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

</beans>

上面配置的结果将获得到一个拥有剖析和事务方面的 按那样的顺序 应用于它上面的 'fooService' 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"/>

<!-- the profiling advice -->
<bean id="profiler" class="x.y.SimpleProfiler">
<!-- execute before the transactional advice (hence the lower order number) -->
<property name="order" value="1"/>
</bean>

<aop:config>

<aop:pointcut id="entryPointMethod" expression="execution(* x.y..*Service.*(..))"/>

<!-- will execute after the profiling advice (c.f. the order attribute) -->
<aop:advisor
advice-ref="txAdvice"
pointcut-ref="entryPointMethod"
order="2"/> <!-- order value is higher than the profiling aspect -->

<aop:aspect id="profilingAspect" ref="profiler">
<aop:pointcut id="serviceMethodWithReturnValue"
expression="execution(!void x.y..*Service.*(..))"/>
<aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>
</aop:aspect>

</aop:config>

<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>

<!-- other <bean/> definitions such as a DataSource and a PlatformTransactionManager here -->

</beans>

上面配置的结果是创建了一个 'fooService' bean,剖析方面和事务方面被 依照顺序 施加其上。如果我们希望剖析通知在目标方法执行之前 后于 事务通知执行,而且在目标方法执行之后 先于 事务通知,我们可以简单地交换两个通知bean的order值。

如果配置中包含更多的方面,它们将以同样的方式受到影响。

结合AspectJ使用 @Transactional
通过AspectJ切面,你也可以在Spring容器之外使用Spring框架的 @Transactional 功能。要使用这项功能你必须先给相应的类和方法加上 @Transactional注解,然后把 spring-aspects.jar 文件中定义的 org.springframework.transaction.aspectj.AnnotationTransactionAspect 切面连接进(织入)你的应用。同样,该切面必须配置一个事务管理器。你当然可以通过Spring框架容器来处理注入,但因为我们这里关注于在Spring容器之外运行应用,我们将向你展示如何通过手动书写代码来完成。


// construct an appropriate transaction manager
DataSourceTransactionManager txManager = new DataSourceTransactionManager(getDataSource());

// configure the AnnotationTransactionAspect to use it; this must be done before executing any transactional methods
AnnotationTransactionAspect.aspectOf().setTransactionManager(txManager);
注意
使用此切面(aspect),你必须在 实现 类(和/或类里的方法)、而 不是 类的任何所实现的接口上面进行注解。AspectJ遵循Java的接口上的注解 不被继承 的规则。

定义在类上的 @Transactional 注解指定了类中所有方法执行时的默认事务语义。

定义在类的方法上的 @Transactional 注解将覆盖掉类上注解的所指定的默认事务语义(如过存在的话)。 所有的方法都可以注解,不管它的可见度是什么样的。

要把 AnnotationTransactionAspect 织入你的应用,你或者基于AspectJ构建你的应用

http://www.ngui.cc/el/5557643.html

相关文章

fedora 内核更新

在安装一 个新的fedora发行版本后,可以选择升级所有的软件,也可以只升级下fedora编译过的内核包,在测试完新的内核后,再把旧的内核包卸载掉,节约空 间。这里只探讨fedora的自动在线升级方式,没有使用手动的方式,也即意味着会自动搞定vmlinuz,initrd.img,system.map 等文…

jquery encoding

客户端&#xff1a; $.ajax({ url:${logStatisticByType}, data:{"ids":encodeURI(ids)}, type:"get", success:function(){alert("xxx");} }); 在服务端&#xff1a; String ids CommonManage.getParam("ids"); try { ids java.n…

El表达式得到list的长度

在jsp页面中不能通过${list.size}取列表长度&#xff0c;而是 <% taglib uri"http://java.sun.com/jsp/jstl/core" prefix"c"%> <% taglib prefix"fn" uri"http://java.sun.com/jsp/jstl/functions"%> list的长度是&…

jstl 格式化标签

需要引入格式化标签库 <% taglib prefix”fmt” uri”http://java.sun.com/jsp/jstl/fmt”%> 一&#xff0c;格式化日期 Value&#xff1a;表示的是日期 Type&#xff1a;表示的是日期显示的格式&#xff0c;date表示显示时间&#xff0c;time表示显示日期&#xff0c;bo…

Mssql未能加载文件或程序集“Microsoft.SqlServer.Sqm,

未能加载文件或程序集“Microsoft.SqlServer.Sqm, Version10.0.0.0, Cultureneutral, PublicKeyToken89845dcd8080cc91”或它的某一个依赖项。系统找不到指定的文件。 (SqlMgmt) ------------------------------ 程序位置: 在 Microsoft.SqlServer.Management.SqlMgmt.SqmHelp…

struts2 效验文件模板

struts2中的数据校验文件配置 <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd&quo…

struts2格式化日期和时间

struts2格式化日期和时间struts2 中的默认的日期输出并不符合我们的中文日常习惯.以下是我知道的在struts2中进行日期格式化输出的几种方式. 1.利用 <s:date>进行格式化.如: <s:date name"birthday" format"yyyy-MM-dd"/>就是将属性birthday按…

Mssql 时间日期格式化

1 取值后格式化{0:d}小型&#xff1a;如2005-5-6{0:D}大型&#xff1a;如2005年5月6日{0:f}完整型2 当前时间获取 DateTime.Now.ToShortDateString3 取值中格式化SQL Server里面可能经常会用到的日期格式转换方法:sql server使用convert来取得datetime日期数据&#xff0c;以下…

js复制到剪切板

function copytoclip(obj){var objtestobj.innerText; window.clipboardData.setData(text, objtest); alert("复制成功");}

js 得到对象的宽度和高度

//绝对宽度Obj.offsetWidth//绝对高度Obj.offsetHeight以下是获取窗口对象的宽高值。clientHeight 获取对象的高度&#xff0c;不计算任何边距、边框、滚动条&#xff0c;但包括该对象的补白。clientLeft 获取 offsetLeft 属性和客户区域的实际左边之间的距离。clientT…