栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring-事务Transactional

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring-事务Transactional

Spring-事务Transactional 1 基础概念

可参考SpringBoot事务Transaction 你真的懂了么?

2 原理
  1. SpringBoot会将调用事务注解方法所在的对象进行Cglib动态代理,可见:

    NovelManager#insertNovel会将NovelBO中的Author和Book分别插入数据库:

  2. 在调用insertNovel#insertNovel这个用@Transactional注解标记方法之前,利用动态代理加入了一段开启事务的代码:

    这里面TransactionAspectSupport里最重要的关键方法如下:

    // 再调用@Transactional注解的方法前开启一个事务
    TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
    
    // 调用@Transactional注解的方法,使用事务
    retVal = invocation.proceedWithInvocation();
    
    // 处理异常时的retVal
    if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
    	// Set rollback-only in case of Vavr failure matching our rollback rules...
    	TransactionStatus status = txInfo.getTransactionStatus();
    	if (status != null && txAttr != null) {
    		retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
    	}
    }
    // 在用户业务方法返回后提交事务,或是在异常时回滚事务
    // 在后面第8步会详细说正常和异常回滚的情况
    commitTransactionAfterReturning(txInfo);
    
  3. 创建Connection和事务
    可见以下关键日志输出:

    [DEBUG][2021-12-25T11:15:11.147+0800][][][http-nio-8081-exec-1][AbstractPlatformTransactionManager.java:370]:Creating new transaction with name [com.mljk.springboot.demo.manager.NovelManager.insertNovel]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,timeout_10000,-java.lang.Exception 
    [DEBUG][2021-12-25T11:15:11.157+0800][][][http-nio-8081-exec-1][HikariConfig.java:1098]:transactionIsolation............default 
    [DEBUG][2021-12-25T11:15:11.329+0800][][][http-nio-8081-exec-1][DataSourceTransactionManager.java:263]:Acquired Connection [HikariProxyConnection@221551358 wrapping com.mysql.cj.jdbc.ConnectionImpl@1f695a36] for JDBC transaction 
    [DEBUG][2021-12-25T11:15:11.331+0800][][][http-nio-8081-exec-1][DataSourceTransactionManager.java:281]:Switching JDBC Connection [HikariProxyConnection@221551358 wrapping com.mysql.cj.jdbc.ConnectionImpl@1f695a36] to manual commit 
    

    注意这里创建了数据库 Connection:

    这里创建的Connection是HikariProxyConnection@xxx wrapping com.mysql.cj.jdbc.ConnectionImpl@yyy

    这里Spring还帮我们为NovelManager.insertNovel创建了新的事务,然后改为了手动提交事务。

  4. 开始执行Author插入方法调用

    可看到以下关键日志

    [INFO ][2021-12-25T11:15:18.199+0800][][][http-nio-8081-exec-1][AuthorService.java:20]:start to insert author:Author(id=null, name=Tom2, nation=CHN) 
    
    [DEBUG][2021-12-25T11:15:18.207+0800][][][http-nio-8081-exec-1][Logger.java:49]:Creating a new SqlSession 
    [DEBUG][2021-12-25T11:15:18.213+0800][][][http-nio-8081-exec-1][Logger.java:49]:Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@555c2272] 
    [DEBUG][2021-12-25T11:15:18.237+0800][][][http-nio-8081-exec-1][Logger.java:49]:JDBC Connection [HikariProxyConnection@221551358 wrapping com.mysql.cj.jdbc.ConnectionImpl@1f695a36] will be managed by Spring 
    [DEBUG][2021-12-25T11:15:18.241+0800][][][http-nio-8081-exec-1][baseJdbcLogger.java:137]:==>  Preparing: INSERT INTO author ( name, nation ) VALUES ( ?, ? ) 
    [DEBUG][2021-12-25T11:15:18.271+0800][][][http-nio-8081-exec-1][baseJdbcLogger.java:137]:==> Parameters: Tom2(String), CHN(String) 
    [DEBUG][2021-12-25T11:15:18.274+0800][][][http-nio-8081-exec-1][baseJdbcLogger.java:137]:<==    Updates: 1 
    [DEBUG][2021-12-25T11:15:18.289+0800][][][http-nio-8081-exec-1][Logger.java:49]:Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@555c2272] 
    
    [INFO ][2021-12-25T11:15:18.289+0800][][][http-nio-8081-exec-1][AuthorService.java:22]:finished inserting author:Author(id=1, name=Tom2, nation=CHN) 
    

    可知,在调用mybatis插入数据时,帮我们创建了SqlSession并注册同步到了事务,然后PreaparedStatement方式开始执行SQL,最后释放了这个事务化的SqlSession。

  5. 这个时候我们查询数据库,是没有这条新数据的

  6. 继续执行Book插入方法调用

    日志如下:

    [INFO ][2021-12-25T11:15:18.289+0800][][][http-nio-8081-exec-1][BookService.java:20]:start to insert book:Book(id=null, name=ThreeBody, price=128.0, authorId=1, publishDate=2012-12-12 10:18:00.0) 
    
    [DEBUG][2021-12-25T11:15:18.290+0800][][][http-nio-8081-exec-1][Logger.java:49]:Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@555c2272] from current transaction 
    [DEBUG][2021-12-25T11:15:18.291+0800][][][http-nio-8081-exec-1][baseJdbcLogger.java:137]:==>  Preparing: INSERT INTO book ( name, price, author_id, publish_date ) VALUES ( ?, ?, ?, ? ) 
    [DEBUG][2021-12-25T11:15:18.297+0800][][][http-nio-8081-exec-1][baseJdbcLogger.java:137]:==> Parameters: ThreeBody(String), 128.0(Double), 1(Long), 2012-12-12 10:18:00.0(Timestamp) 
    [DEBUG][2021-12-25T11:15:18.298+0800][][][http-nio-8081-exec-1][baseJdbcLogger.java:137]:<==    Updates: 1 
    [DEBUG][2021-12-25T11:15:18.298+0800][][][http-nio-8081-exec-1][Logger.java:49]:Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@555c2272] 
    
    [INFO ][2021-12-25T11:15:18.298+0800][][][http-nio-8081-exec-1][BookService.java:22]:finished inserting book:Book(id=1, name=ThreeBody, price=128.0, authorId=1, publishDate=2012-12-12 10:18:00.0) 
    

    可见这里从当前同一个事务中获取了DefaultSqlSession@551e31e0,然后执行了SQL插入数据。

    特别注意,这个时候,事务还没有提交,数据库里面依然无数据:

  7. 处理SqlSession
    可见关键日志:

    [DEBUG][2021-12-25T11:17:32.866+0800][][][http-nio-8081-exec-1][Logger.java:49]:Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@555c2272] 
    [DEBUG][2021-12-25T11:17:42.551+0800][][][http-nio-8081-exec-1][Logger.java:49]:Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@555c2272] 
    [DEBUG][2021-12-25T11:17:44.379+0800][][][http-nio-8081-exec-1][Logger.java:49]:Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@555c2272] 
    

    可见事务同步已经提交、反注册最终关闭了该SqlSession,但此时事务还未提交,数据库依然无法查到插入的数据。

  8. 正常时提交事务
    上接前面说的commitTransactionAfterReturning(txInfo);方法,调用栈如下:

    关键逻辑可见AbstractPlatformTransactionManager#processCommit,其内定义了一些事务执行前后触发的事件。比如事务提交后重置连接、释放连接等。

    这一步关键日志如下:

    [DEBUG][2021-12-25T11:17:54.501+0800][][][http-nio-8081-exec-1][AbstractPlatformTransactionManager.java:740]:Initiating transaction commit 
    [DEBUG][2021-12-25T11:18:10.992+0800][][][http-nio-8081-exec-1][DataSourceTransactionManager.java:326]:Committing JDBC transaction on Connection [HikariProxyConnection@221551358 wrapping com.mysql.cj.jdbc.ConnectionImpl@1f695a36] 
    [DEBUG][2021-12-25T11:42:54.542+0800][][][http-nio-8081-exec-1][DataSourceTransactionManager.java:385]:Releasing JDBC Connection [HikariProxyConnection@221551358 wrapping com.mysql.cj.jdbc.ConnectionImpl@1f695a36] after transaction 
    

    可见事务通过此前拥有的JDBC Connection进行了提交,完成后释放了连接。此后我们可以在数据库查到插入的数据了:

  9. 异常时事务回滚
    前面讲到的

还可参考:

  • Spring中的@Transactional注解为什么要加rollbackFor = Exception.class之源码解析
  • SpringBoot中的Transaction研究(一)TransactionManager
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/678349.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号