mybatis的執行流程


1.SqlSessionFactoryBuilder與SqlSessionFactory

  我們一般在使用mybatis是都會通過new SqlSessionFactoryBuilder.build(...)來獲取SqlSessionFactory,那么這條語句發生了什么,我們來看一看源碼

  (1).通過將配置文件傳遞給SqlSessionFactoryBuilder調用build()方法來獲取SessionSessionFactory.

public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null); //我們跳到這個方法去看
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);  //通過這個方法來加載我們mybatis的xml文件,解析成為XMLConfigBuilder對象。 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.
      }
    }
}
public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);    //通過將Configuration對象最為參數來新建一個DefaultSqlSessionFactory.
}

2.SqlSessionFactory與SqlSession

 (1)我們一般通過SqlSqlSessionFactory.oppenSession()來獲取一個SqlSession.我們來看看源碼都發生了什么。

@Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); //我們調到這個方法,會以默認的Executor來執行我們的操作。
  }
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment(); //通過Configuration對象去獲取我們的配置信息。 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);  //新建默認的Executor,默認為Simple return new DefaultSqlSession(configuration, executor, autoCommit);  //將Configuration,我們得到的executor作為參數來新建一個DefaultSqlSession.
    } 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();
    }
  }

  (2)我們也可以通過自定義的ExecutorType來創建我們的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();

}

  (3)SqlSession介紹

   sqlSession作為頂層的接口,為我們提供的數據庫訪問的接口方法。

3.Executor

  (1)在sqlSession中實際上並沒有實際的數據庫操作而是交給下層的Executor來進行,Executor這一層主要負責mybatis中緩存的查詢。

  舉個例子:

  

@Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);    //獲取MapperStatement對象,在mybatis中我們標簽<select/>,<update/>,等這些CRUD標簽都會被解析為一個個的MappedStatement對象。
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); //調用executor的方法來查詢
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

 

  (2)我們轉到executor(BaseExecutor)的方法中,注意在BaseExecutor中存儲的是我們的一級緩存,關於一二級緩存在下一篇中提及,此處只討論執行過程

@Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);  //查找緩存的key return query(ms, parameter, rowBounds, resultHandler, key, boundSql); //轉到這個方法
 }
@Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;  //查詢緩存是否存在 if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);  //若緩存不存在則去查詢數據庫
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);   //將當前的查找key放入到緩存中 try {
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); //執行子類中查詢數據庫的方法
    } finally {
      localCache.removeObject(key);
    }
    localCache.putObject(key, list);        //將結果放入到緩存中 if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

我們轉到其中一個子類中的查詢數據庫方法(SimpleExecutor)

@Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);  //通過configuration來獲取StatementHandler對象
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);  //將查詢數據庫的操作交給StatementHandler去實現
    } finally {
      closeStatement(stmt);
    }
  }

4.StatementHandler

 statementHandler用來執行原始的Jdbc操作

@Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();      //通過preparedStatement來執行原始的數據庫操作 return resultSetHandler.<E> handleResultSets(ps); //返回結果並封裝為ResultHandler對象
  }

這樣mybatis的一次執行就完成了。

5.最后我們在使用SqlSession時一般會使用SqlSession.getMapper來獲取我們的代理類

@Override
  public <T> T getMapper(Class<T> type) {
    return configuration.<T>getMapper(type, this); //通過configuration來獲取Mapper
  }
//configuration類中的方法
public
<T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); //在configuration對象中的mapperRegistry來獲取mapper,在mapperRegistry中保存了我們的接口信息。 }
//mapperRegistry類中的方法
public
<T> T getMapper(Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { return mapperProxyFactory.newInstance(sqlSession); //通過mapperProxyFactory來獲取mapper實例 } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); }
//MapperProxyFactory類中的方法
protected
T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); //通過jdk動態代理來獲取代理對象 } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }

通過以上來獲取我們的Mapper對象。

對於一些細節,可以去查看mybatis的源碼進行學習

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM