在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. Java连接FTP成功,但是上传是失败,报错:Connected time out

    Java代码在本机上传文件到FTP服务器的时候成功,但是部署到测试服务器的时候出现,连接FTP成功但是上传失败,并且报Connected time out错误: 测试服务器和FTP服务都在阿里云上:( ...

  2. C语言程序第二次作业

    (一)改错题 1.输出带框文字:在屏幕上输出以下3行信息. ************* Welcome ************* 源程序 include int mian() { printf(&q ...

  3. rasa_core:基于机器学习的对话引擎

    用机器学习管理你的对话,让它提升每一个对话.Rasa Core引导对话,考虑对话的历史和外部环境. 而不是成千上万的规则,Rasa 从真正的对话中挑选模式. 现在是扔掉你的状态机的时候了! Manag ...

  4. JSSDK实现微信自定义分享---java 后端获取签名信息

    一.首先说下关于微信Access_token的问题,微信Access_token分为2中: 1.授权token获取方式: 这种token需要code值(如何获取code值查看官方文档) "h ...

  5. 入口文件开始,分析Vue源码实现

    Why? 网上现有的Vue源码解析文章一搜一大批,但是为什么我还要去做这样的事情呢?因为觉得纸上得来终觉浅,绝知此事要躬行. 然后平时的项目也主要是Vue,在使用Vue的过程中,也对其一些约定产生了一 ...

  6. JSON.parse(),JSON.stringify(),jQuery.parseJSON()

    http://www.jb51.net/article/29893.htm http://www.jb51.net/article/87642.htm

  7. JQuery when() done() then()

    jQuery.when(deferreds) 参数deferreds,一个或多个延时对象或JS对象,我们初略的认为它就是一个或多个异步请求. 例如:$.when($.ajax("page1. ...

  8. SpringMVC mock测试详解

    @RunWith(SpringRunner.class) @SpringBootTest(classes = WebmanagerApplication.class) //配置事务的回滚,对数据库的增 ...

  9. 如何joomla修改版权信息

    1.在language\zh_CN目录下有一个zh-CN.mod_footer.ini文件,修改里面的内容: 2.具体模板的html\mod_footer目录下的default.php文件内(具体文件 ...

  10. 自定义Java注解的方式与应用

    注解的作用 Annotation(注解)是JDK 5.0引入的特性,它的基本作用就是修饰编程元素. 注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记.编译器.开发工具或其他程序可以用反射 ...