- 什么是事务
保证业务操作完整性的一种数据库机制 (driver 驱动)- 事务特定 ACID
A 原子性 (多次操作 要不一起成功 要不一起失败 (部分失败 savepoint))
C 一致性 (事务开始时数据状态,事务结束是数据状态 一致 )
I 隔离性 (多个事务不能互相影响,做到隔离)
D 持久性 (事务操作的结果 ,永久持久化在数据库)- 事务
单机版本 和 分布式版本事务- spring控制事务
核心要点:通过AOP方式创建事务。
方式:编程式事务、声明式事务
一个事务的demo
@Configuration
@ComponentScan("com.qxlx.tx.annotation")
@EnableTransactionManagement
public class AppConfig {
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername("root");
dataSource.setPassword("rootroot");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
@Transactional
public void register(User user) {
userDao.save(user);
}
}
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void save(User user) {
jdbcTemplate.update("insert into user(name,age) values (?,?)",user.getName(),user.getAge());
}
}
测试类
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) ioc.getBean("userServiceImpl");
User user = new User();
user.setName("Qxlx");
user.setAge(27);
userService.register(user);
我们来思考,有哪些核心流程,其实对应AOP来说,1 前置处理类以及生成代理类的过程 2.在生成代理类之后,通过代理类对象执行最终方法的调用的拦截处理。
其实事务也是基于AOP进行完成功能的,所以整体也是这两个部分。
事务基础
事务属性的目的,为了更好的描述 事务的特点
隔离级别
## 传播属性
## 只读、超时、异常
前置类的初始化
在AppConfig类 引入了一个注解 @EnableTransactionManagement,我们知道一半Enable注解都是通过Import的方式引入外部类 然后去完成对应的功能。
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
所以实际上是引入了两个类。
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
}
AutoProxyRegistrar
查看类结果图,发现其实本质就是BPP的实现类,
所以总结一下通过@enable注解 注册一个BPP,然后BPP实例化之后,在实例化自定义bean的时候去生成代理类。剩下的代码就不过看了。
ProxyTransactionManagementConfiguration
这个TransactionInterceptor 拦截器很关键,在运行具体方法的时候,使用的。
运行时的核心流程
userService.register(user);
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
开启事务-createTransactionIfNecessary
getTransaction
获取事务的状态,同时开启事务
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
// new一个 DataSourceTransactionObject对象
// 同时从threadLocal中获取Connect datasource为key
// 第一次为空
Object transaction = doGetTransaction();
// 是否存在事务
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 开启事务
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = obtainDataSource().getConnection();
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);
con.setAutoCommit(false);
}
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// Bind the connection holder to the thread.
// 绑定连接到ThreadLocal中
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
prepareTransactionInfo
进行TransactionInfo封装,
1.封装TransactionInfo对象, 将新的transactionInfo存储起来到ThreadLocal。
清除事务信息 cleanupTransactionInfo
将外部事务进行恢复
事务提交 commitTransactionAfterReturning
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
事务回滚 completeTransactionAfterThrowing
原始方法 抛出异常一定回滚吗,
Exception及其子类 默认提交
RuntimeException及其子类 或者Error错误
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
}
可以发现默认抛出的异常时 RuntimeException 子类才会进行事务的回滚,或者是Error的类型。
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
那么到这里就结束了吗,其实上面罗列的只是一个简单的流程。我们知道Spring有几大特性,其中最为复杂的传播属性是Spring特有的机制,那么他是如何完整多个事务嵌套的场景执行的呢?
传播机制原理核心原理
其实大概的流程 如果用伪代码来描述的话大概如下,假设我们执行的流程是这样。
addUser() ; 传播属性为REQUIRED
addBlack() ; 默认传播属性 REQUIRED_NEW
1.先执行外部addUser(); 创建连接->反射执行目标方法addUser()->接着执行addBlack()-> (1.挂起addUser事务 2.创建新事务 3.执行addBlack()-> 提交事务 4.恢复addUser事务)-> addBlack 提交事务 完成
其实问题的核心点,我们需要找到挂起事务的流程 以及恢复事务的流程 看看代码是如何实现的,基本上了解spring底层是如何完成这个处理的。
总结
spring事务整体的核心流程 其实就是在AOP是在IOC基础上,动态创建代理对象,事务在IOC+AOP的基础上 创建事务代理对象,完成一系列的功能。源码内容本身是比较复杂和关系比较错乱,如果想每行源码都搞清楚,那么不太现实,所以核心就是抓主干逻辑,梳理清楚核心关键点,在看细节。