浏览 24374 次
锁定老帖子 主题:Spring事务管理与数据库连接
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2005-03-25
DataSource事务相关的类比较多,我们一步步来拨开其中的密团。 1 如何获得连接 看DataSourceUtils代码 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 看其中的一些代码 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,我们感兴趣的是这两个方法 public void requested(); { this.referenceCount++; } public void released(); { this.referenceCount--; } 看得出这是一个引用计数的技巧。原来Spring中对Connection是竟量使用已创建的对象,而不是每次都创建一个新对象。这就是DataSourceUtils中 if (conHolder != null); { conHolder.requested();; return conHolder.getConnection();; }的原因 4 释放连接 完成事物后DataSourceTransactionManager有这样的代码 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 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方法的第一句和最后一句 public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action); throws DataAccessException { Connection con = DataSourceUtils.getConnection(getDataSource(););; //其他代码 DataSourceUtils.closeConnectionIfNecessary(con, getDataSource(););; } } 作用不言自明了吧 从TransactionAwareDataSourceProxy中获取的连接是这个样子的 public Connection getConnection(); throws SQLException { Connection con = DataSourceUtils.doGetConnection(getTargetDataSource();, true);; return getTransactionAwareConnectionProxy(con, getTargetDataSource(););; } 万变不离其宗,不过我们还是看看getTransactionAwareConnectionProxy protected Connection getTransactionAwareConnectionProxy(Connection target, DataSource dataSource); { return (Connection); Proxy.newProxyInstance( ConnectionProxy.class.getClassLoader();, new Class[] {ConnectionProxy.class}, new TransactionAwareInvocationHandler(target, dataSource););; } 原来返回的是jdk的动态代理。继续看TransactionAwareInvocationHandler 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,不如直接使用现成的。 btw 这几天找工作找的很郁闷,被两家公司各面了一把,又各被拒了一把。特别可气一家看到我有3年的vb6经验就断定我只会vb6。所以这几天灌水灌的比较少。 继续投简历ing 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2005-03-25
分析的挺好,这几天我也恰好看了这些类
直接调用JDBCTemplate虽然也不错,但是对各种类型的数据库封装,其他处理等等,用JDBCTemplate就不太方便了. 而且不想把自己的类依赖Spring,所以最后还是使用了TransactionAwareDataSourceProxy,不知道有何不好的地方? 如果不用JDBCTemplate,使用自己的JDBC,还有何选择哪? |
|
返回顶楼 | |
发表时间:2005-03-25
scud 写道 分析的挺好,这几天我也恰好看了这些类
直接调用JDBCTemplate虽然也不错,但是对各种类型的数据库封装,其他处理等等,用JDBCTemplate就不太方便了. 而且不想把自己的类依赖Spring,所以最后还是使用了TransactionAwareDataSourceProxy,不知道有何不好的地方? 如果不用JDBCTemplate,使用自己的JDBC,还有何选择哪? 从代码上看,没什么不好的。TransactionAwareDataSourceProxy本身不是问题,关键那个动态代理用到的TransactionAwareInvocationHandler 这个handler模拟了JDBCTemplate的功能,每次调用那个冒牌的connection时,都会调用TransactionAwareInvocationHandler的invoke 唯一可能有问题的就是需要jdk支持动态代理。如果jdk版本低,这套proxy就不能用了 |
|
返回顶楼 | |
发表时间:2005-03-25
Proxy是在1.3里面出现的
如果jdk低于1.3,我看spring,webwork等等,好像都玩不了了 |
|
返回顶楼 | |
发表时间:2005-03-25
在without ejb中rod指出了事务框架的清晰结构:
Separating these concerns leads to much cleaner architectures: Business objects demarcate abstract transactions without understanding involved resources, and data access objects fetch transactional resources without worrying about participation in transactions. 在spring的实现中也都是如此做的,bo只负责事务边界的声明(通过aop这个也可以弄出去了),dao只需要去拿需要的事务资源,资源创建和加入事务都由事务框架来做。 所以也就有了TransactionSynchronizationManager这个类,它负责将事务资源绑定到对应线程。包括对connection, session,persistensemanager的管理都是如此。 |
|
返回顶楼 | |