美文网首页程序员
源码分析: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