Mybatis启动流程详解

今天,我拟从一个简单的selectOne查询入手,追踪mybatis框架执行的足迹。

Mybatis整体流程图

Mybatis整体流程图

单元测试代码(selectOne类型)

    @Test
    public  void queryFinancialAccountTest(){
        FundFinancialExtDTO financialAccountExtPO = new FundFinancialExtDTO();
        // 部分代码略去
        FundAccAndExtDTO fundAccAndExtDTO = fundFinancialExtMapper.queryFinancialAccount(financialAccountExtPO);
    }

mybatis.xml配置

    <!-- Mybatis配置 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.roger.practice.dal.dao" />
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="typeAliasesPackage" value="com.roger.practice.entity" />
        <property name="mapperLocations" value="classpath*:mapper/**/*.xml" />
        <property name="plugins">
            <array>
                <bean class="com.roger.practice.mybatis.page.interceptor.PageInterceptor" />
                <bean class="com.roger.practice.mybatis.page.interceptor.PageSqlRewriteInterceptor">
                    <property name="dialect" value="oracle" />
                </bean>
            </array>
        </property>
    </bean>

初始化部分 SqlSessionFactoryBuilder

Mybatis集成在Spring中,方法afterPropertiesSet()将在所有的属性被初始化后被调用。查看org.mybatis.spring.SqlSessionFactoryBean类中的afterPropertiesSet()方法,发现该方法开始创建SqlSessionFactory实例

    @Override
    public void afterPropertiesSet() throws Exception {
        // 略去代码请参考源码
        this.sqlSessionFactory = buildSqlSessionFactory();
    }

    protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
        // 略去代码将对象工厂objectFactory,对象包装工厂objectWrapperFactory,类型别名typeAliasesPackage/typeAliases,插件plugins,类型处理器typeHandlersPackage/typeHandlers,缓存cache,环境environments,事务工厂transactionFactory等信息配置存到Configuration对象中。
        // 通过xmlMapperBuilder来解析mapper文件
        if (!isEmpty(this.mapperLocations)) {
          for (Resource mapperLocation : this.mapperLocations) {
            if (mapperLocation == null) {
              continue;
            }

            try {
              XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                  configuration, mapperLocation.toString(), configuration.getSqlFragments());
              xmlMapperBuilder.parse();
            } catch (Exception e) {
              throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
            } finally {
              ErrorContext.instance().reset();
            }

            if (LOGGER.isDebugEnabled()) {
              LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
            }
          }
        } else {
          if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
          }
        }
        return this.sqlSessionFactoryBuilder.build(configuration);
    }

org.apache.ibatis.session.SqlSessionFactoryBuilder提供了9种构造SqlSessionFactory的方法,但最终都要调用包含Configuration对象的构造方法,其通过加载配置文件构造SqlSessionFactory对象、返回DefaultSqlSessionFactory对象。

9种构造SqlSessionFactory的方法

    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }

org.apache.ibatis.binding.MapperProxyFactory
Spring负责创建SqlSessionTemplate,执行getMapper方法时会创建动态代理,代理Test用例中的FundFinancialExtMapper接口。

  @Override
  public <T> T getMapper(Class<T> type) {
    return getConfiguration().getMapper(type, this);
  }

SelectOne查询流程

基本查询流程

org.apache.ibatis.binding.MapperProxy
Mybatis初始化加载的时候,利用MapperProxy代理了自己的Mapper接口类,生成一个代理处理类。代理处理的逻辑都在invoke方法里,它根据目标类的接口(本例是FundFinancialExtMapper)生成 MapperMethod。sqlSession是由spring负责生成的SqlSessionTemplate,它是spring连接mybatis的模板类。接下来调用MapperMethod的execute方法就能获取执行结果。

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

org.apache.ibatis.binding.MapperMethod
MapperMethod就像是一个分发者,它根据SqlCommandType,并获取执行参数commandName和param,交由SqlSessionTemplate对象执行具体的操作。这样mapper对象与sqlSession就真正的关联起来了。本例中,将执行SqlSessionTemplate类中的selectOne方法。

        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);

org.mybatis.spring.SqlSessionTemplate
SqlSessionTemplate的实际执行是交给它的代理类完成的。查看SqlSessionTemplate构造函数可知,它是由内部类SqlSessionInterceptor动态代理的,所有的处理逻辑都是在invoke方法里。invoke方法里执行了:
1. 通过静态方法SqlSessionUtils.getSqlSession创建sqlSession,实际返回DefaultSqlSession对象。
2. DefaultSqlSession执行selectOne方法。
3. 执行成功则提交,出现异常则关闭sqlSession。

  // SqlSessionTemplate的构造函数
  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {
    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }
  // 内部类SqlSessionInterceptor
  private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        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) {
        // 略去代码请参考源码
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }

