經過源碼分析SqlSession功能實現、如何建立以及在Spring中是如何集成的。html
MyBatis工做的主要Java接口,經過這些接口你能夠執行命令,獲取mapper和管理事務
--代碼註釋git
查看大圖github
在圖中能夠看到,咱們操做數據庫的方法都在裏面。spring
從類圖能夠看到SqlSession 有 DefaultSqlSession、SqlSessionManager2個實現類sql
/** * * The default implementation for {@link SqlSession}. * Note that this class is not Thread-Safe. * SqlSession 默認實現,非線程安全 * * @author Clinton Begin */ public class DefaultSqlSession implements SqlSession { public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) { this.configuration = configuration; this.executor = executor; this.dirty = false; this.autoCommit = autoCommit; } /** * 返回單個查詢結果 * @param statement 惟一標識匹配的語句. * @param parameter 查詢參數. * @param <T> * @return */ @Override public <T> T selectOne(String statement, Object parameter) { // Popular vote was to return null on 0 results and throw exception on too many. List<T> list = this.<T>selectList(statement, parameter); if (list.size() == 1) { return list.get(0); } else if (list.size() > 1) { //期待返回一條記錄,但返回了多條 throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); } else { return null; } } /** * 返回集合結果 * @param statement 惟一標識匹配的語句 * @param parameter 查詢參數 * @param rowBounds 返回結果的大小控制 * @param <E> * @return */ @Override public <E> List<E> selectList(String statement, Object parameter, 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(); } } /** * 返回Map對象 * @param statement 惟一標識匹配的語句. * @param parameter 查詢參數 * @param mapKey key值,字段的屬性別名 * @param rowBounds 返回結果的大小控制 * @param <K> * @param <V> * @return */ @Override public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { final List<? extends V> list = selectList(statement, parameter, rowBounds); final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<K, V>(mapKey, configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory()); final DefaultResultContext<V> context = new DefaultResultContext<V>(); for (V o : list) { context.nextResultObject(o); mapResultHandler.handleResult(context); } return mapResultHandler.getMappedResults(); } /** * 遊標查詢 * @param <T> * @return */ @Override public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); Cursor<T> cursor = executor.queryCursor(ms, wrapCollection(parameter), rowBounds); registerCursor(cursor); return cursor; } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } /** * @param statement 惟一標識匹配的語句 * @param parameter 查詢參數 * @param rowBounds 返回結果的大小控制 * @param handler 外部結果處理器 */ @Override public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { MappedStatement ms = configuration.getMappedStatement(statement); executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } /** * 增長 * @return */ @Override public int insert(String statement, Object parameter) { return update(statement, parameter); } /** * 修改 * @return */ @Override public int update(String statement) { return update(statement, null); } /** * 增刪改公用方法 * @param statement 惟一標識匹配的執行語句 * @param parameter 參數 * @return 返回影響的行數 */ @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(); } } /** * 刪除 * @return */ @Override public int delete(String statement) { return update(statement, null); } /** * 提交 * @param force forces connection commit */ @Override public void commit(boolean force) { try { executor.commit(isCommitOrRollbackRequired(force)); dirty = false; } catch (Exception e) { throw ExceptionFactory.wrapException("Error committing transaction. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } /** * 回滾 * @param force forces connection rollback */ @Override public void rollback(boolean force) { try { executor.rollback(isCommitOrRollbackRequired(force)); dirty = false; } catch (Exception e) { throw ExceptionFactory.wrapException("Error rolling back transaction. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } /** * 提交批處理執行 * @return 批處理提交更新記錄 */ @Override public List<BatchResult> flushStatements() { try { return executor.flushStatements(); } catch (Exception e) { throw ExceptionFactory.wrapException("Error flushing statements. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } /** * 關閉 */ @Override public void close() { try { executor.close(isCommitOrRollbackRequired(false)); closeCursors(); dirty = false; } finally { ErrorContext.instance().reset(); } } /** * 獲取Mapper * @param type Mapper對應的Class類型 * @param <T> * @return */ @Override public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); } // 省略其餘代碼 }
先看類圖:數據庫
查看大圖apache
從圖中能夠看出 SqlSessionManager實現了SqlSessionFactory接口,又封裝了DefaultSqlSessionFactory
代碼以下:編程
public class SqlSessionManager implements SqlSessionFactory, SqlSession { //省略其餘代碼 public static SqlSessionManager newInstance(SqlSessionFactory sqlSessionFactory) { return new SqlSessionManager(sqlSessionFactory); } }
SqlSessionManager與DefaultSqlSessionFactory區別主要有2個:安全
public void startManagedSession() { this.localSqlSession.set(openSession()); } @Override public SqlSession openSession() { return sqlSessionFactory.openSession(); }
在DefaultSqlSessionFactory中每次openSession都會產生一個新的DefaultSqlSessionsession
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { try { //新建DefaultSqlSession return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { } finally { } }
更詳細的源碼參考下節中:DefaultSqlSessionFactory 源碼分析
SqlSessionManager 內部提供了一個sqlSessionProxy,這個sqlSessionProxy提供了全部SqlSession接口的實現,而實現中正是使用了上面提到的本地線程保存的Sqlsession實例。
這樣,在同一個線程實現不一樣的sql操做,能夠複用本地線程Sqlsession,避免了DefaultSqlSessionFactory實現的每個sql操做都要建立新的Sqlsession實例。
讓咱們具體來看下sqlSessionProxy 的實現:
public class SqlSessionManager implements SqlSessionFactory, SqlSession { private SqlSessionManager(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; //建立SqlSession代理對象 this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance( SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionInterceptor()); } @Override public <T> T selectOne(String statement, Object parameter) { //使用代理對象執行數據庫操做 return sqlSessionProxy.<T> selectOne(statement, parameter); } 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) { //不爲null try { return method.invoke(sqlSession, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } else { //爲null 則打開新鏈接 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(); } } } } //省略其餘代碼 }
要了解SqlSession具體如何建立,咱們就須要知道SqlSessionFactory,也就是SqlSession工廠。
從類圖能夠看出 SqlSessionFactory 爲具體SqlSession工廠定義
DefaultSqlSessionFactory 實現了SqlSessionFactory,SqlSession是由DefaultSqlSessionFactory生成
/** * 經過外部傳入的connection 或 database 建立(打開) SqlSession * 方法重載,經過參數不一樣建立SqlSession */ 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(); }
public class DefaultSqlSessionFactory implements SqlSessionFactory { private final Configuration configuration; public DefaultSqlSessionFactory(Configuration configuration) { #1 this.configuration = configuration; } //省略... /** * #mark 建立SqlSession * @param execType 執行器類型 * @param level 事務隔離級別 * @param autoCommit 是否自動提交 * @return */ private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { #2 Transaction tx = null; try { //傳入的configuration獲取環境變量對象、Environment能夠配置多個環境配置 final Environment environment = configuration.getEnvironment(); //從環境對象中獲取事務工廠對象 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); //根據DataSource、事務隔離級別、自動提交建立事務對象 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //#mark 新建執行者 20170820 final Executor executor = configuration.newExecutor(tx, execType); //#mark 建立默認SqlSession 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(); } } //省略... private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) { if (environment == null || environment.getTransactionFactory() == null) { return new ManagedTransactionFactory(); } return environment.getTransactionFactory(); } private void closeTransaction(Transaction tx) if (tx != null) { try { tx.close(); } catch (SQLException ignore) { // Intentionally ignore. Prefer previous error. } } } }
詳細說明:
標註#1 經過構造方法傳入Configuration 配置對象,Configuration是一個貫穿全劇的對象
標註#2 openSessionFromDataSource 顧名思義,從DataSource打開SqlSession,調用new DefaultSqlSession(configuration, executor, autoCommit) 構建SqlSession,具體實現查看源碼備註
Executor 、ErrorContext 後續詳細介紹
從前面的描述中,咱們知道SqlSession由DefaultSqlSessionFactory 產生。經過IDEA關聯搜索功能,咱們找到了具體的調用類爲:SqlSessionFactoryBuilder。 SqlSessionFactoryBuilder 主要是獲取配置輸入流,建立DefaultSqlSessionFactory實例
先看下類圖:
SqlSessionFactoryBuilder 源碼分析
/** * Builds {@link SqlSession} instances. * SqlSession 工廠構造器 * * @author Clinton Begin */ public class SqlSessionFactoryBuilder { //省略 /** * 經過字符流構建 * @param reader 字符流 * @param environment 環境變量 * @param properties 屬性配置 * @return */ public SqlSessionFactory build(Reader reader, String environment, Properties properties) { #1 try { //從字符流中建立XML配置對象 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } //省略 /** * 經過字節流構建 * @param inputStream * @param environment * @param properties * @return */ public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } /** * #mark SqlSessionFactory 初始化 * @param config * @return */ public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); } }
依據測試規範,咱們找到測試類 SqlSessionTest,這個類方法比較多,我精簡出須要的部分。
/** * #mark 源碼學習入口 */ public class SqlSessionTest extends BaseDataTest { private static SqlSessionFactory sqlMapper; @BeforeClass public static void setup() throws Exception { //初始化數據源,使用內存數據庫、運行一次自動銷燬 createBlogDataSource(); //資源文件地址 final String resource = "org/apache/ibatis/builder/MapperConfig.xml"; //獲取資源文件字符流 final Reader reader = Resources.getResourceAsReader(resource); //構建 SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader); } /** * 測試SqlSession 開啓和關閉 * @throws Exception */ @Test public void shouldOpenAndClose() throws Exception { SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE); session.close(); } /** * 測試提交一個未使用的SqlSession * @throws Exception */ @Test public void shouldCommitAnUnUsedSqlSession() throws Exception { SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE); session.commit(true); session.close(); } /** * 測試提交一個未使用的SqlSession * @throws Exception */ @Test public void shouldRollbackAnUnUsedSqlSession() throws Exception { SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE); session.rollback(true); session.close(); } /** * 跟蹤一個完整查詢 * 查出全部做者 #20170831 * @throws Exception */ @Test public void shouldSelectAllAuthors() throws Exception { SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE); try { List<Author> authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors"); assertEquals(2, authors.size()); } finally { session.close(); } } //省略部分代碼
@BeforeClass
public static void setup() throws Exception {}
在這個方法中包含了具體的SqlSession的建立過程
SqlSessionFactoryBean在基本的 MyBatis 中,session 工廠可使用 SqlSessionFactoryBuilder 來建立。而在 MyBatis-Spring 中,則使用 SqlSessionFactoryBean 來替代。
-- 官方文檔
那咱們下載MyBatis-Spring源碼 具體看看
SqlSessionFactoryBean實現了Spring 的3個重要接口:
關鍵代碼
/** * {@inheritDoc} */ @Override public void afterPropertiesSet() throws Exception { //參數檢測 notNull(dataSource, "Property 'dataSource' is required"); notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required"); state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null), "Property 'configuration' and 'configLocation' can not specified with together"); //sqlSessionFactory 實例化 this.sqlSessionFactory = buildSqlSessionFactory(); } /** * Build a {@code SqlSessionFactory} instance. * * The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a * {@code SqlSessionFactory} instance based on an Reader. * Since 1.3.0, it can be specified a {@link Configuration} instance directly(without config file). * * @return SqlSessionFactory * @throws IOException if loading the config file failed */ protected SqlSessionFactory buildSqlSessionFactory() throws IOException { Configuration configuration; //省略 configuration 建立代碼 //返回建立的SqlSessionFactory return this.sqlSessionFactoryBuilder.build(configuration); }
關鍵代碼
/** * {@inheritDoc} */ @Override public SqlSessionFactory getObject() throws Exception { if (this.sqlSessionFactory == null) { afterPropertiesSet(); } //返回建立好的SqlSessionFatory對象 return this.sqlSessionFactory; }
關鍵代碼
/** * {@inheritDoc} */ @Override public void onApplicationEvent(ApplicationEvent event) { if (failFast && event instanceof ContextRefreshedEvent) { // fail-fast -> check all statements are completed //檢測MyBatis全部配置文件語句是否完成 this.sqlSessionFactory.getConfiguration().getMappedStatementNames(); } }
關於MyBatis源碼解讀之SqlSession就介紹到這裏。若有疑問,歡迎留言,謝謝。