事物概念解析
什么是事务
事务是逻辑上的一组执行单元,要么都执行,要么都不执行.
事务的特性(ACID)

什么是ACID
ACID是指数据库管理系统DBMS中事物所具有四个特性
原子性:Automicity
操作不能背分割,要么都成功,要么都失败,若事务出错了,那么事务就会回滚。
一致性:Consistency
数据库一直处于一致的状态,事务开始前是一个一致状态,结束后是另一个一致状态
隔离性:Isolation
一个事务的影响在该事务提交前对其它事务是不可见的
持久性:Durablility
若事务已经提交了,那么就回在数据库中永久的保存下来
Spring事务三大接口介绍
Spring并不直接管理事务,而是提供了多种事务管理器 ,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。Spring事务管理器的接口是:
org.springframework.transaction.PlatformTransactionManager ,
通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public interface PlatformTransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException; }
|
TransactionDefinition:事物属性的定义
事务定义信息(事务隔离级别、传播行为、超时、只读、回滚)
org.springframework.transaction.TransactionDefinition
TransactionDefinition接口中定义了5个方法以及一些表示事务属性的常量比如隔离级别、传播行为等等的常量。下面只是列出了TransactionDefinition接口中的方法而没有给出接口中定义的常量,该接口中的常量信息会在后面依次介绍到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
| 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();
@Nullable String getName();
}
|

TransactionStatus:事务运行状态
TransactionStatus接口用来记录事务的状态,该接口定义了一组方法,用来获取或判断事务的相应状态信息.
PlatformTransactionManager.getTransaction(…) 方法返回一个 TransactionStatus 对象。返回的TransactionStatus 对象可能代表一个新的或已经存在的事务(如果在当前调用堆栈有一个符合条件的事物
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public interface TransactionStatus extends SavepointManager, Flushable {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
@Override void flush();
boolean isCompleted();
}
|
@EnableTransactionManagement
注入了哪些组件
1、注解导入了TransactionManagementConfigurationSelector
2、TransactionManagementConfigurationSelector导入了
1)AutoProxyRegistrar:导入了 InfrastructureAdvisorAutoProxyCreator
2)ProxyTransactionManagementConfiguration
BeanFactoryTransactionAttributeSourceAdvisor
TransactionAttributeSource
TransactionInterceptor

因此,EnableTransactionManagement,主要帮我们导入了(默认)4个组件
- InfrastructureAdvisorAutoProxyCreator
- 注意,如果开启了@EnableAspectJAutoProxy,则实际上使用的还是AnnotationAwareAspectJAutoProxyCreator(优先级高)

- TransactionAttributeSource 事务属性源
- TransactionInterceptor 事务拦截器
- BeanFactoryTransactionAttributeSourceAdvisor 事务属性源工厂**==增强器==**
InfrastructureAdvisorAutoProxyCreator
类结构图如下,发现与@EnableAspectJAutoProxy导入的:AnnotationAwareAspectJAutoProxyCreator类似

结合具体源码得到流程图如下:发现结构和也和AnnotationAwareAspectJAutoProxyCreator类似

因此Spring事务注解的启动原理与AOP类似

Spring事务方法调用了流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| new AnnotationConfigApplicationContext refresh()
finishBeanFactoryInitialization(beanFactory);
AbstractAutoProxyCreator.postProcessBeforeInstantiation() shouskip() findEligibleAdvisors findCandidateAdvisors() findAdvisorsThatCanApply() getTransactionAttributeSource() computeTransactionAttribute() findTransactionAttribute 1、实现类的目标方法上找 2、实现类上找 3、原方法上找 4、原类上找
AbstractAutoProxyCreator.postProcessAfterInitialization()
|
事务代码运行调用流程
以下是某个事务方法执行大致过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| JdkDynamicAopProxy.invoke->MethodInterceptor.invoke() ->ReflectiveMethodInvocation.proceed() ->TransactionInterceptor.invoke() ->invokeWithinTransaction getTransactionAttributeSource(); try{ invocation.proceedWithInvocation(); }catch{ completeTransactionAfterThrowing(txInfo, ex); } commitTransactionAfterReturning(txInfo);
|
实例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| public interface PayService { void pay(String accountId,double money); void updateProductStore(Integer productId); }
@Component public class PayServiceImpl implements PayService { @Autowired private AccountInfoDao accountInfoDao; @Autowired private ProductInfoDao productInfoDao;
@Transactional @Override public void pay(String accountId, double money) { double blance = accountInfoDao.qryBlanceByUserId(accountId); if(new BigDecimal(blance).compareTo(new BigDecimal(money))<0) { throw new RuntimeException("余额不足"); } ((PayService) AopContext.currentProxy()).updateProductStore(1); int retVal = accountInfoDao.updateAccountBlance(accountId,money); System.out.println(1/0); }
@Transactional(propagation =Propagation.REQUIRES_NEW) @Override public void updateProductStore(Integer productId) { try{ productInfoDao.updateProductInfo(productId); } catch (Exception e) { throw new RuntimeException("内部异常"); } } }
@Configuration @EnableAspectJAutoProxy @EnableTransactionManagement @ComponentScan(basePackages = {"com.study.tx"}) public class TxConfig { @Bean public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUsername("root"); dataSource.setPassword("123456"); dataSource.setUrl("jdbc:mysql://localhost:3306/spring-study"); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); return dataSource; }
@Bean public JdbcTemplate jdbcTemplate(DataSource dataSource) { return new JdbcTemplate(dataSource); }
@Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }
public class TxMain { public static void main(String[] args) { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(TxConfig.class); String[] names = ioc.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } PayService payService = ioc.getBean(PayService.class); payService.pay("123456789",10); } }
@Configuration @EnableAspectJAutoProxy @EnableTransactionManagement @ComponentScan(basePackages = {"com.study.tx"}) public class TxConfig { @Bean public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUsername("root"); dataSource.setPassword("123456"); dataSource.setUrl("jdbc:mysql://localhost:3306/spring-study"); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); return dataSource; }
@Bean public JdbcTemplate jdbcTemplate(DataSource dataSource) { return new JdbcTemplate(dataSource); }
@Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } }
|
如代码所示
payservice.pay() 隔离级别是 Propagation.REQUIRED,加入事务:如果不存在事务,新建事务,存在事务则加入该事务
payService.updateProductStore() 隔离级别是 Propagation.REQUIRES_NEW 新建事务:不管是否存在事务,新建一个事务
具体执行过程如下

