工作中,需要學習一下MyBatis sqlSession的産生過程,翻看了mybatis-spring的源碼,閱讀了一些mybatis的相關doc,對mybatis sqlSession有了一些認知和了解,這裡簡單的總結和整理一下。
首先, 通過翻閱源碼,我們來整理一下mybatis進行持久化操作時重要的幾個類:
- SqlSessionFactoryBuilder:build方法建立SqlSessionFactory執行個體。
- SqlSessionFactory:建立SqlSession執行個體的工廠。
- SqlSession:用于執行持久化操作的對象,類似于jdbc中的Connection。
- SqlSessionTemplate:MyBatis提供的持久層通路模闆化的工具,線程安全,可通過構造參數或依賴注入SqlSessionFactory執行個體。
Hibernate是與MyBatis類似的orm架構,這裡與Hibernate進行一下對比,Hibernate中對于connection的管理,是通過以下幾個重要的類:
- SessionFactory:建立Session執行個體的工廠,類似于MyBatis中的SqlSessionFactory。
- Session:用來執行持久化操作的對象,類似于jdbc中的Connection。
- HibernateTemplate:Hibernate提供的持久層通路模闆化的工具,線程安全,可通過構造參數或依賴注入SessionFactory執行個體。
在日常的開發中,我們經常需要這樣對MyBatis和Spring進行內建,把sqlSessionFactory交給Spring管理,通常情況下,我們這樣配置:
<
bean
id
=
"sqlSessionFactory"
class
=
"org.mybatis.spring.SqlSessionFactoryBean"
>
<
property
name
=
"dataSource"
ref
=
"dataSource"
/>
</
bean
>
其中使用到了org.mybatis.spring.SqlSessionFactoryBean,其 是MyBatis為Spring提供的用于建立SqlSessionFactory的類,将在Spring應用程式的上下文建議一下可共享的 MyBatis SqlSessionFactory執行個體,我們可以通過依賴注入将SqlSessionFactory傳遞給MyBatis的一些接口。
如果通過Spring進行事務的管理,我們需要增加Spring注解的事務管理機制,如下配置:
<
bean
id
=
"transactionManager"
class
=
"org.springframework.jdbc.datasource.DataSourceTransactionManager"
>
<
property
name
=
"dataSource"
ref
=
"dataSource"
/>
</
bean
>
<
tx:annotation-driven
/>
這樣,我們就可以使用Spring @Transactional注解,進行事務的控制,表明所注釋的方法應該在一個事務中運作。 Spring将在事務成功完成後送出事務,在事務發生錯誤時進行異常復原,而且,Spring會将産生的MyBatis異常轉換成适當的 DataAccessExceptions,進而提供具體的異常資訊。
下面,我們通過分析SqlSessionUtils中getSession的源碼,來詳細的了解一下sqlSession的産生過程,源碼如下:
public
static
SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
notNull(sessionFactory,
"No SqlSessionFactory specified"
);
notNull(executorType,
"No ExecutorType specified"
);
SqlSessionHolder holder = (SqlSessionHolder) getResource(sessionFactory);
if
(holder !=
null
&& holder.isSynchronizedWithTransaction()) {
if
(holder.getExecutorType() != executorType) {
throw
new
TransientDataAccessResourceException(
"Cannot change the ExecutorType when there is an existing transaction"
);
}
holder.requested();
if
(logger.isDebugEnabled()) {
logger.debug(
"Fetched SqlSession ["
+ holder.getSqlSession() +
"] from current transaction"
);
}
return
holder.getSqlSession();
}
if
(logger.isDebugEnabled()) {
logger.debug(
"Creating a new SqlSession"
);
}
SqlSession session = sessionFactory.openSession(executorType);
// Register session holder if synchronization is active (i.e. a Spring TX is active)
//
// Note: The DataSource used by the Environment should be synchronized with the
// transaction either through DataSourceTxMgr or another tx synchronization.
// Further assume that if an exception is thrown, whatever started the transaction will
// handle closing / rolling back the Connection associated with the SqlSession.
if
(isSynchronizationActive()) {
Environment environment = sessionFactory.getConfiguration().getEnvironment();
if
(environment.getTransactionFactory()
instanceof
SpringManagedTransactionFactory) {
if
(logger.isDebugEnabled()) {
logger.debug(
"Registering transaction synchronization for SqlSession ["
+ session +
"]"
);
}
holder =
new
SqlSessionHolder(session, executorType, exceptionTranslator);
bindResource(sessionFactory, holder);
registerSynchronization(
new
SqlSessionSynchronization(holder, sessionFactory));
holder.setSynchronizedWithTransaction(
true
);
holder.requested();
}
else
{
if
(getResource(environment.getDataSource()) ==
null
) {
if
(logger.isDebugEnabled()) {
logger.debug(
"SqlSession ["
+ session +
"] was not registered for synchronization because DataSource is not transactional"
);
}
}
else
{
throw
new
TransientDataAccessResourceException(
"SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization"
);
}
}
}
else
{
if
(logger.isDebugEnabled()) {
logger.debug(
"SqlSession ["
+ session +
"] was not registered for synchronization because synchronization is not active"
);
}
}
return
session;
}
- 它會首先擷取SqlSessionHolder,SqlSessionHolder用于在TransactionSynchronizationManager中保持目前的SqlSession。
- 如果holder不為空,并且holder被事務鎖定,則可以通過holder.getSqlSession()方法,從目前事務中擷取sqlSession,即 Fetched SqlSession from current transaction。
- 如果不存在holder或沒有被事務鎖定,則會建立新的sqlSession,即 Creating a new SqlSession,通過sessionFactory.openSession()方法。
- 如果目前線程的事務是活躍的,将會為SqlSession注冊事務同步,即 Registering transaction synchronization for SqlSession。