Spring事务管理与数据库连接

原创 2005年05月30日 10:23:00

Spring中的DataSource 事务。
DataSource事务相关的类比较多,我们一步步来拨开其中的密团。

1 如何获得连接
看DataSourceUtils代码
java代码: 

protected static Connection doGetConnection(DataSource dataSource, boolean allowSynchronization)
                        throws SQLException {
               
                ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
                if (conHolder != null) {
                        conHolder.requested();
                        return conHolder.getConnection();
                }

               
                Connection con = dataSource.getConnection();
                if (allowSynchronization && TransactionSynchronizationManager.isSynchronizationActive()) {
                                                conHolder = new ConnectionHolder(con);
                        TransactionSynchronizationManager.bindResource(dataSource, conHolder);
                        TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(conHolder, dataSource));
                        conHolder.requested();
                }

                return con;
        }
 

原来连接是从TransactionSynchronizationManager中获取,如果TransactionSynchronizationManager中已经有了,那么拿过来然后调用conHolder.requested()。否则从原始的DataSource这创建一个连接,放到一个ConnectionHolder,然后再调用TransactionSynchronizationManager.bindResource绑定。
好,我们又遇到两个新的类TransactionSynchronizationManager和ConnectionHolder和。继续跟踪


2 TransactionSynchronizationManager
看其中的一些代码
java代码: 

private static ThreadLocal resources = new ThreadLocal();
public static Object getResource(Object key) {
                Map map = (Map) resources.get();
                if (map == null) {
                        return null;
                }
                Object value = map.get(key);
                                return value;
        }
public static void bindResource(Object key, Object value) throws IllegalStateException {
                Map map = (Map) resources.get();
                                if (map == null) {
                        map = new HashMap();
                        resources.set(map);
                }
                map.put(key, value);
                        }
 
原来TransactionSynchronizationManager内部建立了一个ThreadLocal的resources,这个resources又是和一个map联系在一起的,这个map在某个线程第一次调用bindResource时生成。
联系前面的DataSourceUtils代码,我们可以总结出来。
某个线程使用DataSourceUtils,当第一次要求创建连接将在TransactionSynchronizationManager中创建出一个ThreadLocal的map。然后以DataSource作为键,ConnectionHolder为值放到map中。等这个线程下一次再请求的这个DataSource的时候,就从这个map中获取对应的ConnectionHolder。用map是为了解决同一个线程上多个DataSource。
然后我们来看看ConnectionHolder又是什么?

3 对连接进行引用计数
看ConnectionHolder代码,这个类很简单,看不出个所以然,只好再去看父类代码ResourceHolderSupport,我们感兴趣的是这两个方法
java代码: 

public void requested() {
                this.referenceCount++;
        }

        public void released() {
                this.referenceCount--;
        }
 

看得出这是一个引用计数的技巧。原来Spring中对Connection是竟量使用已创建的对象,而不是每次都创建一个新对象。这就是DataSourceUtils中 java代码: 

if (conHolder != null) {
                        conHolder.requested();
                        return conHolder.getConnection();
                }
 
的原因


4 释放连接
完成事物后DataSourceTransactionManager有这样的代码
java代码: 

protected void doCleanupAfterCompletion(Object transaction) {
                DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;

                // Remove the connection holder from the thread.
                TransactionSynchronizationManager.unbindResource(this.dataSource);
                txObject.getConnectionHolder().clear();

                //...                DataSourceUtils.closeConnectionIfNecessary(con, this.dataSource);
        }
 

DataSourceUtils
java代码: 

protected static void doCloseConnectionIfNecessary(Connection con, DataSource dataSource) throws SQLException {
                if (con == null) {
                        return;
                }

                ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
                if (conHolder != null && con == conHolder.getConnection()) {
                        // It's the transactional Connection: Don't close it.
                        conHolder.released();
                        return;
                }
               
                // Leave the Connection open only if the DataSource is our
                // special data source, and it wants the Connection left open.
                if (!(dataSource instanceof SmartDataSource) || ((SmartDataSource) dataSource).shouldClose(con)) {
                        logger.debug("Closing JDBC connection");
                        con.close();
                }
        }
 

恍然大悟。如果事物完成,那么就
TransactionSynchronizationManager.unbindResource(this.dataSource);将当前的ConnectionHolder
从TransactionSynchronizationManager上脱离,然后doCloseConnectionIfNecessary。最后会把连接关闭掉。

5 两个辅助类JdbcTemplate和TransactionAwareDataSourceProxy
JdbcTemplate中的execute方法的第一句和最后一句
java代码: 

public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action)
                        throws DataAccessException {

                Connection con = DataSourceUtils.getConnection(getDataSource());
                //其他代码
        DataSourceUtils.closeConnectionIfNecessary(con, getDataSource());
                }
        }
 

作用不言自明了吧

从TransactionAwareDataSourceProxy中获取的连接是这个样子的
java代码: 

