mybatis原始碼閱讀(一):SqlSession和SqlSessionFactory
轉載自 mybatis原始碼閱讀(一):SqlSession和SqlSessionFactory
一、介面定義
聽名字就知道這裡使用了工廠方法模式,SqlSessionFactory負責建立SqlSession物件。其中開發人員最常用的就是DefaultSqlSession
(1)SqlSession介面定義
public interface SqlSession extends Closeable { // 泛型方法,引數表示使用的查詢SQL語句,返回值為查詢的結果物件 <T> T selectOne(String statement); // 第二個引數表示需要使用者傳入的實參,也就是SQL語句繫結的實參 <T> T selectOne(String statement,Object parameter); // 查詢結果集有多條記錄,會封裝成結果物件列表返回 <E> List<E> selectList(String statement); <E> List<E> selectList(String statement,Object parameter); // 第三個引數用於限制解析結果集的範圍 <E> List<E> selectList(String statement,Object parameter,RowBounds rowBounds); /** * * selectMap 方法的原理和引數都與selectList方法型別,但結果集會被對映成Map物件返回 * 其中mapKey引數指定了結果集哪一列作為map的key,其他引數同上 */ <K,V> Map<K,V> selectMap(String statement,String mapKey); <K,String mapKey,RowBounds rowBounds); // 返回值是遊標物件,引數同上 <T> Cursor<T> selectCursor(String statement); <T> Cursor<T> selectCursor(String statement,Object parameter); <T> Cursor<T> selectCursor(String statement,RowBounds rowBounds); // 查詢的結果物件將由此處指定的handler物件處理,其餘引數同上 void select(String statement,ResultHandler handler); void select(String statement,RowBounds rowBounds,ResultHandler handler); // 執行insert語句 int insert(String statement); int insert(String statement,Object parameter); // 執行update語句 int update(String statement); int update(String statement,Object parameter); // 執行delete int delete(String statement); int delete(String statement,Object parameter); // 提交事務 void commit(); void commit(boolean force); // 事務回滾 void rollback(); void rollback(boolean force); // 將請求重新整理到資料庫 List<BatchResult> flushStatements(); // 關閉當前session @Override void close(); // 清空session 快取 void clearCache(); // 獲取Configuration 物件 Configuration getConfiguration(); // 獲取type 物件的Mapper物件 <T> T getMapper(Class<T> type); // 獲取該Sqlsession 物件的資料庫連線 Connection getConnection(); }
SqlSession資料庫的C、R、U、D及事務處理介面,你懂的。
(2)SqlSessionFactory
public interface SqlSessionFactory { SqlSession openSession(); SqlSession openSession(boolean autoCommit); SqlSession openSession(Connection connection); SqlSession openSession(TransactionIsolationLevel level); SqlSession openSession(ExecutorType execType); SqlSession openSession(ExecutorType execType,boolean autoCommit); SqlSession openSession(ExecutorType execType,TransactionIsolationLevel level); SqlSession openSession(ExecutorType execType,Connection connection); Configuration getConfiguration(); }
這個大家也都懂的
SqlSession實現類:DefaultSqlSession和SqlSessionManager
SqlSessionFactory實現類:DefaultSqlSessionFactory和SqlSessionManager
(3)DefaultSqlSession
@Override public int update(String statement,Object parameter) { try { dirty = true; MappedStatement ms = configuration.getMappedStatement(statement); return executor.update(ms,wrapCollection(parameter)); } catch (Exception e) { throw ExceptionFactory.wrapException("Error updating database. Cause: " + e,e); } finally { ErrorContext.instance().reset(); } } @Override public <E> List<E> selectList(String statement,RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); return executor.query(ms,wrapCollection(parameter),rowBounds,Executor.NO_RESULT_HANDLER); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e,e); } finally { ErrorContext.instance().reset(); } }
這裡主要看這兩個方法,因為delete和insert最終執行掉用的都是update方法,查詢就更不用說了。從程式碼上看都是從configuration物件中獲取MappedStatement 物件 然後把事情交給小弟Executor去執行,這裡用了很典型的策略設計模式,這個關於Executor 後面介紹。
(4)DefaultSqlSessionFactory
/**
* 通過資料來源獲取資料庫連線,並建立Executor以及DefaultSqlSession物件
*/
private SqlSession openSessionFromDataSource(ExecutorType execType,TransactionIsolationLevel level,boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(),level,autoCommit);
final Executor executor = configuration.newExecutor(tx,execType);
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();
}
}
/**
* 使用者提供資料庫連線物件,使用該資料庫連線物件建立Executor和DefaultSqlSession物件
*/
private SqlSession openSessionFromConnection(ExecutorType execType,Connection connection) {
try {
boolean autoCommit;
try {
autoCommit = connection.getAutoCommit();
} catch (SQLException e) {
// Failover to true,as most poor drivers
// or databases won't support transactions
autoCommit = true;
}
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
final Transaction tx = transactionFactory.newTransaction(connection);
final Executor executor = configuration.newExecutor(tx,autoCommit);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e,e);
} finally {
ErrorContext.instance().reset();
}
}
DefaultSqlSessionFactory主要提供了兩種建立DefaultSqlSession物件的方式,一種是通過資料來源獲取資料庫連線,並建立Executor以及DefaultSqlSession物件,另一種是使用者提供資料庫連線物件,使用該資料庫連線物件建立Executor和DefaultSqlSession物件。
(5)SqlSessionManager
SqlSessionManager同時實現SqlSession介面和SqlSessionFactory介面,也就是同時提供了建立SqlSession物件以及SqlSession物件操作資料庫的功能。SqlSessionManager與DefaultSqlSessionFactory的主要不同點是SqlSessionManager提供了兩種模式,一種是和DefaultSqlSessionFactory的行為相同,同一執行緒每次訪問資料庫就都會建立新的DefaultSqlSession,第二種是通過ThreadLocal變數記錄當前執行緒的SqlSession物件,避免同一執行緒多次建立SqlSession物件。至於使用動態代理的目的,是為了通過攔截器InvocationHandler,增強目標target的方法呼叫。
private final SqlSessionFactory sqlSessionFactory;
// SqlSession的代理物件 會使用JDK的動態代理方式實現
private final SqlSession sqlSessionProxy;
/**
* ThreadLocal 變數,記錄一個與當前執行緒繫結的SqlSession物件
* localSqlSession 中記錄的SqlSession物件的代理物件,在SqlSessionManager初始化的時候
*/
private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();
/**
* SqlSessionManager 的私有構造方法
*/
private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
// 使用動態代理生成SqlSession的代理物件
this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(),new Class[]{SqlSession.class},new SqlSessionInterceptor());
}
/**
* 通過newInstance方法建立SqlSessionManager物件
* @param reader
* @return
*/
public static SqlSessionManager newInstance(Reader reader) {
return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader,null,null));
}
// 內部類
private class SqlSessionInterceptor implements InvocationHandler {
public SqlSessionInterceptor() {
// Prevent Synthetic Access
}
@Override
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable {
// 獲取當前執行緒繫結的SqlSession物件
final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
if (sqlSession != null) {// 第二種模式
try {
return method.invoke(sqlSession,args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} else {// 第一種模式 建立新的SqlSession
final SqlSession autoSqlSession = openSession();
try {
final Object result = method.invoke(autoSqlSession,args);
autoSqlSession.commit();
return result;
} catch (Throwable t) {
autoSqlSession.rollback();
throw ExceptionUtil.unwrapThrowable(t);
} finally {
autoSqlSession.close();
}
}
}
}