前言
在普遍的JAVA-WEB專案的實際業務處理中,最終都是通過SqlSessionTemplate執行資料庫的CURD操作,本文結合mybatis原始碼,對SqlSessionTemplate進行詳細的介紹,
SqlSessionTemplate是個線稱安全的類,如果你的系統是微服務架構的話,那一個微服務(組件)里面的所有DAO可以共享同一個SqlSessionTemplate對應的bean實體,因為SqlSessionTemplate實作了SqlSession介面,他會保證使用的SqlSession是和當前Spring的事務相關的,并且他還會管理session的生命周期,包含關閉、提交和回滾操作,
那他具體是如何實作的呢,下面進行原始碼決議,
一、SqlSessionTemplate的初始化
首先,SqlSessionTemplate在進行初始化的時候,會把SqlSessionFactory作為引數傳入:
<bean id="mySqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
二、創建SqlSessionFactory的代理類
此處,在初始化SqlSessionTemplate實體的時候,創建一個SqlSessionFactory的代理類作為SqlSessionTemplate的一個屬性是關鍵點,
以下均為mysql原始碼
1.創建代理
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
// 此處創建了sqlSession的代理類的實體,當通過代理類進行方法呼叫時
// 該呼叫會被導向SqlSessionInterceptor的invoke方法
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}
2.切面實作
創建代理的目的就是執行切面方法,下面進行切面功能的詳細解讀:
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 獲取SqlSession(這個SqlSession才是真正使用的,它是執行緒獨有的)
// 對于getSqlSession方法的具體實作,在下面有詳細解讀
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
// 呼叫從Spring的事物背景關系中獲取的sqlSession
// 結合具體的引數(args)和sqlSession,進行最終的sql執行,操作資料庫
Object result = method.invoke(sqlSession, args);
// 然后判斷一下當前的sqlSession是否被Spring托管 如果未被Spring托管則自動commit
if (!isSqlSessionTransactional(sqlSession,
SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
// 如果出現例外則根據情況轉換后拋出
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null &&
unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the
// translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.
translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
// 關閉sqlSession的具體實作,是根據當前sqlSession是否在Spring的事物背景關系中來決定具體操作的
// 如果sqlSession被Spring管理,則呼叫holder.released(),使計數器-1
// 否則直接關閉當前的sqlSession
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
getSqlSession方法如下:
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory,
ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
// 根據sqlSessionFactory,從TransactionSynchronizationManager定義的資源map中
// 獲取當前執行緒對應的SqlSessionHolder
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.
getResource(sessionFactory);
// 從SqlSessionHolder中提取SqlSession物件
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Creating a new SqlSession");
}
// 如果無法通過SqlSessionHolder獲取到sqlSession,那就利用sessionFactory新創建一個sqlSession
session = sessionFactory.openSession(executorType);
// 新創建一個SqlSessionHolder,并且將剛創建的sqlSession與其系結
// 然后將SqlSessionHolder物件注冊到TransactionSynchronizationManager中
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
總結
綜上所述,通過對SqlSessionTemplate的原始碼解讀,我們應該可以理解為什么可以在一個微服務(組件)里面的所有DAO可以共享同一個SqlSessionTemplate對應的bean實體,并保證執行緒安全和事務的完整性,因為:
1、多個執行緒都通過呼叫同一個SqlSessionTemplate進行資料庫操作時,SqlSessionTemplate通過代理機制,每一次都執行切面里的getSqlSession方法來獲取真正操作資料庫的sqlSession,
2、在getSqlSession方法中,使用到了重要的TransactionSynchronizationManager類,此類利用ThreadLocal方式,將當前執行緒與一個事物相系結,從而在當前執行緒想要獲取sqlSession時,判斷當前執行緒是否存在還未完成的事物,如果存在則利用與當前事物系結的sqlSession執行資料庫操作,如果不存在則新建sqlSession進行相應操作,從而保證了執行緒的安全和資料庫事物的完整性,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/247639.html
標籤:其他
上一篇:論文筆記《Improving Docker Registry Design based on Production Workload Analysis》
