事务同步管理器的使用场景:
- 同步涉及的资源包括:SqlSession & Connection。
- 同步资源核心目的是线程共享,意味着必须跟线程绑定。
- 同步资源伴随着线程生存或者消亡,意味着线程结束之前必须手动清除其绑定的资源。
- 事务同步管理器提供的所有字段、方法均是静态属性,使用任何类型的对象【SqlSession、数据库连接Connection】。
事务同步管理器同时提供了一个接口TransactionSynchronization作为扩展点,通常情况下只需继承抽象适配类TransactionSynchronizationAdapter完成扩展。
public interface TransactionSynchronization{
int STATUS_COMMITTED = 0;
int STATUS_ROLLED_BACK = 1;
int STATUS_UNKNOWN = 2;
default void suspend() {
}
default void resume() {
}
default void flush() {
}
default void beforeCommit(boolean readOnly) {
}
default void beforeCompletion() {
}
default void afterCommit() {
}
default void afterCompletion(int status) {
}
}
如上所示该扩展点主要是在事务提交之前增加了扩展,方便用户控制事务提交前后的行为。
具体实现过程可以参考SqlSessionSynchronization、ConnectionSynchronization。
截止当前事务同步管理器处理的资源主要包括:DataSource获取的数据库连接以及SqlSessionFactory。
public abstract class TransactionSynchronizationManager {
ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources");
ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal<>("Transaction synchronizations");
ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal<>("Actual transaction active");
public static Object getResource(Object key) {// key 可能是DataSource、SqlSessionFactory
// 获取目标对象的key
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
// 优先从本地线程中获取目标对象
Object value = doGetResource(actualKey);
return value;
}
// 将目标对象与本地线程建立绑定关系
public static void bindResource(Object key, Object value) throws IllegalStateException {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Map<Object, Object> map = resources.get();
if (map == null) {
map = new HashMap<>();
resources.set(map);
}
Object oldValue = map.put(actualKey, value);
}
// 解除 目标对象与本地线程的绑定关系
public static Object unbindResource(Object key) throws IllegalStateException {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Object value = doUnbindResource(actualKey);
return value;
}
@Nullable
private static Object doUnbindResource(Object actualKey) {
Map<Object, Object> map = resources.get();
if (map == null) {
return null;
}
Object value = map.remove(actualKey);
if (map.isEmpty()) {
resources.remove();
}
if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
value = null;
}
return value;
}
public static boolean isSynchronizationActive() {
return (synchronizations.get() != null);
}
public static void initSynchronization() throws IllegalStateException {
synchronizations.set(new LinkedHashSet<>());
}
public static void registerSynchronization(TransactionSynchronization synchronization){
Set<TransactionSynchronization> synchs = synchronizations.get();
synchs.add(synchronization);
}
}
事务同步管理器涉及三个核心属性:resources、synchronizations、actualTransactionActive,这些属性都是跟线程存在绑定关系。
- resources:线程绑定的资源。包括数据库连接Connection、SqlSession。
- synchronizations:线程绑定TransactionSynchronization类型的资源。该资源绑定之前首先初始化Synchronization,其次判断是否被激活,最后才会被添加到集合类型属性synchronizations中。
1.数据库连接
不管应用中是否涉及事务,数据库连接的创建都是通过DataSourceUtils工具类完成的。
- 如果没有事务则直接使用新建的自动提交特性的数据库连接,并且不需要与线程建立绑定关系。
- 如果存在事务则提前初始化TransactionSynchronizationManager的属性synchronizations,之后就会将Connection交由ConnectionHolder持有,并且与线程建立绑定关系,最后初始化ConnectionSynchronization实例。
public abstract class DataSourceUtils {
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
return doGetConnection(dataSource);
}
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
conHolder.setConnection(fetchConnection(dataSource));
}
return conHolder.getConnection();
}
// 通过 DataSource 创建新的Connection
Connection con = fetchConnection(dataSource);
if (TransactionSynchronizationManager.isSynchronizationActive()) {
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {// 创建 ConnectionHolder
holderToUse = new ConnectionHolder(con);
}else {
holderToUse.setConnection(con);
}
holderToUse.requested();
// 初始化TransactionSynchronization之ConnectionSynchronization
TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
// 将 Connection 与当前线程建立绑定关系
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
}
return con;
}
}
注意:通过DataSourceUtils获取数据库连接只发生在Mybatis执行目标SQL之前,并且只有此处重置ConnectionSynchronization。
2.SqlSession
通过SqlSessionFactory工厂类得到的SqlSession与线程绑定的流程:
- 首先Synchronization处于激活状态。这个激活动作是被事务触发,所以如果目标方法不存在事务则SqlSession不会与线程绑定。
- 初始化SqlSession的Holder之SqlSessionHolder。
- 将SqlSession与线程建立绑定关系。
- 初始化SqlSessionSynchronization实例,并与线程建立绑定关系。
- 设置SqlSessionHolder之synchronizedWithTransaction属性为true。
public final class SqlSessionUtils {
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator) {
// 尝试由 事务同步管理器 获取SqlSession
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
// 如果 holder 不为空,则尝试从 holder 直接获取SqlSession
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
// 通过 SqlSessionFactory工厂类 创建新的 SqlSession
session = sessionFactory.openSession(executorType);
// 尝试将 新得到的SqlSession 与 事务同步管理器 建立绑定关系
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) {
SqlSession session = null;
// 如果 holder 不为空,并且 SqlSession 是跟事务相关的
if (holder != null && holder.isSynchronizedWithTransaction()) {
holder.requested();
session = holder.getSqlSession();
}
return session;
}
private static void registerSessionHolder(SqlSessionFactory sf, ExecutorType ct,PersistenceExceptionTranslator et, SqlSession session) {
SqlSessionHolder holder;
// 首先同步器必须处于激活状态
if (TransactionSynchronizationManager.isSynchronizationActive()) {
Environment environment = sf.getConfiguration().getEnvironment();
// 必须是事务管理器,好像默认都是 SpringManagedTransactionFactory
if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
holder = new SqlSessionHolder(session, ct, et);
// 真正建立 资源与线程 的绑定关系
TransactionSynchronizationManager.bindResource(sf, holder);
TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sf));
// 设置 SqlSessionHolder 的属性SynchronizedWithTransaction 为true
holder.setSynchronizedWithTransaction(true);
holder.requested();
}
}
}
}
所以同步事务管理器如果对SqlSession发挥作用则必定存在于事务下。
3.ResourceHolder
public abstract class ResourceHolderSupport{
private boolean synchronizedWithTransaction = false;
}
如上所示ResourceHolder存在一个重要属性:synchronizedWithTransaction。
3.1.ConnectionHolder
public class ConnectionHolder extends ResourceHolderSupport {
@Nullable
private ConnectionHandle connectionHandle;
@Nullable
private Connection currentConnection;
private boolean transactionActive = false;
}
数据库连接Holder:分别持有Connection以及transactionActive属性。
3.2.SqlSessionHolder
public final class SqlSessionHolder extends ResourceHolderSupport {
private final SqlSession sqlSession;
private final ExecutorType executorType;
private final PersistenceExceptionTranslator exceptionTranslator;
}