spring的事务配置详解
接下来我将给大家介绍spring事务配置的两种方式:1.基于XML的事务配置。2.基于注解方式的事务配置。前言:在我们详细介绍spring的两种声明式事务管理之前,我们需要先理解这些概念1)spring的事务管理是通过Aop的方式来实现;2)声明式事务是spring对事务管理的最常用的方式,因为这种方式对代码的影响最小,因此也就符合非侵入式的轻量级的容器的概念;3)我们需要理解
接下来我将给大家介绍spring事务配置的两种方式:
1.基于XML的事务配置。2.基于注解方式的事务配置。
前言:在我们详细介绍spring的两种声明式事务管理之前,我们需要先理解这些概念
正文:
1.基于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);
}
</pre>对应于这个接口有一个实现类如下:<pre name="code" class="java">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();
}}
注:上述两端代码是对应于业务层的。我们平时配置事务也最好在业务层进行配置。
<!-- 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">
<!-- 首先我们要把服务对象fooService声明成一个bean -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- 然后是声明一个事物建议tx:advice,spring为我们提供了事物的封装,这个就是封装在了<tx:advice/>中 -->
<!-- <tx:advice/>有一个transaction-manager属性,我们可以用它来指定我们的事物由谁来管理。 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- 配置这个事务建议的属性 -->
<tx:attributes>
<!-- 指定所有get开头的方法执行在只读事务上下文中 -->
<tx:method name="get*" read-only="true"/>
<!-- 其余方法执行在默认的读写上下文中 -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- 我们定义一个切面,它匹配FooService接口定义的所有操作 -->
<aop:config>
<!-- <aop:pointcut/>元素定义AspectJ的切面表示法,这里是表示x.y.service.FooService包下的任意方法。 -->
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
<!-- 然后我们用一个通知器:<aop:advisor/>把这个切面和tx:advice绑定在一起,表示当这个切面:fooServiceOperation执行时tx:advice定义的通知逻辑将被执行 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</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>
<!-- other <bean/> definitions here -->
</beans>
<tx:advice/> 有关的设置
通过 <tx:advice/> 标签来指定不同的事务性设置。默认的 <tx:advice/> 设置如下:
事务传播设置是 REQUIRED
隔离级别是DEFAULT
事务是 读/写
事务超时默认是依赖于事务系统的,或者事务超时没有被支持。
任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚
这些默认的设置当然也是可以被改变的。 <tx:advice/> 和 <tx:attributes/> 标签里的 <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事务管理器起效。
到此为止基于XML的事务配置就算完成了。
使用 @Transactional
除了基于XML文件的声明式事务配置外,你也可以采用基于注解式的事务配置方法。直接在Java源代码中声明事务语义的做法让事务声明和将受其影响的代码距离更近了,而且一般来说不会有不恰当的耦合的风险,因为,使用事务性的代码几乎总是被部署在事务环境中。
下面的例子很好地演示了 @Transactional 注解的易用性,随后解释其中的细节。先看看其中的类定义
@Transactional
public class DefaultFooService implements FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}
<?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>
我们知道 @Transactional 注解可以声明在类上,也可以声明在方法上。在大多数情况下,方法上的事务会首先执行
例如: 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 注解的各种属性设置总结如下:
属性 | 类型 | 描述 |
枚举型: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
使用 @Transactional
除了基于XML文件的声明式事务配置外,你也可以采用基于注解式的事务配置方法。直接在Java源代码中声明事务语义的做法让事务声明和将受其影响的代码距离更近了,而且一般来说不会有不恰当的耦合的风险,因为,使用事务性的代码几乎总是被部署在事务环境中。
下面的例子很好地演示了 @Transactional 注解的易用性,随后解释其中的细节。先看看其中的类定义
@Transactional 有关的设置
@Transactional 注解是用来指定接口、类或方法必须拥有事务语义的元数据。 如:“当一个方法开始调用时就开启一个新的只读事务,并停止掉任何现存的事务”。 默认的 @Transactional 设置如下:
事务传播设置是 PROPAGATION_REQUIRED
事务隔离级别是 ISOLATION_DEFAULT
事务是 读/写
事务超时默认是依赖于事务系统的,或者事务超时没有被支持。
任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚
这些默认的设置当然也是可以被改变的。 @Transactional 注解的各种属性设置总结如下:
属性 | 类型 | 描述 |
枚举型: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
更多推荐
所有评论(0)