public Connection getConnection() throws SQLException {
                Connection con = DataSourceUtils.doGetConnection(getTargetDataSource(), true);
                return getTransactionAwareConnectionProxy(con, getTargetDataSource());
        }
 

万变不离其宗,不过我们还是看看getTransactionAwareConnectionProxy
java代码: 

protected Connection getTransactionAwareConnectionProxy(Connection target, DataSource dataSource) {
                return (Connection) Proxy.newProxyInstance(
                                ConnectionProxy.class.getClassLoader(),
                                new Class[] {ConnectionProxy.class},
                                new TransactionAwareInvocationHandler(target, dataSource));
        }
 

原来返回的是jdk的动态代理。继续看TransactionAwareInvocationHandler
java代码: 

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //...                        if (method.getName().equals(CONNECTION_CLOSE_METHOD_NAME)) {
                                if (this.dataSource != null) {
                                        DataSourceUtils.doCloseConnectionIfNecessary(this.target, this.dataSource);
                                }
                                return null;
                        }

                                        }
 

TransactionAwareDataSourceProxy会先从DataSourceUtils获取连接。然后将这个连接用jdk的动态代理包一下返回。外部代码如果调用的这个冒牌的Connection,就会先调用TransactionAwareInvocationHandler的invoke,在这个invoke 中,完成原来调用DataSourceUtils的功能。

总结上面的流程
Spring 对DataSource进行事务管理的关键在于ConnectionHolder和TransactionSynchronizationManager。
0.先从TransactionSynchronizationManager中尝试获取连接
1.如果前一步失败则在每个线程上,对每个DataSouce只创建一个Connection
2.这个Connection用ConnectionHolder包装起来,由TransactionSynchronizationManager管理
3.再次请求同一个连接的时候,从TransactionSynchronizationManager返回已经创建的ConnectionHolder,然后调用ConnectionHolder的request将引用计数+1
4.释放连接时要调用ConnectionHolder的released,将引用计数-1
5.当事物完成后,将ConnectionHolder从TransactionSynchronizationManager中解除。当谁都不用,这个连接被close

以上所有都是可以调用DataSourceUtils化简代码,而JdbcTemplate又是调用DataSourceUtils的。所以在Spring文档中要求尽量首先使用JdbcTemplate,其次是用DataSourceUtils来获取和释放连接。至于TransactionAwareDataSourceProxy,那是下策的下策。不过可以将Spring事务管理和遗留代码无缝集成。

所以如某位朋友说要使用Spring的事务管理,但是又不想用JdbcTemplate,那么可以考虑TransactionAwareDataSourceProxy。这个类是原来DataSource的代理。
其次,想使用Spring事物,又不想对Spring进行依赖是不可能的。与其试图自己模拟DataSourceUtils,不如直接使用现成的。

相关文章推荐

Spring事务管理与数据库连接

1 如何获得连接  看DataSourceUtils代码  Java代码   protected static Connection doGetConnection(Data...

Java数据库连接——JDBC调用存储过程,事务管理和高级应用

阅读目录 一、JDBC常用的API深入详解及存储过程的调用 1、存储过程(Stored Procedure)的介绍 2、JDBC调用无参存储过程 3、JDBC调用含输入参数存储过程 ...
  • wdk1011
  • wdk1011
  • 2017年07月30日 22:13
  • 183

我项目使用的数据库连接管理、事务管理类(欢迎拍砖)

我在项目中使用的数据库连接、以及事务操作的类,类中采用开源的apache Dbutils作为辅助,Bonecp做为数据源,使用时请加上Dbutils包,Bonncp包,还有Log4j包。 DbUti...

Spring学习历程---Hibernate访问数据库可脱离事务管理器

JdbcTemplate执行的语句马上提交,没有事务,所以没有必要在Spring应用中配置事务管理器,因为即使配置了,也没有用。 对于Hibernate而言,情况便有不同,因为hibernate并不会...

Spring3数据库事务管理机制

Spring对事务的解决办法其实分为2种:编程式实现事务,AOP配置声明式解决方案。  http://jinnianshilongnian.iteye.com/blog/1496953  Sp...

Spring整合Hibernate开发 - 使用事务管理器操作数据库 - 主配置文件

Spring学习(4):Spring数据库事务管理

前言事务的提出是为了保证一组操作的原子性,让这组操作要么全部成功,要么全部不成功,不成功的话,所有操作回滚到事务开始之前的状态。这在很多地方都讲的很清楚了。mysql本身提供了事务,jdbc也提供了事...

Spring事务管理与数据库隔离级别的关系(Spring+mysql)

之前写过一篇文章《数据库隔离级别(mysql+Spring)与性能分析 》,里面很多问题写的不是很专业,也不是很有逻辑性,现在重新整理一下,希望对大家有帮助。 这部分通过两天时间反复的做实验...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Spring事务管理与数据库连接
举报原因:
原因补充:

(最多只允许输入30个字)