典型的事务场景就是“取钱”,去ATM机取500块钱,这个过程包括:1、卡中减500;2、ATM机吐出500元。两个操作必需保证同时成功,取钱才能成功。否则卡里钱扣了,ATM机没吐钱,你肯定很不爽;ATM机钱吐了,卡里钱没有扣,银行肯定很不爽。所以其中任一环节出问题,对双方都不好,最满意的结果就是,任一环节失败,就恢复到最初状态。
事务就是用来解决这类问题,因此事务需要遵守ACID原则:
上图是Spring事务管理相关的接口关系,从接口出发来理解Spring事务管理。Spring的org.springframework.transaction包中包含如下接口:TransactionDefinition、TransactionStatus、PlatformTransactionManager、SavepointManager。
下面对其中三个核心接口一一说明。
Sprint的事务管理并非自己来做具体的事务处理,仅是提供事务管理的接口,将具体的事务委托给了各类相关平台来做,比如框图中的jdbc、Hibernate等。
Sprint的事务管理器接口PlatformTransactionManager为不同的平台都提供了相应的实现类:
PlatformTransactionManager接口就三方法:获取状态、提交、回滚。
public interface PlatformTransactionManager { TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) throws TransactionException; }
由于实际中是用jdbc进行持久化操作,下面为其配置方式。创建DataSourceTransactionManager的事务管理器来代理jdbc的事务,DataSourceTransactionManager是通过调用java.sql.Connection来管理事务,而后者是通过DataSource获取到的。通过调用连接的commit()方法来提交事务,同样,事务失败则通过调用rollback()方法进行回滚:
<!-- 事务管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
DataSource接口:
public interface DataSource extends CommonDataSource, Wrapper { Connection getConnection() throws SQLException; Connection getConnection(String username, String password) throws SQLException; }
上面仅讲了jdbc事务,其他类型的事务这里不作说明,具体碰到时再说。
PlatformTransactionManager接口方法getTransaction(TransactionDefinition definition)中传入的参数TransactionDefinition定义了事务的属性,可以把它理解成事务配置。
public interface TransactionDefinition { int PROPAGATION_REQUIRED = 0; int PROPAGATION_SUPPORTS = 1; int PROPAGATION_MANDATORY = 2; int PROPAGATION_REQUIRES_NEW = 3; int PROPAGATION_NOT_SUPPORTED = 4; int PROPAGATION_NEVER = 5; int PROPAGATION_NESTED = 6; int ISOLATION_DEFAULT = -1; int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED; int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED; int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ; int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE; int TIMEOUT_DEFAULT = -1; //传播行为 int getPropagationBehavior(); //隔离级别 int getIsolationLevel(); //超时时间 int getTimeout(); //是否可读 boolean isReadOnly(); //事务名称 String getName(); }
传播行为规定了事务方法和事务方法发生嵌套调用时事务如何进行传播,即协调已经有事务标识的方法之间的发生调用时的事务上下文的规则(是否要有独立的事务隔离级别和锁)。通俗的理解就是,不同传播行为性事务调用是否会异常,主事务与嵌套事务提交、回滚机制。
spring事务有7种传播行为,分别是:
1、PROPAGATION.REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
2、PROPAGATION.SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
3、PROPAGATION.MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
4、PROPAGATION.REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建一个独立的新事务,独立提交与回滚。
5、PROPAGATION.NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6、PROPAGATION.NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
7、PROPAGATION.NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
注解@Transactional默认的传播行为是:PROPAGATION.REQUIRED
《Spring事务传播机制》 这篇文章写“事务传播行为”写得非常好,可以读一读。事务传播机制过程中,重点弄懂几点:
隔离很容易联系到数据库的隔离,事务的隔离也与此相关,并发环境下很容易引起事务的数据脏读(dirty read)、不可重复读(nonrepeatable read)、幻读(phantom read)。
隔离级别 | 含义 |
---|---|
ISOLATION_DEFAULT | 使用后端数据库默认的隔离级别 |
ISOLATION_READ_UNCOMMITTED | 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读 |
ISOLATION_READ_COMMITTED | 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生 |
ISOLATION_REPEATABLE_READ | 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生 |
ISOLATION_SERIALIZABLE | 最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的 |
nonrepeatable read与phantom read差异在于,nonrepeatable read重修改,phantom read重增删。
“只读事务”并不是一个强制选项,它只是一个“暗示”,提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。
“只读事务”仅仅是一个性能优化的推荐配置而已,并非强制你要这样做不可,在事务设计阶段,可以提前设置来优化性能。
“只读事务”中执行数据修改操作,会报异常(SpringBoot环境),如下:
“事务超时”很好理解,需要给事务设定超时时间,不能让程序无休止等待,超出时间则回滚。
因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。
回滚原则可参考 《SpringBoot+Mybatis事务管理》 ,可以依据具体业务自定义。
上面讲到的调用PlatformTransactionManager接口的getTransaction()的方法得到的是TransactionStatus接口的一个实现,这个接口的内容如下:
public interface TransactionStatus extends SavepointManager, Flushable { boolean isNewTransaction(); boolean hasSavepoint(); void setRollbackOnly(); boolean isRollbackOnly(); @Override void flush(); boolean isCompleted(); }
TransactionStatus接口提供简单的控制事务执行和查询事务状态的方法,在回滚或提交的时候需要应用对应的事务状态。
1、 API-Interface TransactionDefinition
2、 SpringBoot+Mybatis事务管理
3、 Spring事务传播机制