在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. NGUI制作可滚动的文本框(摘,如有侵权,联系删除)

    NGUI制作可滚动的文本框 1.首先创建一个UI Root 2.选择UI Root右键 Create 选择Scoll View创建. 3.Scroll view的大小是可以调节的,调节为你需要的适当大 ...

  2. 华科机考:N阶楼梯上楼

    时间限制:1秒空间限制:32768K 题目描述 N阶楼梯上楼问题:一次可以走两阶或一阶,问有多少种上楼方式.(要求采用非递归) 输入描述: 输入包括一个整数N,(1<=N<90). 输出描 ...

  3. Java 中 json字符串转换为类

    使用到alibaba.fastjson包 具体实现 JSONObject jsonObject = JSONObject.parseObject(msg); SmsSenderStatus smsSe ...

  4. 怎样使用Secure CRT查看vcenter和esxi主机的日志文件(转)

    对ESXI主机的日志管理对于故障诊断和合规性至关重要.Esxi主机的日志通过syslog工具进行管理的,在默认的情况下,日志文件存储在主机的scratch分区中(/scratch/log/).scra ...

  5. HashSet与TreeSet

    1.TreeSet 是二差树实现的,Treeset中的数据是自动排好序的,不允许放入null值 2.HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个nu ...

  6. javac编译原理

    javac编译器的作用就是将符合java语言规范的源代码转化成符合java虚拟机规范的java字节码 经历:词法分析器->语法分析器->语义分析器->编译字节码 四个过程生成字节码文 ...

  7. [原创]基于VueJs的前后端分离框架搭建之完全攻略

    首先请原谅本文标题取的有点大,但并非为了哗众取宠.本文取这个标题主要有3个原因,这也是写作本文的初衷: (1)目前国内几乎搜索不到全面讲解如何搭建前后端分离框架的文章,讲前后端分离框架思想的就更少了, ...

  8. 项目实战15.2—企业级堡垒机 jumpserver快速入门

    必备条件 硬件条件 ① 一台安装好 Jumpserver 系统的可用主机(堡垒机) ② 一台或多台可用的 Linux.Windows资产设备(被管理的资产) 服务条件 (1)coco服务 ① 鉴于心态 ...

  9. 剑指架构师系列-Nginx的安装与使用

    Nginx可以干许多事情,在这里我们主要使用Nginx的反向代理与负载均衡功能. 1.Nginx的下载安装 在安装Nginx前需要安装如下软件: GCC  Nginx是C写的,需要用GCC编译 PCR ...

  10. PHP 5 Directory 函数

    PHP Directory 简介 Directory 函数允许您获得关于目录及其内容的信息. 安装 PHP Directory 函数是 PHP 核心的组成部分.无需安装即可使用这些函数. PHP 5 ...