最近在看Mybatis源碼,對於理解SqlSessionTemplate是如何保證線程安全的網上的文章不多。希望通過本文能夠幫助大家清楚理解,類關係圖如下:
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