事务管理器在实际的开发过程中主要是配合注解的使用,也可以在代码中手动的创建事务管理器,
注解是结合@Transaction注解,手动的可以在网上找一些资料,创建TransactionTemplate手动的调用execute方法.
事务管理器的作用是什么呢,其实就是connection的自动提交设置成false,方法执行结束后统一的commit或者rollback,道理是很简单的.
image.png
如果没有事务管理器,那两条sql语句,就得手动的获取connection,并讲自动提交设置为false,再统一提交或者回滚
public static void main(String[] args) throws IOException, SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException{
// 1 提供Properties实例对象
Properties pros = new Properties();
FileInputStream fs = new FileInputStream("C:\\Users\\DuoDuo\\Desktop\\codeTest\\springboottest\\src\\main\\resources\\jdbc.properties");
pros.load(fs);
// 2 读取配置信息
String url = pros.getProperty("url");
String user = pros.getProperty("username");
String password = pros.getProperty("password");
String driverClass = pros.getProperty("driver-class-name");
// 4 加载驱动
Class<Driver> clazz = (Class<Driver>) Class.forName(driverClass);
Driver driver = clazz.newInstance();
// 5 注册驱动
DriverManager.registerDriver(driver);
// 6 获取对象
Connection conn = DriverManager.getConnection(url, user, password);
// 7 取消自动提交
conn.setAutoCommit(false);
// 8 获取Statement
PreparedStatement preparedStatement = conn.prepareStatement("select * from empage");
// 9 执行sql 这里省略多条的update语句
ResultSet resultSet = preparedStatement.executeQuery();
// 10 打印
while(resultSet.next()){
int id = resultSet .getInt("id");
String ename = resultSet .getString("ename");
int age = resultSet .getInt("age");
System.out.println(id+ename+age);
}
//11 自动提交 or rollback
conn.commit();
}
在spring项目中,可以从容器中获取datasource,再Connection connection = dataSource.getConnection();获取连接,一般都是从连接池中获取连接
从示例中也可以看到,java是如何与数据库交换数据的,PreparedStatement是驱动自己去实现的,本例使用的是mysql驱动
有了事务管理器之后,十分的方便,如果结合了spring只需要一个注解就可以实现,本文重点分析使用注解的方式,结合mybatis是如何工作的
1.数据源datasource
org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration.Hikari#dataSource
spring默认使用的是HikariDataSource数据源
image.png
2.事务管理器
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration.JdbcTransactionManagerConfiguration#transactionManager
默认使用的是DataSourceTransactionManager
image.png
3.@Transaction如何包装当前类
通过springboot的自动配置特性引入TransactionAutoConfiguration
image.png
spring.factories文件引入TransactionAutoConfiguration配置类
自己通过debug的方式,查看调用链,执行registerAutoProxyCreatorIfNecessary方法
org.springframework.aop.config.AopConfigUtils#registerAutoProxyCreatorIfNecessary(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)
image.png
将InfrastructureAdvisorAutoProxyCreator注入近BeanDefinitionMap中
InfrastructureAdvisorAutoProxyCreator是AbstractAutoProxyCreator接口的子类,注意postProcessAfterInitialization这个方法,这是bean的后置处理器,上面提到InfrastructureAdvisorAutoProxyCreator类已经注入进容器里,当创建bean的时候就会调用postProcessAfterInitialization后置处理器
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization中wrapIfNecessary(bean, beanName, cacheKey)方法
当前的bean是否被aop切面命中,图中getAdvicesAndAdvisorsForBean方法判断当前类或者当前类的方法是否标注了@Transaction,如果标注创建代理类,getAdvicesAndAdvisorsForBean也判断了是否被AOP命中,@AspectJ 的几个注解
image.png
spring中默认添加的关于事务的Advisor,Advisor就是aop的切面,pointcut切点,可以去网上查一下资料
如果类中有一个方法标注了@Transaction,当前类就会被代理
image.png
4.sql的执行
cglib代理类的第一个callback是DynamicAdvisedInterceptor
执行标注了@Transaction注解的方法的时候,会被拦截,图中就是DynamicAdvisedInterceptor,chain就是拦截链路TransactionInterceptor
image.png
方法调用之前会被拦截,跳过一些方法,参考图中跳过的方法,调用到doBegin方法
org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin
image.png
image.png
bindResource方法将连接放到threadlocal中,后续会被获取,因为这里只是创建了一个connection,并没有执行sql,后续再mybatis中从线程中获取连接执行sql
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
image.png
mybatis的执行
org.apache.ibatis.executor.SimpleExecutor#prepareStatement
image.png
image.png
从threadloacl中获取连接,这就保证了spring事务开启的时候创建的connection和mybatis执行sql使用的connection是同一个,因为spring开启事务的时候的连接取消了自动提交,保证了事务
当方法执行结束后事务的提交
org.springframework.transaction.interceptor.TransactionAspectSupport#commitTransactionAfterReturning
image.png
事务结束,整体的事务大概的流程就这式样,保证spring开始事务的时候连接和实际执行sql的连接是同一个,并且开启事务的时候取消自动提交,在事务结束的时候提交事务或者回滚.
网友评论