深入分析Spring混合事务
在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混合事务的更多相关文章
- 阿里大牛带你深入分析spring事务传播行为
spring框架封装了很多有用的功能和组件,便于在项目开发中快速高效的调用,其中spring的事务使用非常简单,只需要在用到事务的地方加一行注解即可: 1@Transactional 但越是看起来简单 ...
- Spring高级事务管理难点剖析
1Spring事务传播行为 所谓事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播.Spring支持7种事务传播行为 PROPAGATION_REQUIRED(加入已有事务) 如果当前没 ...
- 开涛spring3(9.4) - Spring的事务 之 9.4 声明式事务
9.4 声明式事务 9.4.1 声明式事务概述 从上节编程式实现事务管理可以深刻体会到编程式事务的痛苦,即使通过代理配置方式也是不小的工作量. 本节将介绍声明式事务支持,使用该方式后最大的获益是简 ...
- Spring的事务 之 9.4 声明式事务 ——跟我学spring3
9.4 声明式事务 9.4.1 声明式事务概述 从上节编程式实现事务管理可以深刻体会到编程式事务的痛苦,即使通过代理配置方式也是不小的工作量. 本节将介绍声明式事务支持,使用该方式后最大的获益是简 ...
- 深入分析Spring 与 Spring MVC容器
1 Spring MVC WEB配置 Spring Framework本身没有Web功能,Spring MVC使用WebApplicationContext类扩展ApplicationContext, ...
- Spring的事务管理
事务 事务:是逻辑上一组操作,要么全都成功,要么全都失败. 事务特性(ACID) 原子性:事务不可分割 一致性:事务执行的前后,数据完整性保持一致 隔离性:一个事务执行的时候,不应该受到其他事务的打扰 ...
- spring笔记--事务管理之声明式事务
事务简介: 事务管理是企业级应用开发中必不可少的技术,主要用来确保数据的完整性和一致性, 事务:就是一系列动作,它们被当作一个独立的工作单元,这些动作要么全部完成,要么全部不起作用. Spring中使 ...
- (spring-第20回【AOP基础篇】)Spring与事务
要想了解Spring的事务,首先要了解数据库事务的基本知识,数据库并发会产生很多问题,Spring使用ThreadLocal技术来处理这些问题,那么我们必须了解Java的ThreadLocal技术.下 ...
- spring的事务操作
我们项目一期已经差不多结束了,所以一些细节也被拿了出来,出现最多的就是事务的操作了.因为自己负责的是一个模块(因为是另外一个项目的负责人),所以组员经常会遇到事务的问题,会出现很多奇葩的用法,各种乱用 ...
随机推荐
- Android编写点击TextView拨打电话
在任何一个电商平台都会有点击了手机号码会拨打出一个电话 那么高如何实现这个功能,我们下来分析下原理 当我们点击了一个电话号码后,会弹出一个Dialog显示是否拨打次电话号码,点击确定拨打号码,点击取消 ...
- Python中list的功能介绍
List的功能介绍 1. 列表的两种方法 1. 列表的内置方法 列表的相加 格式:x.__add__(y)等同于x+y 例如:list1 = [1,2,3] print(list1.__add__([ ...
- teachable-machine:探索机器学习如何工作,浏览器中实时浏览
教学机器是一个实验,让所有人都非常方便的探索机器学习,在浏览器中实时浏览,不需要编程.学习更多实验,然后亲自尝试它 访问:https://teachablemachine.withgoogle.com ...
- 如何使用 TeamViewer 配置QuickConnect按钮?
QuickConnect作为TeamViewer中一个比较重要的部分,得到了很多用户的认可.那么在实际运用中,怎么才能设置网页或单个程序的QuickConnect呢?所以小编以此问题为例,教大家如何配 ...
- Java HttpClient伪造请求之简易封装满足HTTP以及HTTPS请求
HttpClient简介 HTTP 协议可能是现在 Internet 上使用得最多.最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源.虽然在 JDK 的 jav ...
- 0. 迷之 -> 和 .
0. 迷之 -> 和 . 箭头(->):左边必须为指针: 点号(.):左边必须为实体. e.g.1 class class A{ public: play(); }; int main() ...
- Promise--优雅的异步回调解决方案
当一个接口需要依赖另一个接口的请求数据时,通常有两种解决方式,一个是将请求数据的接口设为同步,之后调另一个接口,另一个是在请求数据接口的成功回调里调另一个接口. 但是:当一个接口需要依赖很多个接口的请 ...
- 转:Xming + PuTTY 在Windows下远程Linux主机使用图形界面的程序
一.原理Linux/Unix的X Window具有网络透明性.X Window系统里有一个统一的Server来负责各个程序与显示器.键盘和鼠标等输入输出设备的交互,每个有GUI的应用程序都通过网络协议 ...
- Vue结合slot插槽分发父组件内容实现高度复用、更加灵活的dialog组件
之前写过一篇关于vue实现dialog会话框组件的文章(http://www.cnblogs.com/fozero/p/8546883.html)[http://www.cnblogs.com/foz ...
- 实例演示如何在spring4.2.2中集成hibernate5.0.2并创建sessionFactory
本文地址:http://blog.csdn.net/sushengmiyan/article/details/49388209 文章作者:苏生米沿 本文目的:使用spring4.2.2集成hibern ...