穷举所有情况:

事务提交后执行的原理
案例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| public interface VoidSupplier {
void get(); }
@Slf4j public class ThreadPoolUtil {
public static void execute(String threadPoolName, VoidSupplier supplier) { ThreadPoolTaskExecutor executor = SpringUtil.getBean(threadPoolName, ThreadPoolTaskExecutor.class); executor.execute(() -> { try { supplier.get(); } catch (Throwable e) { log.error("Unexpected error occurred invoking thread pool execute", e); } }); } }
@Slf4j @Component public class TransactionalUtil { @Resource private DataSource dataSource;
private static DataSource MY_DATA_SOURCE;
@PostConstruct public void init() { MY_DATA_SOURCE = dataSource; }
public static void afterCommit(VoidSupplier supplier) throws Exception { if (!TransactionSynchronizationManager.isSynchronizationActive()) { supplier.get(); return; }
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { @Override public void afterCommit() { supplier.get(); } }); }
public static void asyncAfterCommit(String threadPoolName,VoidSupplier supplier) { if (!TransactionSynchronizationManager.isSynchronizationActive()) { ThreadPoolUtil.execute(threadPoolName, supplier); return; } TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { @Override public void afterCommit() { ThreadPoolUtil.execute(threadPoolName, supplier); } }); } }
|
其中关键代码TransactionalUtil.afterCommit的原理是什么,我们来一步一步分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
public static void afterCommit(VoidSupplier supplier) throws Exception { if (!TransactionSynchronizationManager.isSynchronizationActive()) { supplier.get(); return; }
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { @Override public void afterCommit() { supplier.get(); } }); }
public static boolean isSynchronizationActive() { return (synchronizations.get() != null); }
|
第1处标注的原理是,以payservice.pay 举例,方法开启了事务,则在执行代理对象执行时
- invokeWithinTransaction
- createTransactionIfNecessary
- status = tm.getTransaction(txAttr);
- doGetTransaction
- isExistingTransaction(transaction)
- suspend(null)(挂机事务)由于不存在事务,不需要挂起当前的
- newTransactionStatus创建一个事务状态
- doBegin
- prepareSynchronization
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) { if (status.isNewSynchronization()) { TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction()); TransactionSynchronizationManager.setCurrentTransactionIsolationLevel( definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ? definition.getIsolationLevel() : null); TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly()) TransactionSynchronizationManager.setCurrentTransactionName(definition.getName()); TransactionSynchronizationManager.initSynchronization(); } }
public static void initSynchronization() throws IllegalStateException { if (isSynchronizationActive()) { throw new IllegalStateException("Cannot activate transaction synchronization - already active"); } logger.trace("Initializing transaction synchronization"); synchronizations.set(new LinkedHashSet<>()); }
|
第2处标注的原理是
invokeWithinTransaction
- createTransactionIfNecessary
- retVal = invocation.proceedWithInvocation();
- commitTransactionAfterReturning(txInfo); 目标方法执行成功,触发事务提交
- txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
- processCommit(defStatus);
- triggerAfterCommit(status);
- TransactionSynchronizationUtils.triggerAfterCommit();
- invokeAfterCommit(TransactionSynchronizationManager.getSynchronizations());
- cleanupAfterCompletion(status);
1 2 3 4 5 6 7 8
| public static void invokeAfterCommit(@Nullable List<TransactionSynchronization> synchronizations) { if (synchronizations != null) { for (TransactionSynchronization synchronization : synchronizations) { synchronization.afterCommit(); } } }
|
小坑:最好不要在TransactionSynchronization.afterCommit方法里执行DML操作,如果在事务提交后抛出异常,这个时候对于
DataSourceTransactionManager
cleanupAfterCompletion(status) 会把连接设置为自动提交,导致DML操作回滚不了(原事务已提交,afterCommit 方法报错,afterCommit 的数据操作回滚不了)
JPASourceTransactionManager
正常回滚
最佳时间:
1、尽量不在aftercommit里做事务操作
2、如果有,也是现实声明为 @Transactional(propagation =Propagation.REQUIRES_NEW),详细看PayServiceImpl中updateProductStoreInNewTx,updateProductStore这两个方法,前者事务正常回滚,后者不行
3、在aftercommit异步执行业务