Spring Transaction Management
Spring框架为事务管理提供了一致的抽象,具有跨不同事务API(例如Java事务API(JTA),JDBC,Hibernate和Java Persistence API(JPA))的一致编程模型,支持声明式事务管理,有比JTA更加简单的编程式API,能与Spring的数据访问抽象的出色集成。
什么是事务?
事务是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行; 事务是一组不可再分割的操作集合(工作逻辑单元);
事务的四大特性:
1.原子性: 事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做。
2.一致性:最终结果保持相同,比如A给B转账100,A账户-100,B账户 +100,A和B账户总和要和转账之前AB总和保持相同。
3.隔离性:一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
4.持久性: 也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
事务的隔离级别:
隔离级别是指若干个并发的事务之间的隔离程度,Spring 在TransactionDefinition 这个接口 定义了五个隔离级别:
(1) ISOLATION_DEFAULT:默认的隔离级别,默认你接入的数据库默认隔离级别。一般默认的是READ_COMMITTED,幻读。其他的四个对应JDBC的隔离级别。
(2) ISOLATION_READ_UNCOMMITTED :指示可能发生脏读、不可重复读取和幻象读取。此级别允许一个事务更改的行,在提交该行中的任何更改之前,被另一个事务读取(“脏读”)。如果回滚了任何更改,则第二个事务将检索到无效行。这个隔离级别一般不会用。
(3) ISOLATION_READ_COMMITTED:可以阻止脏读取;但是可能发生不可重复读取和幻象读取。此级别仅禁止事务读取包含未提交更改的行。
(4) ISOLATION_REPEATABLE_READ:可以防止脏读和不可重复读取;可能会发生幻象读取。此级别禁止事务读取包含未提交更改的行,并且还禁止一个事务读取行,第二个事务更改行,第一个事务重新读取行,第二次获得不同值的情况(“不可重复读取”)。该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。幻读就是你读的过程中,别的事务对你读的内容进行修改,导致你多次查询的数据不一致。
(5)ISOLATION_SERIALIZABLE:可以防止脏读、不可重复读和幻象读。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
隔离级别越高,性能越差,所以大部分默认是幻读,READ_COMMITTED。
Spring 的事务传播特性:
(1) PROPAGATION_REQUIRED:支持当前事务,如果没有事务就创建新的事务。默认的传播特性。
(2)PROPAGATION_SUPPORTS:支持当前事务,如果没有事务,就以没有事务的方式执行。
(3)PROPAGATION_MANDATORY:支持当前事务,如果事务不存在就抛出异常。
(4)PROPAGATION_REQUIRES_NEW:创建新事务,如果有事务就暂停当前事务。
(5)PROPAGATION__NOT_SUPPORTS:不支持当前事务,总是以非事务方式执行。
(6)PROPAGATION_NEVER:不支持当前事务,如果事务存在就抛出异常。
(7)PROPAGATION_NESTED:如果存在当前事务,则在嵌套事务中执行。
Spring 的事务管理:
Spring 的事务管理是通过 PlatformTransactionManager这个接口来实现的。
Spring 的事务管理分为两种:声明式事务,编程式事务。
一、声明式事务:
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-->配置JDBC数据源</!-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-->将数据源注入到platformTransactionManager事务管理器中</!-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-->设置事务通知</!-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="say" read-only="true" isolation="READ_COMMITTED"/>
</tx:attributes>
</tx:advice>
<!-->通过aop去配置事务通知</!-->
<aop:config>
<aop:pointcut id="addLogin" expression="execution(* test.Service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="addLogin"/>
</aop:config>
</beans>
(1) 回滚声明式事务
一、声明配置那些异常抛出,事务会标记回滚
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
二、声明配置事务不回滚
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
三、事务回滚根据配置的最大范围的匹配规则执行
这里会除了InstrumentNotFoundException异常外,其他的异常都会回滚
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
</tx:attributes>
</tx:advice>
四、编程式事务回滚
代码侵入性强,耦合紧,不建议使用。
public void resolvePosition() {
try {
// some business logic...
} catch (NoProductInStockException ex) {
// trigger rollback programmatically
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
(2) 为不同的bean 设置不同的事务
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:config>
<aop:pointcut id="defaultServiceOperation"
expression="execution(* x.y.service.*Service.*(..))"/>
<aop:pointcut id="noTxServiceOperation"
expression="execution(* x.y.service.ddl.DefaultDdlManager.*(..))"/>
<aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/>
<aop:advisor pointcut-ref="noTxServiceOperation" advice-ref="noTxAdvice"/>
</aop:config>
<!-- this bean will be transactional (see the 'defaultServiceOperation' pointcut) -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- this bean will also be transactional, but with totally different transactional settings -->
<bean id="anotherFooService" class="x.y.service.ddl.DefaultDdlManager"/>
<tx:advice id="defaultTxAdvice">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<tx:advice id="noTxAdvice">
<tx:attributes>
<tx:method name="*" propagation="NEVER"/>
</tx:attributes>
</tx:advice>
<!-- other transaction infrastructure beans such as a TransactionManager omitted... -->
</beans>
(3) tx:method/ 设置
属性 | 必须的? | 默认 | 描述 |
---|---|---|---|
name | Yes | 与事务属性关联的方法名。通配符()可以用于将相同的事务属性设置与许多方法(例如get、handle*、on*Event等)关联起来。 | |
propagation | NO | REQUIRED | 事务传播行为。 |
isolation | NO | DEFAULT | 事务隔离级别,仅适用于设置事务传播时的REQUIRED或者REQUIRED_NEW。 |
timeout | NO | -1 | 事务超时,仅适用于设置事务传播时的REQUIRED或者REQUIRED_NEW。 |
read-only | NO | false | 读写与只读事务。仅适用于设置事务传播时的REQUIRED或者REQUIRED_NEW。 |
rollback-for | NO | 触发事务回滚的异常实例设置。 | |
no-rollback-for | No | 不触发事务回滚的实例设置。 |
(4)使用@Transational注解
一、在类上加@Transactionnal代表类中所有的方法都具有事务
@Transactional
public class DefaultFooService implements FooService {
Publisher<Foo> getFoo(String fooName) {
// ...
}
Mono<Foo> getFoo(String fooName, String barName) {
// ...
}
Mono<Void> insertFoo(Foo foo) {
// ...
}
Mono<Void> updateFoo(Foo foo) {
// ...
}
}
二、在方法上加@Transactionnal代表该方法具有事务
public class DefaultFooService implements FooService {
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
}
}
二、编程式事务:
(1) TransactionTemplate使用
TransactionTemplate类的实例是线程安全的,因为实例不维护任何会话状态。不过,TransactionTemplate实例确实保持配置状态。因此,虽然许多类可能共享TransactionTemplate的一个实例,但如果一个类需要使用具有不同设置(例如,不同的隔离级别)的TransactionTemplate,则需要创建两个不同的TransactionTemplate实例。
public class SimpleService implements Service {
// single TransactionTemplate shared amongst all methods in this instance
private final TransactionTemplate transactionTemplate;
// use constructor-injection to supply the PlatformTransactionManager
public SimpleService(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
public Object someServiceMethod() {
//有返回值时
return transactionTemplate.execute(new TransactionCallback() {
// the code in this method runs in a transactional context
public Object doInTransaction(TransactionStatus status) {
updateOperation1();
return resultOfUpdateOperation2();
}
});
}
}
public void someServiceMethod1(){
//没有返回值时使用TransactionCallbackWithoutResult匿名类
transactionTemplate.execute(new TransactionCallbackWithoutResult匿名类() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
updateOperation1();
updateOperation2();
}
});
}
//setRollbackOnly()回滚事务
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
updateOperation1();
updateOperation2();
} catch (SomeBusinessException ex) {
status.setRollbackOnly();
}
}
});
事务设置
public class SimpleService implements Service {
private final TransactionTemplate transactionTemplate;
public SimpleService(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
// the transaction settings can be set here explicitly if so desired
this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
this.transactionTemplate.setTimeout(30); // 30 seconds
// and so forth...
}
}
<bean id="sharedTransactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="isolationLevelName" value="ISOLATION_READ_UNCOMMITTED"/>
<property name="timeout" value="30"/>
</bean>
(2) TransactionManager
直接的实现
通过引用PlatformTransactionManager实例和
通过使用TransactionDefinition
和TransactionStatus
对象,您可以启动事务,回滚和提交
PlatformTransactionManager txManager = new DataSourceTransactionManager();
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can be done only programmatically
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
// put your business logic here
}
catch (MyException ex) {
txManager.rollback(status);
throw ex;
}
txManager.commit(status);