在 Spring Boot 中使用事务,需要导入 MySQL 依赖:
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.0
导入依赖之后会自动注入DataSourceTransactionManager
无需其他配置就可使用@Transactional
注解进行事务的使用了。
@Service
public class UserServiceImpl implements UserService{
@Resource
private UserMapper userMapper;
@Override
@Transactional
public void insertUser2(User user) throws Exception {
//插入用户信息
userMapper.insertUser(user);
//手动抛出异常
throw new SQLException("数据库异常");
}
}
通过@Transactional正常事物功能就实现了,如上throw new SQLException("数据库异常"); 事物不会回滚,原因如下
Spring Boot 默认的事务规则是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚。比如上面例子中抛出的 RuntimeException,可完成回滚,而抛出 SQLException,则无法回滚。针对非检测异常,如果要进行事务回滚,可以在 @Transactional 注解中使用 rollbackFor 属性指定异常,比如 @Transactional(rollbackFor = Exception.class),这样就没有问题了。这也在告诉我们,实际项目开发中,一定要指定异常。
跟踪源码之后,事物实现主要分三部分:
如下是创建@Transactional注解的类的创建过程
1. bean的创建过程,参考spring之DefaultListableBeanFactory的bean的创建过程
2. 主要是BeanPostProcessor的子类AnnotationAwareAspectJAutoProxyCreator的初始化之后业务逻辑方法postProcessAfterInstantiation,postProcessAfterInstantiation内部会调用org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary方法,wrapIfNecessary主要判断是否有复合要求的Advice,如果有就创建代理类。
3. 判断是否有符合要求的Advisor,Spring advisors(org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor) 和 AspectJ生成的Advisors(@Aspect注解)
4. 事物TransactionAttributeSourcePointcut切面判断制定方法是否能有@Transactional注解,如果有@Transactional注解,解析注解生成TransactionAttribute对象
如上就是spring生成bean的时候,判断是否包含@Transactional,如果包含就生成代理proxy。
TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor,调用代理类的方法,都会调用org.springframework.transaction.interceptor.TransactionInterceptor#invoke,代码如下
我们主要看invokeWithinTransaction方法,代码如下
1. 内部会调用org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction获取或创建一个事物,获取数据库Connection,设置成AutoCommit=false,把ConnectionHolder绑定到ThreadLocal。
2.执行业务target类的方法,执行数据操作,会从ThreadLocal中获取ConnectionHolder中的Connection执行sql
3.如果业务代码执行失败,回滚事物
4.如果业务代码执行成功,提交事物
重点讲一下AbstractPlatformTransactionManager#getTransaction,事务传播类型的功能就是在这里实现的。有事物存在就执行handleExistingTransaction方法,没有事物存在就执行下面的代码。
handleExistingTransaction代码如下
/**
* Create a TransactionStatus for an existing transaction.
*/
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
return startTransaction(definition, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
return status;
}
else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
return startTransaction(definition, transaction, debugEnabled, null);
}
}
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
if (isValidateExistingTransaction()) {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
Constants isoConstants = DefaultTransactionDefinition.constants;
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] specifies isolation level which is incompatible with existing transaction: " +
(currentIsolationLevel != null ?
isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
"(unknown)"));
}
}
if (!definition.isReadOnly()) {
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] is not marked as read-only but existing transaction is");
}
}
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
如上代码所示:
挂起事物:实现很简单,就是解绑上下文中的ConnectionHolder,保存到内存中,等操作完成,再从内存中还原事物状态和ConnectionHolder。
Spring事务的构成,基本有三个部分,事务属性的定义,事务对象及状态信息的持有,事务管理器的处理。
1.TransactionInterceptor
事务处理增强
2.TransactionAttributeSourcePointcut
事务处理属性源切点
3.TransactionAttributeSourceAdvisor
事务处理属性源切面