mybatis(3) - 執行關鍵步驟類源碼

使用配置

由於使用的是SpringBoot方式,因此此處隱藏了對於MapperScan的處理配置。通常不須要分庫分表時,能夠直接使用DruidDataSource做爲數據源
詳情見 【@Mapper與@MapperScan如何正確使用】解析。java

/*事務管理*/
    @Bean
    private DataSourceTransactionManager transactionManager(TDataSource mysqlTddlDataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(mysqlTddlDataSource);
        return transactionManager;
    }

    @Bean
    private SqlSessionFactoryBean dataSqlSessionFactory(TDataSource mysqlTddlDataSource) throws IOException {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(mysqlTddlDataSource);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:mapper/*Mapper.xml"));
        sqlSessionFactoryBean.setPlugins(new Interceptor[] { new SqlStatementInterceptor(ServiceNameConstant.GIANT) }); // 自定義在sql時的攔截切面處理
        return sqlSessionFactoryBean;
    }

    @Bean(name = "mysqlTddlDataSource", initMethod = "init")
    private TDataSource dataSource() {
        TDataSource datasource = new TDataSource();
        datasource.setAppName(appname);
        datasource.setDynamicRule(true);
        return datasource;
    }

結論

  1. Configuration是很重要的類!
  2. 對於每個mapperInterface都由MapperFactoryBean構建,常規是在初始化時注入DefaultSqlSessionFactory,並由此建立SqlSessionTemplate
  3. MapperFactoryBean.getObject ->SqlSessionTemplate.getMapper -> Configuration.getMapper ->mapperRegistry.getMapper->MapperProxyFactory.newInstance 構建對於mapperInterface的代理對象MapperProxy
  4. 執行時: MapperProxy -> MapperMethod -> SqlSessionTemplate -> SqlSessionInterceptor 開啓defaultSqlSession執行

 

源碼解析

Configuration

mybatis在構建DefaultSqlSessionFactory時,會初始化該關鍵對象。 他的活動週期貫穿整個mybatis的執行鏈路!不少處理都須要用到它!mysql

  1. 保存了mybatis啓動的各項配置信息,例如xml的解析對象,datasource信息等等, 最終構建的DefaultSqlSessionFactory須要它做爲構造函數的入參;
  2. 生成Executor、ParameterHandler、ResultSetHandler、StatementHandler代理對象的入口!
  3. 各攔截器的存儲
  4. MapperRegistry 註冊mapperInterface 經過buildSqlSessionFactory方法中XMLConfigBuilder對mapper.xml文件的解析出的mapperInterface!!
  5. MapperRegistry生成MapperProxy入口。
  6. .....等等

SqlSessionFactoryBean

構建DefaultSqlSessionFactory的工廠類, 因此咱們直接看getObject()方法的核心buildSqlSessionFactory方法spring

public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }
  @Override
  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

buildSqlSessionFactory方法

內部構建了一個關鍵對象Configuration 不少處理都須要用到它sql

  • 將interceptor填入Configuration的interceptorChain中
  • 指定TransactionFactory爲SpringManagedTransactionFactory(數據庫交互事務的處理)

  • dataSource與transactionFactory都封裝在Environment中後存儲在configuration
  • XMLMapperBuilder解析指定路徑下的mapper.xml文件,包涵了ResultMaps、Cache、Statement的構建描述信息。並註冊mapper接口信息!

MapperFactoryBean

MapperProxy的工廠類。數據庫

在【mybatis的MapperScan到底作了什麼】的文章中分析:ClassPathMapperScanner指定用MapperFactoryBean來處理Mapper接口 。文章中提到過bean的定義信息BeanDefinition存儲在了DefaultListableBeanFactory中。mybatis

那是如何構建bean的呢?app

AbstractBeanFactory.doGetBean
根據beanName去DefaultListableBeanFactory的beanDefinitionMap中獲取對象定義並構建實例ide

DefaultListableBeanFactory是AbstractBeanFactory的實現類。函數

SqlSessionTemplate

繼承sqlSession接口,SqlSession對象徹底包含以數據庫爲背景的全部執行SQL操做的方法。ui

SqlSession的update/insert/delete會調用BaseExecutor的update方法;

SqlSession的selectList會調用BaseExecutor的query方法;

經過源碼能夠看出SqlSessionTemplate中有一個代理對象sqlSessionProxy,實際的handler是SqlSessionInterceptor

SqlSessionInterceptor

構建DefaultSqlSession

首先:
斷定在事務管理器中當前線程是否有SqlSessionHolder,有則referenceCount++並返回SqlSession;無則SqlSessionFactory.opeanSession開啓一個SqlSession, 並斷定是否有事務;有則將SqlSession包裝成SqlSessionHolder,與當前線程綁定在TransactionSynchronizationManager。
因此: 若是沒開啓事務SqlSession必定會真正的建立,有事務則視spring的事務傳播級別來複用!

斷定事務的狀況下當前線程綁定 DefaultSqlSession與DefaultSqlSessionFactory的關係:

DefaultSqlSessionFactory執行openSessionFromDataSource方法

建立一個實例由SpringManagedTransactionFactory構建的SpringManagedTransaction此實例含Connection內部變量,此時的值爲null)。 建立一個類型由傳入的ExecutorType(默認是「SIMPLE」)指定的Executor被包裝成invocationHandler爲Plugin(包裝了Interceptor)的代理對象, 並返回DefaultSqlSession。

MapperProxy

對於每個Mapper的BeanWrapper指定MapperFactoryBean後的getObject()處理:
此處的getSqlSession() 返回的是SqlSessionTemplate對象,mapperInterface是實際的mapper接口

接下來的執行過程爲:

SqlSessionTemplate的getMapper方法--->Configuration的getMapper方法 ---> MapperRegistry的getMapper方法----> MapperProxyFactory的newInstance方法

因此: 每個Mapper其實是一個InvocationHandler爲MapperProxy的代理對象!

MapperMethod

MapperProxy的invoke會進入MapperMethod的execute方法。
將會斷定是什麼類型的sql命令,該用什麼樣的對象來處理返回結果,若是參數含有rowBounds則調用分頁的查詢。

command.getName()是xml配置文件中的sql id, args是sql的入參。

StatementHandler

最終的執行! 實例能夠參見SimpleStatementHandler

public interface StatementHandler {
  //從鏈接中獲取一個Statement
  Statement prepare(Connection connection)
      throws SQLException;
 
  //設置statement執行裏所需的參數
  void parameterize(Statement statement)
      throws SQLException;
 
  //批量
  void batch(Statement statement)
      throws SQLException;
 
  //更新:update/insert/delete語句
  int update(Statement statement)
      throws SQLException;
 
  //執行查詢
  <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException;
 
  BoundSql getBoundSql();
 
  ParameterHandler getParameterHandler();
}

何時獲取鏈接

當實際調用mapper中的方法時 -> MapperProxy.inovke -> MapperMethod.execute ,最終回到了SqlSessionTemplate內部對象sqlSessionProxy執行。

上文也說過sqlSessionProxy的handler是SqlSessionInterceptor其會執行進入DefaultSqlSession(implements SqlSession)

DefaultSqlSession的執行

在上文中有講過: Executor 其實是一個InvocationHandler爲Plugin的代理類。
因此:實際上Executor 在執行方法時,將會執行對象代理的Plugin的invoke方法(interceptor.intercept()方法) 再執行Executor實例的方法。

以update方法爲例:

在Executor實例BaseExecutor的update方法中跳轉入SimpleExecutor的doUpdate方法。 內部在執行prepareStatement時,會獲取connection

public class SimpleExecutor extends BaseExecutor {

  public SimpleExecutor(Configuration configuration, Transaction transaction) {
    super(configuration, transaction);
  }

  @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
	  //得到配置文件對象
      Configuration configuration = ms.getConfiguration();
	  //得到statementHandler裏面有statement,來處理
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      stmt = prepareStatement(handler, ms.getStatementLog());
	  //最終是一個statement進行處理
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }

  @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裏面有statement,來處理
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
	  //得到statement
      stmt = prepareStatement(handler, ms.getStatementLog());
	  //最終是一個statement進行處理
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

  @Override
  public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
    return Collections.emptyList();
  }

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog); // 獲取鏈接
    stmt = handler.prepare(connection);
	//將Statement轉爲PrepareStatement
    handler.parameterize(stmt);
    return stmt;
  }

}

獲取connection是從BaseExecutor中的transcation中獲取; 又回到了org.mybatis.spring.transaction.SpringManagedTransaction(管理JDBC鏈接的open/commit/rollback/close)

最終來到org.springframework.jdbc.datasource.DataSourceUtils
首先從事務管理器TransactionSynchronizationManager的ThreadLocal中獲取當前datasource對應的ConnectionHolder,若是爲空或者沒有鏈接,則構建新的鏈接。若是是在spring事務內,由於在開啓事務過程當中會綁定一個已經提早開啓的鏈接,因此此處會返回該鏈接(事務是線程內獨佔共享的), 若是是非事務的則會直接新增鏈接。固然: 若是是在開啓事務的線程中也會將新增的Connection包裝成ConnectionHolder並綁定在線程上下文中。

相關文章
相關標籤/搜索