Spring事务
Spring事务
Spring对事务的支持
Spring框架提供了对事务的一致性抽象,使得开发人员编写的代码可以在不同的环境下适用于不同的事务管理,Spring框架提供了编程式事务管理和声明式事务管理
Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource(数据源信息)、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分
DataSource基于数据源的数据管理器
<!--引用外部的Properties文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置一个数据源-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}"/>
<!--基于数据源的事务管理器,通过属性引用数据源-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"/>
事务管理器
<!-- 事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="myDataSource"></property>
</bean>
<!-- 事务使用的范围以及类型 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor pointcut="within(com.xms.controller.*)"
advice-ref="txAdvice"/>
</aop:config>
- 编程式事务管理:
可以清楚地控制事务的边界,可自行实现事务开始时间、结束时间、撤消操作的时机等,可以实现细粒度的事务控制 - 声明式事务管理:
声明式事务管理方法允许你在配置的帮助下而不是源代码硬编程来管理事务。这意味着你可以将事务管理从事务代码中隔离出来。你可以只使用注释或基于配置的 XML 来管理事务
事务三大接口
- PlatformTransactionManager 事务管理器
- TransactionDefinition 事务的一些基础信息,如超时时间、隔离级别、传播属性等
- TransactionStatus 事务的一些状态信息,如是否一个新的事务、是否已被标记为回滚
PlatformTransactionManager事务管理器
Spring框架对事务抽象的关键就是PlatformTransactionManager接口中定义的TransactionDefinition 事务定义接口和TransactionStatus事务状态接口定义
public interface PlatformTransactionManager {
//根据事务定义TransactionDefinition,获取事务
TransactionStatus getTransaction(TransactionDefinition definition);
//提交事务
void commit(TransactionStatus status);
//回滚事务
void rollback(TransactionStatus status);
}
TransactionDefinition 事务定义接口
事务的定义包括: 事务的隔离级别,事务的传播属性,超时时间设置,是否只读
事务的隔离级别是数据库本身的事务功能,事务的传播属性则是spring为我们提供的功能
该接口的实现DefaultTransactionDefinition,默认的事务定义
public class DefaultTransactionDefinition implements TransactionDefinition, Serializable {
private int propagationBehavior = PROPAGATION_REQUIRED;
private int isolationLevel = ISOLATION_DEFAULT;
private int timeout = TIMEOUT_DEFAULT;
private boolean readOnly = false;
//略
}
事务的传播属性为PROPAGATION_REQUIRED,即当前没有事务的时候,创建一个,如果有则使用当前事务 2.事务的隔离级别采用底层数据库默认的隔离级别 3.超时时间采用底层数据库默认的超时时间 4.是否只读为false
TransactionStatus事务状态接口定义
TransactionStatus它继承了SavepointManager接口,SavepointManager是对事务中上述保存点功能的封装,如下:
public interface SavepointManager {
Object createSavepoint() throws TransactionException;
void rollbackToSavepoint(Object savepoint) throws TransactionException;
void releaseSavepoint(Object savepoint) throws TransactionException;
}
TransactionStatus本身更多存储的是事务的一些状态信息,是否是一个新的事物 是否有保存点 是否已被标记为回滚
整个流程:
@Autowired
private PlatformTransactionManager transactionManager;
TransactionStatus status = null;
// 手动开启事务
status = transactionManager.getTransaction(new DefaultTransactionDefinition());
// 事务提交
transactionManager.commit(status);
// 事务回滚
if (StringMoreUtils.checkValNotNull(status)) {
transactionManager.rollback(status);
}
事务管理方式
spring支持编程式事务管理和声明式事务管理两种方式。
编程式事务管理
- Spring提供的最原始的事务管理方式是基于TransactionDefinition、PlatformTransactionManager、TransactionStatus 编程式事务
- 而TransactionTemplate的编程式事务管理是使用模板方法设计模式对原始事务管理方式的封装
- spring推荐使用TransactionTemplate
TransactionTemplate解读
查看 TransactionTemplate.java 源码
public class TransactionTemplate extends DefaultTransactionDefinition implements TransactionOperations, InitializingBean {
protected final Log logger = LogFactory.getLog(this.getClass());
private PlatformTransactionManager transactionManager;
public TransactionTemplate() {
}
public TransactionTemplate(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public TransactionTemplate(PlatformTransactionManager transactionManager, TransactionDefinition transactionDefinition) {
super(transactionDefinition);
this.transactionManager = transactionManager;
}
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public PlatformTransactionManager getTransactionManager() {
return this.transactionManager;
}
public void afterPropertiesSet() {
if (this.transactionManager == null) {
throw new IllegalArgumentException("Property 'transactionManager' is required");
}
}
/*
* 控制事务管理主要依赖于这个方法,execute方法里面的核心又是action.doInTransaction(status);
*/
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
return((CallbackPreferringPlatformTransactionManager)this.transactionManager).execute(this, action);
} else {
TransactionStatus status = this.transactionManager.getTransaction(this);
Object result;
try {
result = action.doInTransaction(status);
} catch (RuntimeException var5) {
this.rollbackOnException(status, var5);
throw var5;
} catch (Error var6) {
this.rollbackOnException(status, var6);
throw var6;
} catch (Exception var7) {
this.rollbackOnException(status, var7);
throw new UndeclaredThrowableException(var7, "TransactionCallback threw undeclared checked exception");
}
this.transactionManager.commit(status);
return (T) result;
}
}
private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException {
this.logger.debug("Initiating transaction rollback on application exception", ex);
try {
this.transactionManager.rollback(status);
} catch (TransactionSystemException var4) {
this.logger.error("Application exception overridden by rollback exception", ex);
var4.initApplicationException(ex);
throw var4;
} catch (RuntimeException var5) {
this.logger.error("Application exception overridden by rollback exception", ex);
throw var5;
} catch (Error var6) {
this.logger.error("Application exception overridden by rollback error", ex);
throw var6;
}
}
}
从源码可以看出TransactionTemplate主要依赖于execute(TransactionCallback action)方法执行事务管理,execute方法里面的核心又是action.doInTransaction(status);
execute方法的参数 (TransactionCallback action),查看接口TransactionCallback.java 发现其仅有一个方法:
public interface TransactionCallback<T> {
T doInTransaction(TransactionStatus var1);
}
并且有一个抽象类TransactionCallbackWithoutResult实现了接口TransactionCallback
public abstract class TransactionCallbackWithoutResult implements TransactionCallback<Object> {
public TransactionCallbackWithoutResult() {
}
public final Object doInTransaction(TransactionStatus status) {
this.doInTransactionWithoutResult(status);
return null;
}
protected abstract void doInTransactionWithoutResult(TransactionStatus var1);
}
所以当我们借助TransactionTemplate.execute( … )执行事务管理的时候,传入的参数有两种选择:
1、TransactionCallback
2、TransactionCallbackWithoutResult
两种区别从命名看就相当明显了,一个是有返回值,一个是无返回值。这个的选择就取决于你是读还是写了
TransactionTemplate实现案例
1、在spring配置文件中配置相关TransactionTemplate示例
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置 transactionTemplate -->
<bean id="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
</bean>
2、对TransactionCallback进行属性设置(该设置也可以在Spring的配置文件中指定)
//设置事务传播属性
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 设置事务的隔离级别,设置为读已提交(默认是ISOLATION_DEFAULT:使用的是底层数据库的默认的隔离级别) transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
// 设置是否只读,默认是false
transactionTemplate.setReadOnly(true);
// 默认使用的是数据库底层的默认的事务的超时时间
transactionTemplate.setTimeout(30000);
3、业务代码引用:
(1)借助(TransactionCallback)执行事务管理,既带有返回值:
public Object getObject(String str) {
/*
* 执行带有返回值<Object>的事务管理
*/
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
try {
...
//....... 业务代码
return new Object();
} catch (Exception e) {
//回滚
transactionStatus.setRollbackOnly();
return null;
}
}
});
}
(2)借助(TransactionCallbackWithoutResult)执行事务管理,既无返回值:
public void update(String str) {
/*
* 执行无返回值的事务管理
*/
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
try {
// .... 业务代码
} catch (Exception e){
//回滚
transactionStatus.setRollbackOnly();
}
}
});
}
声明式事务管理
声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。
基于tx和aop名字空间的xml配置文件
好处是事务管理的API不介入程序,最符合一个非 侵入型轻量级容器的理想多数情况下事务不需要细粒度控制,因此建议使用,Spring Framework的声明式事务管理是建立在Spring的面向切面编程(aspect-oriented programming, AOP) 上的从概念上来讲, 在事务型代理上调用一个方法看起来像这样:
<!--引用外部的Properties文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置一个数据源-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}"/>
<!--基于数据源的事务管理器,通过属性引用数据源-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"/>
<!-- 事务使用的范围以及类型 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor pointcut="within(com.xms.controller.*)"
advice-ref="txAdvice"/>
</aop:config>
基于@Transactional注解
@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@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
}
}
需要明确几点:
- 默认配置下 Spring 只会回滚运行时、未检查异常(继承自 RuntimeException 的异常)或者 Error
- @Transactional 注解只能应用到 public 方法才有效
- 异常的分类:
@Transactional 默认回滚 运行时异常和error,如果想回滚所有的异常,应该加上 rollbackFor = Throwable.class
spring事务的传播行为定义
当一个事务方法被另外一个事务方法调用时,必须指定事务的传播行为。比如:在当前事务中运行,也可以新起事务
spring提供了7种传播行为:
–required(最常用) 必须有字段,不能为空
–supports 支持当前事务,如果当前没有事务,就以非事务方式执行。
–MANDATORY(强制) 使用当前的事务,如果当前没有事务,就抛出异常
–REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起
–NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
–NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
–NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类 似的操作
事务的基本要素 ACID
- 原子性: 事务开始后 要么全部做完,要么全部不做,不能执行一半停止,如果在执行中出错,会回滚到事务开始前的状态,也就是说事务是一个不可分割的整体,就好像化学里的原子,是构成物质的基本单位
- 一致性: A像B转账,不可能A扣了钱,B没有收到
- 隔离性: 同一时间,只允许一个事务对数据操作,不同事务间没有干扰,比如A从一张银行卡取钱,在这个动作结束前,B不能向银行卡存钱
- 持久性: 事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚
事务的并发问题
- 脏读: 事务A读取了事务B更新的数据,然后事务B回滚,那么事务A读取的数据为脏数据
- 不可重复读: 事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据操作并提交,导致事务A读取到的数据不一致
- 幻读: 系统管理员A将学生成绩从具体分数划分为ABCDE等级,但是这个时候系统管理B又插入了一条具体分数的记录,当A操作结束后发现还有一条记录没有改过来,就好像发生了幻觉
不可重复读侧重于修改,幻读侧重于新增或者删除操作,解决不可重复读需要锁住满足条件的行,解决幻读是锁表
spring对事务的管理总结
a.使用编程方式管理事务
–DataSource
–TransactionTemplate
–PlatformTransactionManager
优点:编程扩展性更强
缺点:代码的耦合度较高,侵入性较强
b.使用配置方式管理事务
–基于XML配置
–基于注解配置