org.apache.ibatis.executor.Executor
sqlSession只是一个门面,真正发挥作用的是executor,对sqlSession方法的访问最终都会落到executor的相应方法上去。
executor对象是执行openSessionFromDataSource方法时创建的,见org.apache.ibatis.session.Configuration里的newExecutor方法。executor具体实现是SimpleExecutor,由于cacheEnabled默认为ture,还追加了缓存功能。另外它还可以追加拦截器。

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      // 实现缓存
      executor = new CachingExecutor(executor);
    }
    // 追加拦截器,比如分页拦截器
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

org.apache.ibatis.executor.CachingExecutor
query方法会最终委派org.apache.ibatis.executor.SimpleExecutor类中的doQuery方法。query方法如下:

  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

org.apache.ibatis.executor.SimpleExecutor
SimpleExecutor的doQuery方法是具体的实现。

  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的创建与executor的实现很相似,又是在Configuration里通过newStatementHandler方法创建的。
      // 由org.apache.ibatis.executor.statement.RoutingStatementHandler代理实际的StatementHandler实现。
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 对查询语句进行预编译,并解析参数实体。
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 方法执行时改由PreparedStatementHandler实际代理进行查询
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

通过StatementType获取具体的StatementHandler类,从默认配置可知,PreparedStatementHandler是实际代理的对象。其中构造StatementHandler的时候,查看BaseStatementHandler构造函数可知,ParameterHandler和ResultSetHandler也是有Configuration生成的,同样也可追加拦截器。

org.apache.ibatis.executor.statement.PreparedStatementHandler
PreparedStatementHandler的父类是BaseStatementHandler,BaseStatementHandler的构造函数是有这么一段:

    if (boundSql == null) { // issue #435, get the key before calculating the statement
      generateKeys(parameterObject);
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }

它触发了sql 的解析,在解析sql的过程中,TypeHandler也被决断出来了,决断的原则就是根据参数的类型和参数对应的JDBC类型决定使用哪个TypeHandler。比如:参数类型是String的话就用StringTypeHandler,参数类型是整数的话就用IntegerTypeHandler等。

query方法如下,它执行了execute方法并完成结果集的映射。

  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    // 实际执行在此处那!!!
    ps.execute();
    // 由org.apache.ibatis.executor.resultset.DefaultResultSetHandler代理实现
    // 完成结果集的映射
    return resultSetHandler.<E> handleResultSets(ps);
  }

org.apache.ibatis.scripting.defaults.DefaultParameterHandler
setParameters方法用来解析参数实体,其中propertyName获取参数名,value是参数值,typeHandler和jdbcType是参数类型。

org.apache.ibatis.executor.resultset.DefaultResultSetHandler
完成结果集的映射。

org.apache.ibatis.session.SqlSessionFactory
SqlSessionFactory作为SqlSession的工厂,提供了8种获取SqlSession的方法,同时还提供了获取Configuration的方法。
8种获取SqlSession的方法
8种获取SqlSession的方法主要涉及4个参数:是否自动提交、自定义Connection、事务级别、ExecutorType(Statement类型【普通、预处理、批处理】)。包含Connection类型参数的方法会调用openSessionFromConnection方法,其它都会调用openSessionFromDataSource方法,最终都返回DefaultSqlSession对象。

org.apache.ibatis.session.defaults.DefaultSqlSessionFactory是SqlSessionFactory接口的实现,以openSessionFromDataSource方法为例,创建sqlsession经过了以下几个主要步骤:
1) 从配置中获取Environment;
2) 根据Environment创建事务工厂TransactionFactory;
3) 从Environment中取得DataSource、进而创建事务对象Transaction;
4) 创建Executor对象;
5) 创建sqlsession对象。

  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();
    }
  }

  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, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

org.apache.ibatis.session.SqlSession
这里写图片描述

org.apache.ibatis.session.defaults.DefaultSqlSession
DefaultSqlSession实现了SqlSession接口,主要封装了Configuration对象、Executor对象、是否自动提交。

  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

  public DefaultSqlSession(Configuration configuration, Executor executor) {
    // 默认不自动提交
    this(configuration, executor, false);
  }

它利用自己封装的一套东西,还包括Executor(封装Statement)、ResultHandler(封装处理ResultSet对象)、RowBounds(封装分页对象),提供了CRUD、提供了缓存机制、提供了根据配置文件获取Sql语句的方法,提供了事务的提交和回滚等。

总结

大体流程就是:
1. 加载XML配置文件创建Configuration对象完成初始化,创建并使用SqlSessionFactory对象。
2. 利用MapperProxy代理具体的Mapper接口类,生成了MapperMethod。
3. Spring负责生成SqlSessionTemplate,它实际由SqlSessionInterceptor动态代理,所有的处理逻辑都是在 invoke方法里,主要是获取SqlSession、生成可带缓存可追加插件的Executor,并执行操作。DefaultSqlSession由SimpleExecutor静态代理执行查询操作。
4. RoutingStatementHandler根据配置Statement类型创建真正执行数据库操作的StatementHandler,实际由PreparedStatementHandler进行查询语句的预编译、查询参数实体解析、执行查询。
5. DefaultResultSetHandler完成结果集的映射。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值