`
m635674608
  • 浏览: 5028115 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Spring事务,connection获取,用DataSourceUtils的原理

 
阅读更多
前几天解释了Spring的抽象事务机制。这次讲讲Spring中的DataSource 事务。 
DataSource事务相关的类比较多,我们一步步来拨开其中的密团。 

1 如何获得连接 
看DataSourceUtils代码 

Java代码 
1.protected static Connection doGetConnection(DataSource dataSource, boolean allowSynchronization);   
2.            throws SQLException {   
3.           
4.        ConnectionHolder conHolder = (ConnectionHolder); TransactionSynchronizationManager.getResource(dataSource);;   
5.        if (conHolder != null); {   
6.            conHolder.requested();;   
7.            return conHolder.getConnection();;   
8.        }   
9.  
10.           
11.        Connection con = dataSource.getConnection();;   
12.        if (allowSynchronization && TransactionSynchronizationManager.isSynchronizationActive();); {   
13.                        conHolder = new ConnectionHolder(con);;   
14.            TransactionSynchronizationManager.bindResource(dataSource, conHolder);;   
15.            TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(conHolder, dataSource););;   
16.            conHolder.requested();;   
17.        }   
18.  
19.        return con;   
20.    }  
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代码 
1.private static ThreadLocal resources = new ThreadLocal();;   
2.public static Object getResource(Object key); {   
3.        Map map = (Map); resources.get();;   
4.        if (map == null); {   
5.            return null;   
6.        }   
7.        Object value = map.get(key);;   
8.                return value;   
9.    }   
10.public static void bindResource(Object key, Object value); throws IllegalStateException {   
11.        Map map = (Map); resources.get();;   
12.                if (map == null); {   
13.            map = new HashMap();;   
14.            resources.set(map);;   
15.        }   
16.        map.put(key, value);;   
17.            }  
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代码 
1.public void requested(); {   
2.        this.referenceCount++;   
3.    }   
4.  
5.    public void released(); {   
6.        this.referenceCount--;   
7.    }  
public void requested(); { 
this.referenceCount++; 


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

看得出这是一个引用计数的技巧。原来Spring中对Connection是竟量使用已创建的对象,而不是每次都创建一个新对象。这就是DataSourceUtils中 
Java代码 
1.if (conHolder != null); {   
2.            conHolder.requested();;   
3.            return conHolder.getConnection();;   
4.        }  
if (conHolder != null); { 
conHolder.requested();; 
return conHolder.getConnection();; 
}的原因 


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

Java代码 
1.protected void doCleanupAfterCompletion(Object transaction); {   
2.        DataSourceTransactionObject txObject = (DataSourceTransactionObject); transaction;   
3.  
4.        // Remove the connection holder from the thread.   
5.        TransactionSynchronizationManager.unbindResource(this.dataSource);;   
6.        txObject.getConnectionHolder();.clear();;   
7.  
8.        //...       DataSourceUtils.closeConnectionIfNecessary(con, this.dataSource);;   
9.    }  
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代码 
1.protected static void doCloseConnectionIfNecessary(Connection con, DataSource dataSource); throws SQLException {   
2.        if (con == null); {   
3.            return;   
4.        }   
5.  
6.        ConnectionHolder conHolder = (ConnectionHolder); TransactionSynchronizationManager.getResource(dataSource);;   
7.        if (conHolder != null && con == conHolder.getConnection();); {   
8.            // It's the transactional Connection: Don't close it.   
9.            conHolder.released();;   
10.            return;   
11.        }   
12.           
13.        // Leave the Connection open only if the DataSource is our   
14.        // special data source, and it wants the Connection left open.   
15.        if (!(dataSource instanceof SmartDataSource); || ((SmartDataSource); dataSource);.shouldClose(con);); {   
16.            logger.debug("Closing JDBC connection");;   
17.            con.close();;   
18.        }   
19.    }  
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代码 
1.public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action);   
2.            throws DataAccessException {   
3.  
4.        Connection con = DataSourceUtils.getConnection(getDataSource(););;   
5.        //其他代码   
6.    DataSourceUtils.closeConnectionIfNecessary(con, getDataSource(););;   
7.        }   
8.    }  
public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action); 
throws DataAccessException { 

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


作用不言自明了吧 

从TransactionAwareDataSourceProxy中获取的连接是这个样子的 

Java代码 
1.public Connection getConnection(); throws SQLException {   
2.        Connection con = DataSourceUtils.doGetConnection(getTargetDataSource();, true);;   
3.        return getTransactionAwareConnectionProxy(con, getTargetDataSource(););;   
4.    }  
public Connection getConnection(); throws SQLException { 
Connection con = DataSourceUtils.doGetConnection(getTargetDataSource();, true);; 
return getTransactionAwareConnectionProxy(con, getTargetDataSource(););; 

万变不离其宗,不过我们还是看看getTransactionAwareConnectionProxy 

Java代码 
1.protected Connection getTransactionAwareConnectionProxy(Connection target, DataSource dataSource); {  
2.        return (Connection); Proxy.newProxyInstance(   
3.                ConnectionProxy.class.getClassLoader();,   
4.                new Class[] {ConnectionProxy.class},   
5.                new TransactionAwareInvocationHandler(target, dataSource););;   
6.    }  
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代码 
1.public Object invoke(Object proxy, Method method, Object[] args); throws Throwable {   
2.        //...           if (method.getName();.equals(CONNECTION_CLOSE_METHOD_NAME);); {   
3.                if (this.dataSource != null); {   
4.                    DataSourceUtils.doCloseConnectionIfNecessary(this.target, this.dataSource);;   
5.                }   
6.                return null;   
7.            }   
8.  
9.                    }  
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,不如直接使用现成的

http://yangleiol.iteye.com/blog/731694

分享到:
评论

相关推荐

    Spring-Reference_zh_CN(Spring中文参考手册)

    6.8.1. 在Spring中使用AspectJ来为domain object进行依赖注入 6.8.1.1. @Configurable object的单元测试 6.8.1.2. 多application context情况下的处理 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来...

    Spring 2.0 开发参考手册

    6.8.1. 在Spring中使用AspectJ来为domain object进行依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ Load-time weaving(LTW) 6.9. ...

    Spring中文帮助文档

    6.8.1. 在Spring中使用AspectJ进行domain object的依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7...

    Spring API

    6.8.1. 在Spring中使用AspectJ进行domain object的依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7...

    spring chm文档

    Spring Framework 开发参考手册 Rod Johnson Juergen Hoeller Alef Arendsen Colin Sampaleanu Rob Harrop Thomas Risberg Darren Davison Dmitriy Kopylenko Mark Pollack ...19.2. 使用Spring JMS ...

    Spring源代码解析3:SpringJDBC[归纳].pdf

    `JdbcTemplate`通过`DataSourceUtils.getConnection(getDataSource())`获取数据库连接。`DataSource`是Java的JDBC数据源,负责管理数据库连接。在获取到连接后,`JdbcTemplate`可能需要对连接进行特殊处理,如使用`...

    Spring5源码之JDBC

    }}在execute方法中,Spring首先从DataSource获取连接(Connection),并确保PreparedStatementCreator不为空,然后通过这个creator创建PreparedStatement对象。如果数据库支持批处理,会调用DataSourceUtils的...

    mysql-jdbc-Spring源代码分析

    1. **获取数据库连接**:首先,通过`DataSourceUtils.getConnection()`方法从数据源获取数据库连接。 2. **处理特殊数据库需求**:某些数据库可能需要特殊的处理方式来获取原生连接,例如Oracle数据库。这部分通过`...

    Spring jdbc中数据库操作对象化模型的实例详解

    使用DataSourceUtils获取的连接可以在Spring的事务管理下工作。 8. **DaoSupport抽象类**: Spring JDBC提供了几个基于模板的抽象类,简化了DAO(数据访问对象)的实现: - **JdbcDaoSupport**:提供了...

    edb:春天的 jfinal

    jfinalDb 的数据层操作,兼容 spring 和 jfinal 的事务1.1 围绕 DataSource 数据源对象和 jfinal 的配置类 com.jfinal.plugin.activerecord.Config和 spring事务相关的 DataSourceUtils.getConnection、...

    详解MyBatis多数据源配置(读写分离)

    - 在业务代码中,通过`@Autowired`自动注入动态数据源,然后通过`DataSourceUtils.getConnection(DataSource)`等方式获取连接,或者在Service层使用`@Transactional(readOnly = true/false)`来指定是否为只读操作,...

    详解Java使用sqlite 数据库如何生成db文件

    import org.springframework.jdbc.datasource.DataSourceUtils; ``` 然后,创建一个基本的 DataSource 对象: ```java protected static BasicDataSource dataSource = null; public static DataSource get...

    ConnMethods.zip_Java编程_Java_

    连接数据库可以通过`DataSourceUtils.getConnection()`: ```java JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.execute("SELECT * FROM myTable"); ``` 8. **JTA(Java ...

    java导出数据库的全部表到excel

    如果你使用的是Spring框架,还可以利用其提供的事务管理功能简化代码。 总之,通过结合Apache POI库和JDBC技术,我们可以轻松地在Java中实现从数据库导出所有表到Excel的功能,为数据处理提供便利。

Global site tag (gtag.js) - Google Analytics