最近在看Mybatis源碼,對于了解SqlSessionTemplate是如何保證線程安全的網上的文章不多。希望通過本文能夠幫助大家清楚了解,類關系圖如下:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL4MjM5UDMwYTM2ATMxkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
DefaultSqlSession與SqlSessionManager解析
在Mybatis中SqlSession預設有DefaultSqlSession和SqlSessionManager兩個實作類
DefaultSqlSession是真正的實作類調用Executor,但不是線程安全的。
Mybatis又實作了對SqlSession和SQLSessionFactory的封裝類SqlSessionManager,線程安全并通過localSqlSession實作複用進而提高性能。
private ThreadLocal<SqlSession> localSqlSession = new ThreadLocal();
SqlSessionManager通過SqlSessionInterceptor實作對DefaultSqlSession代理調用。
private class SqlSessionInterceptor implements InvocationHandler {
public SqlSessionInterceptor() {
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//由調用者決定目前線程是否複用 SqlSession
SqlSession sqlSession = (SqlSession)SqlSessionManager.this.localSqlSession.get();
if (sqlSession != null) {
try {
return method.invoke(sqlSession, args);
} catch (Throwable var12) {
throw ExceptionUtil.unwrapThrowable(var12);
}
} else {
//如果不複用,則每次調用都建立 SqlSession 并使用後銷毀
SqlSession autoSqlSession = SqlSessionManager.this.openSession();
Object var7;
try {
Object result = method.invoke(autoSqlSession, args);
autoSqlSession.commit();
var7 = result;
} catch (Throwable var13) {
autoSqlSession.rollback();
throw ExceptionUtil.unwrapThrowable(var13);
} finally {
autoSqlSession.close();
}
return var7;
}
}
}
DefaultSqlSession和SqlSessionManager之間的差別:
1、單例模式下DefaultSqlSession是線程不安全的,而SqlSessionManager是線程安全的;
2、SqlSessionManager可以選擇通過localSqlSession這個ThreadLocal變量,記錄與目前線程綁定的SqlSession對象,供目前線程循環使用,進而避免在同一個線程多次建立SqlSession對象造成的性能損耗;
3、使用DefaultSqlSession為了保證線程安全需要為每一個操作都建立一個SqlSession對象,其性能可想而知;
SqlSessionTemplate是怎麼保證線程安全
SqlSessionTemplate是MyBatis專門為Spring提供的,支援Spring架構的一個SqlSession擷取接口。主要是為了繼承Spring,并同時将是否共用SqlSession的權限交給Spring去管理。
1、通過建立sqlSessionProxy代理類,将調用導向SqlSessionInterceptor的invoke方法。
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
Assert.notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
}
2、擷取線程私有的SqlSession,調用DefaultSqlSession對應的實際方法上
private class SqlSessionInterceptor implements InvocationHandler {
private SqlSessionInterceptor() {
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//擷取同一個線程的複用sqlSession,如果沒有則新生成一個并存到線程私有存儲中
SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
Object unwrapped;
try {
//實際調用DefaultSession的對應方法
Object result = method.invoke(sqlSession, args);
//判斷目前sqlSession是否被Spring管理,如果沒有直接commit
if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
}
unwrapped = result;
} catch (Throwable var11) {
unwrapped = ExceptionUtil.unwrapThrowable(var11);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw (Throwable)unwrapped;
} finally {
if (sqlSession != null) {
//正常傳回将線程私有sqlSession調用次數減一
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
return unwrapped;
}
}
3、檢視getSqlSession()方法就知道每個線程對應的SqlSession都是私有的不會被共用,是以SqlSessionTemplate是線程安全的。
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
Assert.notNull(executorType, "No ExecutorType specified");
//從線程私有存儲中擷取SqlSession
SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Creating a new SqlSession");
}
//沒有則建立一個DefaultSqlSession
session = sessionFactory.openSession(executorType);
//存到線程私有存儲中
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
}
總結
究其根本SqlSession真正的實作類隻有DefaultSqlSession,SqlSessionManager和SqlSessionTemplate都是通過代理轉發到DefaultSqlSession對應方法。
單例模式下的DefaultSqlSession不是線程安全的,SqlSessionManager和SqlSessionTemplate線程安全的根本就是每一個線程對應的SqlSession都是不同的。如果每一個操作都建立一個SqlSession對象,操作完又進行銷毀導緻性能極差。通過線程私有ThreadLocal存儲SqlSession進行複用,進而提高性能。
參考:https://blog.csdn.net/bntx2jsqfehy7/article/details/79441545#commentsedit