文章目录
spring事务控制
简介
事务是恢复和并发控制的基本单位。
事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。
原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(durability)。持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
常规项目使用事务
常规项目都是自己手动控制事务的开关,连接的开关,导致很多时候事务和连接的开关出现问题,导致各种各样的报错,这样的报错通常不好发现,排查困难。
Connection,Session,Transaction的关系
Connection是一个物理连接,一个物理连接可以有多次session。一次session里面可以有多次transaction
Spring使用事务
spring的事务依靠thread保存,和线程绑定,自动开启和释放,只需要我们使用特定的注解即可。
Spring 事务几个关键的接口
PlatformTransactionManager,TransactionStatus,TransactionDefinition。这三个接口都在spring-tx-x.x.x.RELEASE.jar下面的org.springframework.trasaction包下面。
- PlatformTransactionManager : 事务管理器
- TransactionDefinition : 事务的一些基础信息,如超时时间、隔离级别、传播属性等
- TransactionStatus : 事务的一些状态信息,如是否是一个新的事务、是否已被标记为回滚
PlatformTransactionManager
看一下PlatformTransactionManager 有哪些方法
在上面我们看到,PlatformTransactionManager 定义了事务的获取,提交和回滚。获取出来的,就是TransactionStatus。看下PlatformTransactionManager的实现类。
比较常见的实现类有DataSourceTransactionManager,HibernateTransactionManager。如果我们用的mybatis操作mysql数据库,就用的DataSourceTransactionManager。如果用的hibernate,就是HibernateTransactionManager。还有jpa等其他的。
TransactionDefinition
从上面,我们可以看到有一些静态常量,这是都是spring的事务传播类型。下面,是一些方法,比如获取当前事务的传播类型,获取事务隔离级别,获取超时时间,是否只读,获取事务的名字,获取默认的TransactionDefinition。
TransactionStatus
TransationStatus继承了其他接口,所以我们看到他的实现类AbstractTransactionStatus。
该类的属性主要标记了当前transaction和他的状态。该类的方法包含了对上面状态的获取和维护,SavepointManager接口的一些方法的实现。SavepointManager定义了spring事务的保存点(SavePoint)的操作方法。SavePoint主要为了事务嵌套使用的,SavePoint详细解释。
上面三个怎么搭配的
从TransactionInterceptor这个类的invoke方法进入到TransactionAspectSupport.invokeWithinTransaction方法。进入到该方法后,看到一个比较重要的TransactionAttribute txAttr,TransactionAttribute是TransactionDefinination的子接口。txAttr保存了事务的传播属性,事务的隔离级别,事务的是否只读等信息。看到我们利用txAttr获取到的是PlatformTransactionManager(tm对象),继续往下面走到createTransactionIfNecessary方法。在该方法下面会利用tm.getTransaction(txAttr)构建一个TransactionStatus对象。
所以从上面来看,TransationDefinition先决定了PlatformTransactionManager,然后通过PlatformTransactionManager和TransationDefinition获取TransactionStatus。然后把PlatformTransactionManager,TransationDefinition,TransactionStatus保存到TransactionInfo对象中。
进入到TransactionAspectSupport.invokeWithinTransaction
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass);
//申明式事务,只需要把需要的参数传入,具体的提交和回滚由框架决定。txAttr可能为空或不为空,但是tm很多情况下也不是CallbackPreferringPlatformTransactionManager,所以会走if下面的逻辑。
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
//在这个方法里面会判断是否创建事务,会走到下面的PlatformTransactionManager.getTransaction。注意在走到PlatformTransactionManager.getTransaction前时会判断txAttr是否为null,为null时不会走到getTransaction方法。
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
//编程式事务,自己决定是提交还是回滚
else {
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceedWithInvocation();
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
return new ThrowableHolder(ex);
}
}
finally {
cleanupTransactionInfo(txInfo);
}
}
});
// Check result: It might indicate a Throwable to rethrow.
if (result instanceof ThrowableHolder) {
throw ((ThrowableHolder) result).getThrowable();
}
else {
return result;
}
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
}
}
具体看下PlatformTransactionManager.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 {
//根据底层实现类决定返回的transaction,该阶段的transaction,对于我们常用的DataSourceTransactionManager来说,里面有ConnectionHolder和SavepointAllowed属性。
Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled();
//如果TransactionDefinition为空,即可能在方法上或者该方法的类上没有@Transactional注解,或者也没有在其他地方配置
if (definition == null) {
//new一个默认的transactiondefinition
definition = new DefaultTransactionDefinition();
}
//根据底层实现类决,检查当前情况下是否有事务,如果有,那么根据事务传播级别去处理
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
}
// 走到这里就代表当前情况下没有事务,那么下面就会根据事务传播属性等配置信息创建transationStatus和开启事务。
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
//不同的事务传播属性不同的处理方式
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);
//创建transactionStatus
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);
}
}
上面有两个比较重要的方法,doGetTransaction和isExistingTransaction。这里用DatasourceTransactionManager举例。
protected Object doGetTransaction() {
//这里新建了一个txObject,在这里会返回出去
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed()); //是否可以使用savepoint(具体的作用可以看上面)
//利用datasource从TransactionSynchronizationManager获取对象。
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
//设置connectionHolder,但是conHolder可能为null
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
从上面看到一个TransactionSynchronizationManager,这个类里面有很多ThreadLocal相关的对象,resource就是其中一个。也就是说这些都是和线程绑定的。在DatasourceTransactionManager的doBegin会绑定该资源。
继续看isExistingTransaction。
protected boolean isExistingTransaction(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
//如果ConnectionHolder为空,txObject.hasConnectionHolder()返回为false。txObject.getConnectionHolder().isTransactionActive()返回事务是否在活动状态
return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}
如果已存在事务,那么回进入到handleExistingTransaction方法,如果不存在,就会走下面的方法。
当我们第一次访问数据库的时候,是没有事务的,所以会走下面的方法。如果事务的传播方式为required,required_new,nested情况的时候,会走到doBegin方法。在dobegin方法中会创建事务并且绑定到TransactionSynchronizationManager的resource中,设置TransactionSynchronizationManager的当前事务。
如果我们第二次进来,那么可能已经存在事务了,那么会走handleExistingTransaction。
在handleExistingTransaction中,会针对不同的事务传播属性进行特殊处理,例如为never的,会抛出异常,not_supported的会将当前transaction保存到新的transactionStatus中。required_new的会将transaction保存到新的transactionStatus中,同时会创建新的事务。这里和第一次进来无事务的时候一样。
回到TransactionInterceptor的invokeWithinTransaction
继续看到下面的catch,finally,和正常执行。在catch的时候,调用了completeTransactionAfterThrowing,这里对事物进行了回滚,如果有被暂停的事务(比如required_new的时候,会把老事务悬停),会把悬停的事务重新激活。在正常的执行情况下,调用了cleanupTransactionInfo,这里完成了事务的提交,和rollback一样,都会处理被悬停的事务。在finally里面,调用了cleanupTransactionInfo(),这里完成了事务的清除。
扩展
1、手动设置回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); //手动开启事务回滚
编程式事务
@Autowired
private TransactionTemplate transactionTemplate;
@Transactional
public UserInfo saveRegistInfoAndBuildUser(RegistInfo resgitInfo) {
//独立的事务
resgitInfoService.saveRegistInfo(resgitInfo);
// 独立的事务
return transactionTemplate.execute(new TransactionCallback<UserInfo>() {
@Override
public UserInfo doInTransaction(TransactionStatus status) {
try{
UserInfo userInfo = userInfoService.convertRegistInfoToUserInfo(resgitInfo);
userInfoService.saveUserInfo(userInfo);
return userInfo;
}catch(Exception e){
e.printStackTrace();
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); //手动开启事务回滚
}
}
});
}
2、当我们没有申明事务参数时,也就是说我们获取到的txAttr为空时,很多的代码都是没有进入的,比如我们上面的getTransaction(),那当前是没有事务的。