1.编程式事务管理
spring 编程式事务就是自己手动管理事务,spring 提供了 TransactionTemplate 这个类来手动管理事务的方式,使用如下 spring xml 配置
<!-- jdbc事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref local="dataSource" />
</property>
</bean>
<bean id="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager">
<ref local="transactionManager" />
</property>
<!--ISOLATION_DEFAULT 表示由使用的数据库决定 -->
<property name="isolationLevelName" value="ISOLATION_DEFAULT"/>
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
<!-- <property name="timeout" value="30"/> -->
</bean>
TransactionTemplate 编程式事务java 使用
public class SimpleService implements Service {
//注解进来
@autowrite
private final TransactionTemplate transactionTemplate;
public Object someServiceMethod() {
teturn transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
updateOperation1();
updateOperation2();
} catch (SomeBusinessExeption ex) {
status.setRollbackOnly(); //回滚
}
}
});
}
}
当然你也可以使用 PlatformTransactionManager 类,来手动管理事务,不同的是,java代码要写的比 transactionTemplate 多点,需要自己手动提交,手动回滚事务。
而 transactionTemplate 只关心是不是要回滚。其实transactionTemplate 内部实现就是用 PlatformTransactionManager 来实现的,2种方法是一样的,按个人爱好来选择
2.声明式事务管理
Spring Framework的声明式事务管理是建立在Spring的面向切面编程(aspect-oriented programming, AOP)上的.简单来说就是通过aop 方式来管理事务常,不需要手动提交,回滚操作。这种方式对应用代码的影响最小, 并且最符合一个非侵入型轻量级容器的理想.
2.1 基于xml aop 配置方式
一种是 aop 风格的配置,当然还有其他的变种方式,即不使用tx,aop 标签,实际上是一样的,标签最终也是要解析成bean的
基于aop 风格 xml 配置
<!-- 配置事务传播特性 -->
<tx:advice id="TestAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="REQUIRED"/>
<tx:method name="get*" propagation="REQUIRED"/>
<tx:method name="apply*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置参与事务的类 -->
<aop:config>
<!-- com.test.testAda.test.model.service 这个包下面的所有方法都被切入了事务-->
<aop:pointcut id="allTestServiceMethod" expression="execution(* com.test.testAda.test.model.service.*.*(..))"/>
<aop:advisor pointcut-ref="allTestServiceMethod" advice-ref="TestAdvice" />
</aop:config>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:advice/>
标签默认的设置是:
- 传播行为设置是
REQUIRED.
- 隔离等级是
DEFAULT.
- 事务是可读可写.
- 事务超时是使用系统底层组件的默认值, 在不支持超时的时候没有超时.
- 任何的
RuntimeException
均触发回滚, 并且检查的Exception
不会回滚
你可以修改默认的设置; <tx:advice/>
和<tx:attributes/>
标签所需要的<tx:method/>
标签的属性都整理在下面了:
<tx:method/> 标签设置
属性
|
是否必须
|
默认值
|
描述
|
---|---|---|---|
| 是 | 事务属性所关联的方法名称(可能不唯一). 通配符(*)可以用于表示一组相同的方法; 例如, | |
| 不是 | REQUIRED | 事务传播行为. |
| 不是 | DEFAULT | 事务隔离等级. |
| 不是 | -1 | 事务超时的值(以秒为单位). |
| 不是 | false | 事务是不是只读的? |
| 不是 | 会触发回滚的 | |
| 不是 | 不会触发回滚的 |
2.2基于注解的 @Transactional 配置
spring 注解事务
<!-- 使使用注解配置的事务行为生效 ,注解生效的前提请确保spring 能扫描到 @Transactional-->
<tx:annotation-driven transaction-manager="txManager"/>
<!-- 仍然需要一个PlatformTransactionManager -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- (这个需要的对象是在其他地方定义的) -->
<property name="dataSource" ref="dataSource"/>
</bean>
代码中注解事务的调用,类上注解和方法上注解
@Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// 该方法的设置更优先
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// do something
}
}
@Transactional 的属性
属性
|
类型
|
描述
|
---|---|---|
String | 指定事务管理器使用的可选限定符. | |
enum: | 指定传播属性设置. | |
| enum: | 指定隔离等级. |
| boolean | 设置事务是可读可写还是只读 |
| int (用秒作为粒度) | 事务超时. |
| | 指定 必定会触发回滚的类的数组. |
| 类名的数组. 类必须是继承自 | 指定 必定会触发回滚的异常类的数组. |
| | 指定 必定不会触发回滚的类的数组. |
| 类的字符串数组, 这些类都必须继承自 | 指定 必定不会触发回滚的异常类的数组. |
@Transactional使用注意
方法可见性和@Transactional
当使用代理时, 你应该只给public可见性的方法添加@Transactional
注解. 如果你给protected, private或者包访问 的方法添加了@Transactional
注解, 不会产生错误, 但是添加了注解的方法并没有真正的配置了事务. 如果你需要给非公开的方法添加注解 可以参考AspectJ
在代理模式下(默认值), 只有来自外部方法的调用才会被代理拦截. 这意味着自我调用, 在效果上是, 目标对象的一个方法调用了目标对象的另一个 方法, 不会导致产生运行期的事务, 即使被调用的方法被@Transactional
标记了.
以下是无效的事务注解
public class DefaultFooService{
public Foo getFoo(String fooName) {
updateFoo();
}
// 该方法的设置更优先
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo() {
// do something
}
public void main(String[] args){
DefaultFooService d=new DefaultFooService();
d.getFoo("a"); //间接调用(没有直接调用事务注解的那个方法)这样事务不生效的
}
}
3.spring 事务传播行为
事务传播行为类型 | 说明 |
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是 最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似的操作。 |
4.数据库事务隔离等级
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题
脏读(Dirty Read)
脏读意味着一个事务读取了另一个事务未提交的数据,而这个数据是有可能回滚
不可重复读(Unrepeatable Read)
不可重复读意味着,在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。这是由于查询时系统中其他事务修改的提交而引起的。
例如:事务B中对某个查询执行两次,当第一次执行完时,事务A对其数据进行了修改。事务B中再次查询时,数据发生了改变
幻读(phantom read)
幻读,是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样.
√会发生,× 不会发生 | 脏读 | 不可重复读 | 幻读 |
Read uncommitted | √ | √ | √ |
Read committed | × | √ | √ |
Repeatable read | × | × | √ |
Serializable | × | × | × |