TransactionInterceptor 事务处理
参考:Transactional 源码解析
线程安全核心进入 DataSourceUtils.doGetConnection 方法
mybatis
SqlSessionFactoryBean.class
/**
* {@inheritDoc}
*/
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
...
targetConfiguration.setEnvironment(new Environment(this.environment,
this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
this.dataSource));
...
}
关键部分
使用了 SpringManagedTransactionFactory
Executor.class --> BaseExecutor.class
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}
SpringManagedTransaction
private void openConnection() throws SQLException {
this.connection = DataSourceUtils.getConnection(this.dataSource);
this.autoCommit = this.connection.getAutoCommit();
this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
LOGGER.debug(() -> "JDBC Connection [" + this.connection + "] will"
+ (this.isConnectionTransactional ? " " : " not ") + "be managed by Spring");
}
DataSourceUtils
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(fetchConnection(dataSource));
}
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 = fetchConnection(dataSource);
....
TransactionSynchronizationManager
public abstract class TransactionSynchronizationManager {
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal("Transaction synchronizations");
private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal("Current transaction name");
private static final ThreadLocal<Boolean> currentTransactionReadOnly = new NamedThreadLocal("Current transaction read-only status");
private static final ThreadLocal<Integer> currentTransactionIsolationLevel = new NamedThreadLocal("Current transaction isolation level");
private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal("Actual transaction active");
...
TransactionInterceptor
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}```
## jooq
DefaultExecuteContext
```java
@Override
public final Connection connection() {
// All jOOQ internals are expected to get a connection through this
// single method. It can thus be guaranteed, that every connection is
// wrapped by a ConnectionProxy, transparently, in order to implement
// Settings.getStatementType() correctly.
if (wrappedConnection == null && connectionProvider != null)
connection(connectionProvider, connectionProvider.acquire());
return wrappedConnection;
}
@Bean(name = "mysqlDSLContext")
public DSLContext mysqlDSLContext(DataSource dataSource){
//事务织入代理
TransactionAwareDataSourceProxy proxyTransaction = new TransactionAwareDataSourceProxy(dataSource);
//数据源连接池,织入事务代理类
ConnectionProvider connectionProvider = new DataSourceConnectionProvider(proxyTransaction);
//事务管理器
DataSourceTransactionManager txMgr = new DataSourceTransactionManager(dataSource);
//配置类
return DSL.using(
new DefaultConfiguration()
//数据源连接池提供者
.set(connectionProvider)
//spring事务提供者
.set(new SpringTransactionProvider(txMgr))
//数据库方言
.set(SQLDialect.MYSQL)
);
}
TransactionAwareDataSourceProxy
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Invocation on ConnectionProxy interface coming in...
...
if (this.target == null) {
if (method.getName().equals("getWarnings") || method.getName().equals("clearWarnings")) {
// Avoid creation of target Connection on pre-close cleanup (e.g. Hibernate Session)
return 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);
}
....
}
JdbcTemplate.class
@Override
@Nullable
public <T> T execute(ConnectionCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Connection con = DataSourceUtils.getConnection(obtainDataSource());
try {
// Create close-suppressing Connection proxy, also preparing returned Statements.
Connection conToUse = createConnectionProxy(con);
return action.doInConnection(conToUse);
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
String sql = getSql(action);
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw translateException("ConnectionCallback", sql, ex);
}
finally {
DataSourceUtils.releaseConnection(con, getDataSource());
}
}