Spring 代理的事务实现要点
Spring事务的实现要点
事务的开始
org.springframework.transaction.support.TransactionTemplate#execute
|
|
org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction
|
|
事务的初始化数据库连接
org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin
org.springframework.transaction.support.TransactionSynchronizationManager#bindResource
实际SQL的DAO处数据库连接获取
在事务的业务逻辑中,DAO的操作中最终会走到:Executor中,此时一般默认实现是:(mybatis)
org.apache.ibatis.executor.SimpleExecutor
|
|
在org.apache.ibatis.executor.SimpleExecutor#prepareStatement方法中会去拿当前的数据库连接
org.apache.ibatis.executor.BaseExecutor#getConnection
使用Spring框架管理的情况下默认transaction的实现是:
org.mybatis.spring.transaction.SpringManagedTransaction,其getConn的方法如下:
|
|
最终导向了
- org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection:1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768/**...*/public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {try {return doGetConnection(dataSource);}catch (SQLException ex) {throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);}}/*** Actually obtain a JDBC Connection from the given DataSource.* Same as {@link #getConnection}, but throwing the original SQLException.* <p>Is aware of a corresponding Connection bound to the current thread, for example* when using {@link DataSourceTransactionManager}. Will bind a Connection to the thread* if transaction synchronization is active (e.g. if in a JTA transaction).* <p>Directly accessed by {@link TransactionAwareDataSourceProxy}.* @param dataSource the DataSource to obtain Connections from* @return a JDBC Connection from the given DataSource* @throws SQLException if thrown by JDBC methods* @see #doReleaseConnection*/public static Connection doGetConnection(DataSource dataSource) throws SQLException {Assert.notNull(dataSource, "No DataSource specified");//注意此时会首先去TransactionSynchronizationManager中拿当前线程中是否已经有针对这个datasource的事务信息,如果有//则会返回其中的conn,而这个conn是在org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin中创建初始化的//所以,针对org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource实现的业务动态数据源,在事务处理时需要在事务模板的execute方法前进行//determineCurrentLookupKey,而不是在dao方法之前进行,因为此时如果dao在事务之中,在executor中执行sql时候拿到的数据源仍然是//org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin中获取的DB链接,而这两个的时刻的db路由key可能不一致导致错误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;}