1. 概念
什么是事务?事务指的是逻辑上的一组操作,这组操作要么全部成功,要么全部失败。
事务包括四大特性(ACID):原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
- 原子性:指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
- 一致性:指事务前后数据的完整性必须保持一致。
- 隔离性:指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间的数据要相互隔离。
- 持久性:指一个事务一旦被提交,它对数据库中的数据的改变就是永久的,即使数据库发生故障也不应该对其有任何影响。
2. 事务的API
2.1 接口介绍
Spring对事务管理提供了接口支持,主要包括3个高层抽象的接口:
-
PlatformTransactionManager
事务管理器 -
TransactionDefinition
事务定义信息(隔离、传播、超时、只读) -
TransactionStatus
事务具体的运行状态信息(是否新事务、是否有保存点。。。)
2.2 PlatformTransactionManager接口
Spring为不同的持久化框架提供了不同的PlatformTransactionManager
接口实现:
具体实现 | 说明 |
---|---|
org.springframework.jdbc.datasource.DataSourceTransactionManager |
使用Spring JDBC或Mybatis进行持久化数据是使用 |
org.springframework.orm.hibernate5.HibernateTransactionManager |
使用Hibernate5.x版本进行持久化数据时使用 |
org.springframework.orm.jpa.JpaTransactionManager |
使用JPA进行持久化时使用 |
org.springframework.orm.jdo.JdoTransactionManager |
当持久化机制是Jdo时使用 |
org.springframework.transaction.jta.JtaTransactionManager |
使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用 |
2.3 TransactionDefinition接口
TransactionDefinition接口中主要定义了事务的传播行为getPropagationBehavior()
、事务的隔离级别getIsolationLevel()
、超时时间getTimeout()
、是否只读isReadOnly()
、事务名称getName()
2.3.1 TransactionDefinition定义事务隔离级别
我们知道事务4个特性中有一个隔离性,如果不考虑隔离性的的话,会引发一些安全问题:脏读、不可重复读、幻读
- 脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。
- 不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同
- 幻读:一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。在后来的查询中,第一个事务就会发现有些原来没有的记录
隔离级别就是用来解决上述各种问题的,隔离级别有4种(上图中的ISOLATION_***
除了ISOLATION_DEFAULT
):
隔离级别 | 含义 |
---|---|
DEFAULT |
使用后端数据库默认的隔离级别(Spring中的选择项) |
READ_UNCOMMITTED |
允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读 |
READ_COMMITTED |
允许在并发事务已经提交后读取。可防止脏读,但幻读、不可重复读仍可发生 |
REPEATABLE_READ |
对相同的字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生 |
SERIALIZABLE |
完全服从ACID的隔离级别,确保不发送脏、幻、不可重复读。这仔所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表完成的。 |
2.3.2 TransactionDefinition定义事务传播行为
事务的传播行为解决的是业务层方法之间调用的时事务的传递问题。
一般我们的系统会分为3层:Web层、业务层Service、持久层DAO;假设有两个业务类ServiceA
和ServiceB
,ServiceA
中有方法aaa()
,ServiceB
中有方法bbb()
,有一个业务逻辑需要调用ServiceA.aaa()
和ServiceB.bbb()
才能完成,现在aaa()
方法里有事务,bbb()
方法里也有事务,那到底要用哪个呢,这就涉及到了事务的传播行为。
事务的传播行为有7种(上图中的PROPAGATION_***
):
隔离级别 | 含义 |
---|---|
PROPAGATION_REQUIRED |
支持当前事务,如果不存在,就新建一个 |
PROPAGATION_SUPPORTS |
支持当前事务,如果不存在,就不使用事务 |
PROPAGATION_MANDATORY |
支持当前事务,如果不存在,抛出异常 |
PROPAGATION_REQUIRES_NEW |
如果有事务存在,挂起当前事务,创建一个新的事务 |
PROPAGATION_NOT_SUPPORTED |
以非事务的方式运行,如果有事务存在,挂起当前事务 |
PROPAGATION_NEVER |
以非事务的方式运行,如果有事务存在,抛出异常 |
PROPAGATION_NESTED |
如果当前事务存在,则嵌套事务执行 |
2.4 TransactionStatus接口
事务本身会存在一些状态信息,而TransactionStatus接口里面提供了一些方法,通过这些方法可以获得事务相应的状态。
isNewTransaction()
:判断是否是一个新的事务
hasSavepoint()
:是否存在保存点
setRollbackOnly()
:设置为只回滚
isRollbackOnly()
:是否为只回滚
isCompleted()
:是否已完成
3. 编程式事务管理(不常用)
编程式事务管理使用TransactionTemplate
模板来控制事务。TransactionTemplate
的重要方法就是 execute
方法,此方法调用 TransactionCallback
进行处理。实际上我们需要处理的事情全部都是在TransactionCallback
中编码的,我们可以定义一个类并实现此接口,然后作为 TransactionTemplate.execute
的参数。把需要完成的事情放到doInTransaction
中,并且传入一个 TransactionStatus
参数,此参数是来调用回滚的。demo如下:
spring配置文件springDemo1.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"
default-lazy-init="true">
<description>Spring Configuration</description>
<!-- 加载配置属性文件 -->
<context:property-placeholder ignore-unresolvable="true" location="classpath:jdbc.properties" />
<!-- 使用Annotation自动注册Bean base-package 如果多个,用“,”分隔-->
<context:component-scan base-package="com.zhoubg.spring.transaction.demo1" />
<!-- 数据源配置, 使用 Druid 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 数据源驱动类可不写,Druid默认会自动根据URL识别DriverClass -->
<property name="driverClassName" value="${jdbc.driver}" />
<!-- 基本属性 url、user、password -->
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- ==================================1.编程式的事务管理=============================================== -->
<!-- 定义事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置事务管理的模板:Spring为了简化事务管理的代码而提供的类 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
</beans>
模拟业务方法AccountServiceImpl.transfer
:
/**
* 模拟转账操作
* @param out :转出账号
* @param in :转入账号
* @param money :转账金额
*/
public void transfer(final String out,final String in,final Double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try { //具体业务代码
accountDaoImpl.outMoney(out,money); //out账户出账
//int i = 1/0;
accountDaoImpl.inMoney(in,money);// in账户入账
}catch (Exception e){
status.setRollbackOnly(); //设置回滚
e.printStackTrace();
}
}
});
}
4. 声明式事务管理
4.1 声明式事务管理方式一:基于TransactionProxyFactoryBean的方式
spring配置文件springDemo2.xml
关键配置:
<!-- ==================================2.使用XML配置声明式的事务管理(原始方式)=============================================== -->
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置业务层的代理 -->
<bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 配置目标对象 -->
<property name="target" ref="accountServiceImpl" />
<!-- 注入事务管理器 -->
<property name="transactionManager" ref="transactionManager"></property>
<!-- 注入事务的属性 -->
<property name="transactionAttributes">
<props>
<!--
prop的格式:
* PROPAGATION :事务的传播行为
* ISOTATION :事务的隔离级别
* readOnly :只读
* -EXCEPTION :发生哪些异常回滚事务
* +EXCEPTION :发生哪些异常不回滚事务
-->
<prop key="transfer">PROPAGATION_REQUIRED</prop>
<!-- <prop key="transfer">PROPAGATION_REQUIRED,readOnly</prop> -->
<!-- <prop key="transfer">PROPAGATION_REQUIRED,+java.lang.ArithmeticException</prop> -->
</props>
</property>
</bean>
模拟业务方法AccountServiceImpl.transfer
:
public void transfer(final String out,final String in,final Double money) {
accountDaoImpl.outMoney(out,money);
accountDaoImpl.inMoney(in,money);
}
4.2 声明式事务管理方式二:基于AspectJ的XML方式
spring配置文件springDemo3.xml
关键配置:
<!-- ==================================3.使用XML配置声明式的事务管理,基于tx/aop=============================================== -->
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置事务的通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--
propagation :事务传播行为
isolation :事务的隔离级别
read-only :只读
rollback-for:发生哪些异常回滚
no-rollback-for :发生哪些异常不回滚
timeout :过期信息
-->
<tx:method name="transfer" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<!-- 配置切面 -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* com.zhoubg.spring.transaction..demo3.AccountService+.*(..))" id="pointcut1"/>
<!-- 配置切面 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
</aop:config>
模拟业务方法AccountServiceImpl.transfer
:
public void transfer(final String out,final String in,final Double money) {
accountDaoImpl.outMoney(out,money);
accountDaoImpl.inMoney(in,money);
}
4.3 声明式事务管理方式三:基于注解的方式(推荐)
这种方式是基于@Transactional
注解的,简单易用,更清爽,是推荐的使用方式。
@Transactional
可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public
方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
@Transactional
的属性:
属性 | 类型 | 描述 |
---|---|---|
value |
String | 可选的限定描述符,指定使用的事务管理器 |
propagation |
enum: Propagation | 可选的事务传播行为设置 |
isolation |
enum: Isolation | 可选的事务隔离级别设置 |
readOnly |
boolean | 读写或只读事务,默认读写 |
timeout |
int (in seconds granularity) | 事务超时时间设置 |
rollbackFor |
Class对象数组,必须继承自Throwable | 导致事务回滚的异常类数组 |
rollbackForClassName |
类名数组,必须继承自Throwable | 导致事务回滚的异常类名字数组 |
noRollbackFor |
Class对象数组,必须继承自Throwable | 不会导致事务回滚的异常类数组 |
noRollbackForClassName |
类名数组,必须继承自Throwable | 不会导致事务回滚的异常类名字数组 |
spring配置文件springDemo4.xml
关键配置:
<!-- ==================================4.使用注解配置声明式事务============================================ -->
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 开启注解事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
模拟业务方法AccountServiceImpl.transfer
:
@Transactional(readOnly = false)
public void transfer(final String out,final String in,final Double money) {
accountDaoImpl.outMoney(out,money);
accountDaoImpl.inMoney(in,money);
}
以上示例源码:learn-spring-transaction