关于spring事务主要有四种特性和五种隔离级别和7种传播行为,这篇文章就来好好总结一下。
是在事务管理器中配置的,关于事务管理器有不明白的可以翻下之前的文章。
springboot中和注解形式的是在@Transactional注解中配置的(添加注解时添加这些):
除了添加@Transactional注解,还可以使用transactionManager手动处理:
关于脏读、幻读和不可重复读:
1.脏读: 一个事务读到了另一个事务未提交的数据。2.不可重复读: 一个事务读到了另一个事务已经提交的update的数据,导致多次查询结果不一致。当一个事务中有两次相同的查询操作时,第一个查询得到结果后,在第二个查询执行时这条结果被修改了,会导致在同一个事务中两个查询的结果不一致。3.幻读: 一个事务中读到另一个事务已经提交的插入数据导致多次查询结果不一致。当一个事务中有两次或多次相同的查询操作时,第一个查询得到的不存在的记录,第二个查询中却发现被另一个事务插入了,这就是幻读。
关于隔离级别,在spring中的设置: 在org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin方法中:
对应的org.springframework.jdbc.datasource.DataSourceUtils#prepareConnectionForTransaction方法:
业务方法在容器中运行 | 存在父事务(即事务嵌套) | 不存在父事务 |
---|---|---|
REQUIRED | 在父事务中运行 | 自己新建一个事务 |
SUPPORTS | 在父事务中运行 | 正常执行 |
MANDATORY | 在父事务中运行 | 抛出异常 |
REQUIRES_NEW | 新建事务,将父事务挂起(suspend) | 自己新建事务 |
NOT_SUPPORTED | 如果存在方法调用将父事务挂起,调用结束恢复 | 不会开启事务 |
NEVER | 抛出 异常 | 正常执行 |
NESTED | 它的事务和父事务是相依的,与父事务一起提交,一起回滚 | 自己新建 |
上面存在父事务的场景主要是sevice调用service或dao层调用dao层的情况。
下面我们看看在spring源码里面的对应设置: 在执行时确定是否需要新建事务会调用获取事务的方法org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction:
/**
* This implementation handles propagation behavior. Delegates to
* {@code doGetTransaction}, {@code isExistingTransaction}
* and {@code doBegin}.
* @see #doGetTransaction
* @see #isExistingTransaction
* @see #doBegin
*/
@Override
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled();
if (definition == null) {
// Use defaults if no transaction definition given.
definition = new DefaultTransactionDefinition();
}
if (isExistingTransaction(transaction)) {//如果当前存在事务
// Existing transaction found -> check propagation behavior to find out how to behave.
//进入处理已经存在事务的处理方法
return handleExistingTransaction(definition, transaction, debugEnabled);
}
//----------------------下面都是当前不存在事务的处理逻辑---------------------------
// Check definition settings for new transaction.
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
// No existing transaction found -> check propagation behavior to find out how to proceed.
//当传播行为是PROPAGATION_MANDATORY时,如果没有父事务,则抛出异常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
}
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
//创建子事务的方法
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException ex) {
resume(null, suspendedResources);
throw ex;
}
catch (Error err) {
resume(null, suspendedResources);
throw err;
}
}
else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + definition);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}