承接Mybatis源碼解析-MapperRegistry代理mapper接口,本文將在前文基礎上講解持久層會話的生成
SqlSessionFactory生成
在spring中,SqlSessionFactory的生成是通過SqlSessionFactoryBean生成的,如下
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
****
****
return this.sqlSessionFactoryBuilder.build(configuration);
}
創建的SqlSessionFactory對象類型為org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
上述創建的SqlSessionFactory接口最終會注入至前文所提及的MapperFactoryBean對象中,這里就不羅列SqlSessionFactory的接口方法列表了。主要功能就是獲取建立Sql會話
MapperFactoryBean父類SqlSessionDaoSupport
在其父類有個setSqlSessionFactory()方法
private SqlSession sqlSession;
private boolean externalSqlSession;
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (!this.externalSqlSession) {
//包裝成SqlSessionTemplate對象
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
}
mybatis最終會將SqlSession工廠包裝成SqlSessionTemplate適配器。
SqlSessionTemplate構造函數
基本都是由SqlSessionFactory
作為入參
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
//默認的ExecutorType為ExecutorType.SIMPLE
this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}
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;
//對數據庫的操作也會包裝成代理的形式,所有的CRUD操作則都由SqlSessionInterceptor對象來完成
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
}
我們迫在眉睫的需要了解sqlSession代理的處理流程
SqlSessionTemplate#SqlSessionInterceptor
其是一個內部私有類
private class SqlSessionInterceptor implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//通過SqlSessionUtils.getSqlSession()獲得真實處理CRUD的持久層,默認為DefaultSqlSession
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
//執行CRUD操作
Object result = method.invoke(sqlSession, args);
//非事務處理的數據操作需要強制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,再上一個保險
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
創建sqlSession的步驟是通過SqlSessionUtils#getSqlSession()來完成的,我們必須需要仔細分析
SqlSessionUtils#getSqlSession()-獲取真實的持久層操作對象
其實本質是從SqlSessionFactory中創建SqlSession的,這里只是作下緩存SqlSession
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
notNull(sessionFactory, "No SqlSessionFactory specified");
notNull(executorType, "No ExecutorType specified");
//嘗試從ThreadLocal中獲取SqlSessionHolder,類似緩存
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
if (holder != null && holder.isSynchronizedWithTransaction()) {
//不允許再次獲取的SqlSession修改executorType
if (holder.getExecutorType() != executorType) {
throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
}
//對引用同一個SqlSession的計數
holder.requested();
return holder.getSqlSession();
}
//通過SqlSessionFactory創建SqlSession
SqlSession session = sessionFactory.openSession(executorType);
//判斷是否是同步的
if (TransactionSynchronizationManager.isSynchronizationActive()) {
Environment environment = sessionFactory.getConfiguration().getEnvironment();
//TransactionFactory默認的為SpringManagedTransactionFactory
if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
//創建SqlSessionHolder並緩存在ThreadLocal中
holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
TransactionSynchronizationManager.bindResource(sessionFactory, holder);
TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
holder.setSynchronizedWithTransaction(true);
holder.requested();
} else {
if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
} else {
throw new TransientDataAccessResourceException(
"SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
}
}
}
return session;
}
我們知道上述的SqlSessionFactory為DefaultSqlSessionFactory
,下面看下其如何創建SqlSession
DefaultSqlSessionFactory#openSessionFromDataSource
源碼奉上
//默認情況下,參數值為ExecutorType.SIMPLE/null/false
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
//事務接口對象
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
//此處的TransactionFactory為SpringManagedTransactionFactory
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//創建事務對象SpringManagedTransaction
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//根據execType創建池,默認為SimpleExecutor並支持緩存
final Executor executor = configuration.newExecutor(tx, execType);
//返回DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
默認的事務對象則是由
SpringManagedTransactionFactory
來創建的,內含dataSource數據源,且不自動提交默認的Executor線程池則為SimpleExecutor並支持緩存,另外還有BatchExecutor/ReuseExecutor線程池。根據ExecutorType判斷
默認創建的SqlSession對象為
DefaultSqlSession
,內含事務對象、池、Configuration對象
SqlSessionTemplate#CRUD操作
根據前文的分析,SqlSessionTemplate的CRUD操作是由MapperMethod#execute()
方法調用的,其中傳給sqlSession的參數為mappedStatementId
和其中的方法參數集合
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
if (SqlCommandType.INSERT == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
} else if (SqlCommandType.SELECT == command.getType()) {
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
} else {
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
簡單梳理下service層調用CRUD操作的實現步驟
service層調用dao數據層方法-->讀取對應接口dao的MapperFactoryBean#getObject()方法
-->SqlSessionTemplate#getMapper()方法-->得到MapperProxy代理類並調用對應接口方法的MapperMethod
-->以
接口類+method方法名
作為mappedStatementId讀取MappedStatement對象以獲取SqlCommand指令-->根據SqlCommand指令調用SqlSessionTemplate對應的CRUD操作,一般為select()/delete()/update()/insert()方法-->sqlSessionProxy代理調用真實數據層處理類DefaultSqlSession對應的CRUD操作-->選用池來引用MappedStatement對象處理數據庫sql語句
-->返回結果集供MapperMethod處理返回給service層
CREATE/UPDATE/DELETE
簡單從CREATE操作看,先看SqlSessionTempate#insert()方法
public int insert(String statement) {
return this.sqlSessionProxy.insert(statement);
}
進而觀察DefaultSqlSession#insert()方法
public int insert(String statement) {
return insert(statement, null);
}
public int insert(String statement, Object parameter) {
return update(statement, parameter);
}
進行仔細的觀察我們得知,除了select語句,其實調用的都是update方法
public int update(String statement, Object parameter) {
try {
dirty = true;
//根據statement得到相應的MappedStatement對象
MappedStatement ms = configuration.getMappedStatement(statement);
//最終調用Executor池來進行相應的操作
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
而其中的wrapCollection()方法則是將參數分門別類下
//StrictMap就是普通的HashMap,只是在get方法上加了必須存在相應key的判斷
private Object wrapCollection(final Object object) {
if (object instanceof List) {
StrictMap<Object> map = new StrictMap<Object>();
map.put("list", object);
return map;
} else if (object != null && object.getClass().isArray()) {
StrictMap<Object> map = new StrictMap<Object>();
map.put("array", object);
return map;
}
return object;
}
SELECT
直接轉進DefaultSqlSession#select()通用方法
public void select(String statement, Object parameter, ResultHandler handler) {
select(statement, parameter, RowBounds.DEFAULT, handler);
}
public void select(String statement, ResultHandler handler) {
select(statement, null, RowBounds.DEFAULT, handler);
}
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
//調用線程池的query方法來獲取,獲取到的值存放到ResultHandler內部屬性中
executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
小結
SqlSessionTemplate接受dao層的CRUD請求,通過代理調用DefaultSqlSession的CRUD操作
DefaultSqlSession的內部操作都是通過
org.apache.ibatis.executor.Executor
接口處理然后返回結果的