spring 事务基本原理
先看看Java JDBC操作数据库的基本步骤
获取数据库连接 DriverManager.getConnection
开启事务conn.setAutoCommit()
执行CRUD
提交事务|回滚事务conn.commit() / conn.rollback()
关闭连接 conn.close()
spring事务提供了编程式
和声明式
两种方式。使用spring事务,可以不再写2、4步骤代码,spring事务代理会帮我们自动完成事务开启、正常提交事务or异常回滚事务操作。
这里顺便提下我在学些过程中踩到的坑,MySQL数据库事务回滚对innodb索引有效, 而myisam是不支持事务的, 建表的时候选好索引类型。
spring 事务原理底层离不开数据库事务的支持。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。
spring 事务核心概念
Spring 事务的两大核心 :传播 + 隔离
事务传播级别 | 解释 |
---|---|
PROPAGATION_REQUIRED | 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择,也是 Spring 默认的事务的传播。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。新建的事务将和被挂起的事务没有任何关系,是两个独立的事务,外层事务失败回滚之后,不能回滚内层事务执行的结果,内层事务失败抛出异常,外层事务捕获,也可以不处理回滚操作 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务 拥有多个可以回滚的保存点 。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。 |
本质就是数据库隔离级别
spring定义的五种事务隔离级别(本质是4种事务,1种是默认事务,其余四种和数据库的4种事务一一对应)
事务隔离级别 | 解释 |
---|---|
ISOLATION_DEFAULT | spring 默认事务隔离级别。也是数据库默认的事务隔离级别。 |
ISOLATION_READ_UNCOMMITTED | 这是事务最低的隔离级别,它充许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。 |
ISOLATION_READ_COMMITTED | 一个事务只读取另一个事务已提交的数据。保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。 |
ISOLATION_REPEATABLE_READ | 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。 |
ISOLATION_SERIALIZABLE | 这是最可靠的事务隔离级别。事务串行执行(被处理为顺序执行), 对系统的性能影响很大, 谨慎采取此隔离级别 |
注意:隔离级别越高,越能保证数据一致性,但这是要付出性能代价的。使用事务的时候谨慎选取事务隔离级别。
深入spring 事务原理
事务源码分析:
核心四大类
常用接口:
Public interface PlatformTransactionManager{
- TransactionStatus getTransaction(TransactionDefinition definition)
- void commit(TransactionStatus status)
- void rollback(TransactionStatus status)
}
事务隔离级别: 隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:(有一个默认常量,其实也就是四种隔离级别)
public interface TransactionStatus{
boolean isNewTransaction();
void setRollbackOnly();
boolean isRollbackOnly();
}
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="应用DBsource"/>
bean>
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager" />
bean>
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// todo 这里是业务逻辑code
}
});
TransactionTemplate.execute源码分析:
获取TransactionStatus对象, 通过action.doInTransaction(status)回调包裹在事务中的业务逻辑. 如果业务正常, 则提交事务。否则事务异常, 调用rollbackOnException进行回滚, 继续跟进rollbackOnException方法, 底层是通过 conn.rollback() 来回滚的。
@Override
public T execute(TransactionCallback action) throws TransactionException {
if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
}
else {
// 获取事务(底层封装了DataSource、connection)
TransactionStatus status = this.transactionManager.getTransaction(this);
T result;
try {
// action 回调业务逻辑代码
result = action.doInTransaction(status);
}
catch (RuntimeException ex) {
// Transactional code threw application exception -> rollback
rollbackOnException(status, ex);
throw ex;
}
catch (Error err) {
// Transactional code threw error -> rollback
rollbackOnException(status, err);
throw err;
}
catch (Exception ex) {
// Transactional code threw unexpected exception -> rollback
rollbackOnException(status, ex);
throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
}
this.transactionManager.commit(status);
return result;
}
}
getTransaction 方法负责准备事务开启前期等工作。有个比较重要的doBegin方法
把事务自动提交关闭、设置自动超时等
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if (txObject.getConnectionHolder() == null ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = this.dataSource.getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// Bind the session holder to the thread.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, this.dataSource);
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}