这篇文章主要从ibatis的角度分析,ibatis如何获取spring中管理的数据库连接(这里最主要的是用到了ThreadLocal技术)
应用配置:
classpath:context/dao/SqlMapConfig.xml
程序调用(以insert为例):
public class UserDAOImpl extends SqlMapClientDaoSupport implements UserDAO { public int insertUser(User user) { int id = (Integer) getSqlMapClientTemplate().insert("insertUser", user); return id; } }
spring-orm#sqlMapClientTemplate#insert
public Object insert(final String statementName, final Object parameterObject) throws DataAccessException { return execute(new SqlMapClientCallback
spring-orm#sqlMapClientTemplate#execute
publicT execute(SqlMapClientCallback action) throws DataAccessException { Assert.notNull(action, "Callback object must not be null"); Assert.notNull(this.sqlMapClient, "No SqlMapClient specified"); SqlMapSession session = this.sqlMapClient.openSession(); if (logger.isDebugEnabled()) { logger.debug("Opened SqlMapSession [" + session + "] for iBATIS operation"); } Connection ibatisCon = null; try { Connection springCon = null; DataSource dataSource = getDataSource(); boolean transactionAware = (dataSource instanceof TransactionAwareDataSourceProxy); // Obtain JDBC Connection to operate on... try { ibatisCon = session.getCurrentConnection(); if (ibatisCon == null) { springCon = (transactionAware ? dataSource.getConnection() : DataSourceUtils.doGetConnection(dataSource)); session.setUserConnection(springCon); if (logger.isDebugEnabled()) { logger.debug("Obtained JDBC Connection [" + springCon + "] for iBATIS operation"); } } else { if (logger.isDebugEnabled()) { logger.debug("Reusing JDBC Connection [" + ibatisCon + "] for iBATIS operation"); } } } catch (SQLException ex) { throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex); } // Execute given callback... try { return action.doInSqlMapClient(session); } catch (SQLException ex) { throw getExceptionTranslator().translate("SqlMapClient operation", null, ex); } finally { try { if (springCon != null) { if (transactionAware) { springCon.close(); } else { DataSourceUtils.doReleaseConnection(springCon, dataSource); } } } catch (Throwable ex) { logger.debug("Could not close JDBC Connection", ex); } } // Processing finished - potentially session still to be closed. } finally { // Only close SqlMapSession if we know we've actually opened it // at the present level. if (ibatisCon == null) { session.close(); } } }
关键点:
springCon = (transactionAware ? dataSource.getConnection() : DataSourceUtils.doGetConnection(dataSource));
SqlMapClientFactoryBean#afterPropertiesSet
public void afterPropertiesSet() throws Exception { if (this.lobHandler != null) { // Make given LobHandler available for SqlMapClient configuration. // Do early because because mapping resource might refer to custom types. configTimeLobHandlerHolder.set(this.lobHandler); } try { this.sqlMapClient = buildSqlMapClient(this.configLocations, this.mappingLocations, this.sqlMapClientProperties); // Tell the SqlMapClient to use the given DataSource, if any. if (this.dataSource != null) { TransactionConfig transactionConfig = (TransactionConfig) this.transactionConfigClass.newInstance(); DataSource dataSourceToUse = this.dataSource; if (this.useTransactionAwareDataSource && !(this.dataSource instanceof TransactionAwareDataSourceProxy)) { dataSourceToUse = new TransactionAwareDataSourceProxy(this.dataSource); } transactionConfig.setDataSource(dataSourceToUse); transactionConfig.initialize(this.transactionConfigProperties); applyTransactionConfig(this.sqlMapClient, transactionConfig); } } finally { if (this.lobHandler != null) { // Reset LobHandler holder. configTimeLobHandlerHolder.remove(); } } }
设置DataSource类型属性时,会为其生成代理类。该代理类并不是通过spring aop实现的代理类,只是实现了DataSource接口的静态代理类。
ibatis通过代理的DataSource的getConnection获取数据库连接
TransactionAwareDataSourceProxy是连接ibatis与spring的关键。ibatis获取数据库连接都是通过这个spring-jdbc中的类
TransactionAwareDataSourceProxy#getConnection
@Override public Connection getConnection() throws SQLException { DataSource ds = getTargetDataSource(); Assert.state(ds != null, "'targetDataSource' is required"); return getTransactionAwareConnectionProxy(ds); }
TransactionAwareDataSourceProxy#getTransactionAwareConnectionProxy
protected Connection getTransactionAwareConnectionProxy(DataSource targetDataSource) { return (Connection) Proxy.newProxyInstance( ConnectionProxy.class.getClassLoader(), new Class[] {ConnectionProxy.class}, new TransactionAwareInvocationHandler(targetDataSource)); }
spring对TransactionAwareDataSourceProxy获取的数据库连接也进行了代理,生成代理类的方式为Proxy+InvocationHandler
TransactionAwareInvocationHandler#invoke
if (this.target == null) { if (this.closed) { throw new SQLException("Connection handle already closed"); } if (shouldObtainFixedConnection(this.targetDataSource)) { this.target = DataSourceUtils.doGetConnection(this.targetDataSource); } } Connection actualTarget = this.target; if (actualTarget == null) { actualTarget = DataSourceUtils.doGetConnection(this.targetDataSource); }
这里横切面主要为我们做的主要是获取同步的连接(线程内的)
DataSourceUtils#doGetConnection
public static Connection doGetConnection(DataSource dataSource) 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("Fetching resumed JDBC Connection from DataSource"); conHolder.setConnection(dataSource.getConnection()); } return conHolder.getConnection(); } // Else we either got no holder or an empty thread-bound holder here. logger.debug("Fetching JDBC Connection from DataSource"); Connection con = dataSource.getConnection(); if (TransactionSynchronizationManager.isSynchronizationActive()) { logger.debug("Registering transaction synchronization for JDBC Connection"); // Use same Connection for further JDBC actions within the transaction. // Thread-bound object will get removed by synchronization at transaction completion. ConnectionHolder holderToUse = conHolder; if (holderToUse == null) { holderToUse = new ConnectionHolder(con); } else { holderToUse.setConnection(con); } holderToUse.requested(); TransactionSynchronizationManager.registerSynchronization( new ConnectionSynchronization(holderToUse, dataSource)); holderToUse.setSynchronizedWithTransaction(true); if (holderToUse != conHolder) { TransactionSynchronizationManager.bindResource(dataSource, holderToUse); } } return con; }
上面分析这些,ibatis本质上还是让TransactionSynchronizationManager获取数据库连接。至于中间的过程的实现,见仁见智
TransactionSynchronizationManager类中利用threadLocal技术保证了一个线程获取的数据库连接是一样的