美文网首页
spring的事务管理器

spring的事务管理器

作者: 念䋛 | 来源:发表于2022-09-23 15:51 被阅读0次

    事务管理器在实际的开发过程中主要是配合注解的使用,也可以在代码中手动的创建事务管理器,
    注解是结合@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的连接是同一个,并且开启事务的时候取消自动提交,在事务结束的时候提交事务或者回滚.

    相关文章

      网友评论

          本文标题:spring的事务管理器

          本文链接:https://www.haomeiwen.com/subject/iefnortx.html