美文网首页程序员
源码分析:Spring 事务 @Transactional Ti

源码分析:Spring 事务 @Transactional Ti

作者: 郑印 | 来源:发表于2020-12-26 14:39 被阅读0次

    本文通过分析Spring 事务的源码,说明@Transactional Timeout 参数设置的一些问题。

    从问题开始,下面两段代码,事务是否都能正常的回滚?

    timeout 参数大于3

    代码片段1

        @Transactional(rollbackFor = Exception.class , propagation = Propagation.REQUIRED,timeout = 3)
        public int timeout(int timeout,int blogId){
            jdbcTemplate.execute("DELETE FROM blog where id = "+blogId);
            Sleep.second(timeout);
            jdbcTemplate.execute("SELECT 1");
            return timeout;
        }
    

    代码片段2

        @Transactional(rollbackFor = Exception.class , propagation = Propagation.REQUIRED,timeout = 3)
        public int timeout(int timeout , int blogId){
            jdbcTemplate.execute("DELETE FROM blog where id = "+blogId);
            Sleep.second(timeout);
            return timeout;
        }
    

    按照我们的认知,上面的代码都应该因为超时抛出异常从而触发 rollback ,但是实际运行结果是 代码片段2 不会回滚,如果在生产环境意味着数据会真实的被删除

    带着问题来看源码

    1. 首先我们从Timeout的异常堆栈追踪到异常抛出的代码
    1.png
    1. 定位到ResourceHolderSupport#checkTransactionTimeout 方法,查看源码
    2.png

    通过源码可以看到,源码通过一个 dealline (Date) 与当前时间比较来判断是否超时,那么这会就产生了两个疑问:

    • dealline 在哪里设置的?
    • 什么时候调用checkTransactionTimeout?
    1. 跟踪 dealline 属性,找到设置的方法 ResourceHolderSupport#setTimeoutInMillis,通过Debug面板,追踪调用堆栈,可以定位到DataSourceTransactionManager#doBegin方法
    3.png
    1. 查看 DataSourceTransactionManager#doBegin 方法,定位到设置timeout的代码端,到这里我们第一个问题得到了答案


      4.png
    2. 通过ResourceHolderSupport#checkTransactionTimeout堆栈找到调用方法

      • DataSourceUtils#applyTimeout
      • JdbcTemplate#applyStatementSettings
    • JdbcTemplate#execute
    5.png 6.png 7.png

    从这里就能清楚的知道, checkTransactionTimeout 是在每次执行SQL的时候被调用的,这也就能说明为什么代码片段1可以正常回滚了,因为代码片段1在最后执行了一条无意义的SQL “SELECT 1” 触发了 checkTransactionTimeout 的检查。

    整个检查过程抽象为流程图就是下面这样

    process.png

    本文源码地址

    相关文章

      网友评论

        本文标题:源码分析:Spring 事务 @Transactional Ti

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