在ORM框架的事务管理器的事务内,使用JdbcTemplate执行SQL是不会纳入事务管理的。

下面进行源码分析,看为什么必须要在DataSourceTransactionManager的事务内使用JdbcTemplate。

1开启事务

DataSourceTransactionManager

===============================================================================

         protected void doBegin(Object transaction,TransactionDefinition definition) {
DataSourceTransactionObjecttxObject = (DataSourceTransactionObject) transaction;
Connection con = null; try {
if(txObject.getConnectionHolder() == null ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()){
ConnectionnewCon = this.dataSource.getConnection();
if(logger.isDebugEnabled()) {
logger.debug("AcquiredConnection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(newConnectionHolder(newCon), true);
} txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con =txObject.getConnectionHolder().getConnection(); IntegerpreviousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con,definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel); // Switch to manualcommit if necessary. This is very expensive in some JDBC drivers,
// so we don't wantto do it unnecessarily (for example if we've explicitly
// configured theconnection pool to set it already).
if(con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if(logger.isDebugEnabled()) {
logger.debug("SwitchingJDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
txObject.getConnectionHolder().setTransactionActive(true); int timeout =determineTimeout(definition);
if (timeout !=TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
} // Bind the sessionholder to the thread.
if(txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(getDataSource(),txObject.getConnectionHolder());
}
} catch (Exception ex) {
DataSourceUtils.releaseConnection(con,this.dataSource);
throw newCannotCreateTransactionException("Could not open JDBC Connection fortransaction", ex);
}
}

doBegin()方法会以数据源名为Key,ConnectionHolder(保存着连接)为Value,将已经开启事务的数据库连接绑定到一个ThreadLocal变量上。

2绑定连接

TransactionSynchronizationManager

===============================================================================

         public static void bindResource(Objectkey, Object value) throws IllegalStateException {
Object actualKey =TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Assert.notNull(value,"Value must not be null");
Map<Object, Object> map = resources.get();
// set ThreadLocal Map ifnone found
if (map == null) {
map = newHashMap<Object, Object>();
resources.set(map);
}
Object oldValue = map.put(actualKey, value);
// Transparently suppress aResourceHolder that was marked as void...
if (oldValue instanceofResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
oldValue = null;
}
if (oldValue != null) {
throw newIllegalStateException("Already value [" + oldValue + "] for key[" +
actualKey+ "] bound to thread [" + Thread.currentThread().getName() +"]");
}
if (logger.isTraceEnabled()){
logger.trace("Boundvalue [" + value + "] for key [" + actualKey + "] to thread[" +
Thread.currentThread().getName()+ "]");
}
}

resources变量就是上面提到的ThreadLocal变量,这样后续JdbcTemplate就可以用DataSource作为Key,查找到这个数据库连接。

3执行SQL

JdbcTemplate

===============================================================================

         public Objectexecute(PreparedStatementCreator psc, PreparedStatementCallback action)
throwsDataAccessException { Assert.notNull(psc,"PreparedStatementCreator must not be null");
Assert.notNull(action,"Callback object must not be null");
if (logger.isDebugEnabled()){
String sql =getSql(psc);
logger.debug("Executingprepared SQL statement" + (sql != null ? " [" + sql +"]" : ""));
} Connection con = DataSourceUtils.getConnection(getDataSource());
PreparedStatement ps = null;
try {
Connection conToUse= con;
if(this.nativeJdbcExtractor != null &&
this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()){
conToUse =this.nativeJdbcExtractor.getNativeConnection(con);
}
ps =psc.createPreparedStatement(conToUse);
applyStatementSettings(ps);
PreparedStatementpsToUse = ps;
if(this.nativeJdbcExtractor != null) {
psToUse =this.nativeJdbcExtractor.getNativePreparedStatement(ps);
}
Object result =action.doInPreparedStatement(psToUse);
handleWarnings(ps);
return result;
}
catch (SQLException ex) {
// ReleaseConnection early, to avoid potential connection pool deadlock
// in the case whenthe exception translator hasn't been initialized yet.
if (psc instanceofParameterDisposer) {
((ParameterDisposer)psc).cleanupParameters();
}
String sql =getSql(psc);
psc = null;
JdbcUtils.closeStatement(ps);
ps = null;
DataSourceUtils.releaseConnection(con,getDataSource());
con = null;
throwgetExceptionTranslator().translate("PreparedStatementCallback", sql,ex);
}
finally {
if (psc instanceofParameterDisposer) {
((ParameterDisposer)psc).cleanupParameters();
}
JdbcUtils.closeStatement(ps);
DataSourceUtils.releaseConnection(con,getDataSource());
}
}

4获得连接

DataSourceUtils

===============================================================================

        public static Connection doGetConnection(DataSourcedataSource) throws SQLException {
Assert.notNull(dataSource,"No DataSource specified"); ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null&& (conHolder.hasConnection() ||conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if(!conHolder.hasConnection()) {
logger.debug("Fetchingresumed JDBC Connection from DataSource");
conHolder.setConnection(dataSource.getConnection());
}
returnconHolder.getConnection();
}
// Else we either got noholder or an empty thread-bound holder here. logger.debug("FetchingJDBC Connection from DataSource");
Connection con =dataSource.getConnection(); if (TransactionSynchronizationManager.isSynchronizationActive()){
logger.debug("Registeringtransaction synchronization for JDBC Connection");
// Use sameConnection for further JDBC actions within the transaction.
// Thread-boundobject will get removed by synchronization at transaction completion.
ConnectionHolderholderToUse = conHolder;
if (holderToUse ==null) {
holderToUse= new ConnectionHolder(con);
}
else {
holderToUse.setConnection(con);
}
holderToUse.requested();
TransactionSynchronizationManager.registerSynchronization(
newConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse !=conHolder) {
TransactionSynchronizationManager.bindResource(dataSource,holderToUse);
}
} return con;
}

由此可见,DataSourceUtils也是通过TransactionSynchronizationManager获得连接的。所以只要JdbcTemplate与DataSourceTransactionManager有相同的DataSource,就一定能得到相同的数据库连接,自然就能正确地提交、回滚事务。

再以Hibernate为例来说明开篇提到的问题,看看为什么ORM框架的事务管理器不能管理JdbcTemplate。

5 ORM事务管理器

HibernateTransactionManager

===============================================================================

                            if(txObject.isNewSessionHolder()) {
TransactionSynchronizationManager.bindResource(getSessionFactory(),txObject.getSessionHolder());
}

因为ORM框架都不是直接将DataSource注入到TransactionManager中使用的,而是像上面Hibernate事务管理器一样,使用自己的SessionFactory等对象来操作DataSource。所以尽管可能SessionFactory和JdbcTemplate底层都是一样的数据源,但因为在TransactionSynchronizationManager中绑定时使用了不同的Key(一个是sessionFactory名,一个是dataSource名),所以JdbcTemplate执行时是拿不到ORM事务管理器开启事务的那个数据库连接的。

深入分析Spring混合事务的更多相关文章

  1. 阿里大牛带你深入分析spring事务传播行为

    spring框架封装了很多有用的功能和组件,便于在项目开发中快速高效的调用,其中spring的事务使用非常简单,只需要在用到事务的地方加一行注解即可: 1@Transactional 但越是看起来简单 ...

  2. Spring高级事务管理难点剖析

    1Spring事务传播行为 所谓事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播.Spring支持7种事务传播行为 PROPAGATION_REQUIRED(加入已有事务) 如果当前没 ...

  3. 开涛spring3(9.4) - Spring的事务 之 9.4 声明式事务

    9.4  声明式事务 9.4.1  声明式事务概述 从上节编程式实现事务管理可以深刻体会到编程式事务的痛苦,即使通过代理配置方式也是不小的工作量. 本节将介绍声明式事务支持,使用该方式后最大的获益是简 ...

  4. Spring的事务 之 9.4 声明式事务 ——跟我学spring3

    9.4  声明式事务 9.4.1  声明式事务概述 从上节编程式实现事务管理可以深刻体会到编程式事务的痛苦,即使通过代理配置方式也是不小的工作量. 本节将介绍声明式事务支持,使用该方式后最大的获益是简 ...

  5. 深入分析Spring 与 Spring MVC容器

    1 Spring MVC WEB配置 Spring Framework本身没有Web功能,Spring MVC使用WebApplicationContext类扩展ApplicationContext, ...

  6. Spring的事务管理

    事务 事务:是逻辑上一组操作,要么全都成功,要么全都失败. 事务特性(ACID) 原子性:事务不可分割 一致性:事务执行的前后,数据完整性保持一致 隔离性:一个事务执行的时候,不应该受到其他事务的打扰 ...

  7. spring笔记--事务管理之声明式事务

    事务简介: 事务管理是企业级应用开发中必不可少的技术,主要用来确保数据的完整性和一致性, 事务:就是一系列动作,它们被当作一个独立的工作单元,这些动作要么全部完成,要么全部不起作用. Spring中使 ...

  8. (spring-第20回【AOP基础篇】)Spring与事务

    要想了解Spring的事务,首先要了解数据库事务的基本知识,数据库并发会产生很多问题,Spring使用ThreadLocal技术来处理这些问题,那么我们必须了解Java的ThreadLocal技术.下 ...

  9. spring的事务操作

    我们项目一期已经差不多结束了,所以一些细节也被拿了出来,出现最多的就是事务的操作了.因为自己负责的是一个模块(因为是另外一个项目的负责人),所以组员经常会遇到事务的问题,会出现很多奇葩的用法,各种乱用 ...

随机推荐

  1. Mysql参数汇总

    凡是需要耐心. 参数为静态参数则黄色字体标记. 参数为全局变量则粗体标记. 参数为全局.会话变量则不标记. auto_increment_increment auto_increment_offset ...

  2. js打印小结

    <script type="text/javascript"> //打印必备参数 var hkey_root,hkey_path,hkey_key; hkey_root ...

  3. 谷歌发布 TensorFlow Lite [官方网站,文档]

    机器学习社区:http://tensorflow123.com/ 简介 TensorFlow Lite TensorFlow Lite 是 TensorFlow 针对移动和嵌入式设备的轻量级解决方案. ...

  4. Hinton's paper Dynamic Routing Between Capsules 的 Tensorflow , Keras ,Pytorch实现

    Tensorflow 实现 A Tensorflow implementation of CapsNet(Capsules Net) in Hinton's paper Dynamic Routing ...

  5. Tinychain 是比特币的一个简易口袋实现

    Putting the rough in "rough consensus" Tinychain is a pocket-sized implementation of Bitco ...

  6. 阿里架构师带你深入浅出jvm

    本文跟大家聊聊JVM的内部结构,从组件中的多线程处理,JVM系统线程,局部变量数组等方面进行解析 JVM JVM = 类加载器(classloader) + 执行引擎(execution engine ...

  7. 用burpsuite暴力破解后台

    [实验原理] Burp Suite是Web应用程序测试的最佳工具之一,其多种功能执行各种任务.请求的拦截和修改,扫描web应用程序漏洞,以暴力破解登录表单,执行会话令牌等多种的随机性检查. Burp ...

  8. Goland 提示 :configuration is still incorrect 的解决

    安装好 Goland 后,调试编译的时候提示 goland configuration is still incorrect,百度 和 Google 都没有明确答案 Google 上有一些提示,但是也 ...

  9. 用python爬了自己的微信,原来好友都是这样的!

    偶然了解到Python里的itchat包,它已经完成了wechat的个人账号API接口,使爬取个人微信信息更加方便.鉴于自己很早之前就想知道诸如自己微信好友性别比例都来自哪个城市之类的问题,于是乎玩心 ...

  10. 实体类双向映射进行Json序列化时出现无限循环的解决问题

    1.@JsonIgnoreProperties 指定的字段不会被序列化,如下则ExamPaper的directory字段不会被序列化 @OneToMany(mappedBy = "direc ...