Mybatis源码解析(一)——Mybatis的核心组件

Mybatis的执行流程及核心组件

在这里插入图片描述
各组件的作用:

  • Configuration:用于描述Mybatis的主配置信息,其他组件可通过这个组件获取需要的配置信息;

  • MappedStatement:用于描述Mapper中的SQL配置信息,是对Mapper XML配置文件中的sql标签或者Mapper接口中的sql注解配置信息的封装;

  • SqlSession:为Mybatis提供的面向用户的API,表示和数据库交互时的会话对象,用于完成数据库的增删改查功能。SqlSession是Executor组件的外观,目的是对外提供易于理解和使用的数据库操作接口。

  • Executor:是Mybatis的SQL执行器,Mybatis中对数据库所有的增删改查都是由Executor组件完成的;

  • StatementHandler:封装了对JDBCStatement对象的操作。比如为Statement对象设置参数,调用Statement接口提供的方法与数据库交互。

  • ParameterHandler:当Statement类型为CallableStatement 或PreparedStatement是,用于为Statement对象参数占位符设置值;

  • ResultSetHandler:当执行SQL类型为SELECT语句时,用于将查询结果转换成Java对象;

  • TypeHandler:TypeHandler是Mybatis中的类型处理器,用于处理Java类型与JDBC类型之间的映射,作用主要体现在能够根据Java类型调用PreparedStatement或CallableStatement对象对应的set方法为Statement对象设置值,而且能够根据Java类型调用ResultSet对象对应的get获取SQL执行结果。

Configuration详解

Configuration类中定义了一系列的属性用来控制Mybatis运行时的行为;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Configuration除了提供属性控制Mybatis的行为外,还作为容器存放TypeHandler类处理器、TypeAlias类别名、Mapper接口及Mapper SQL配置信息,这些信息在Mybatis框架启动时注册到Configuration组建中。

Executor详解

SqlSession是Mybatis提供的操作数据库的API,但是真正执行SQL的是Executor组件,Executor接口中定义了对数据库的增删改查方法,其中query和queryCursor方法用于执行查询操作,update方法用于执行插入、删除、修改操作。

在这里插入图片描述
Mybatis提供了3中不同的Executor,分别为SimpleExecutor、ResueExecutor、BatchExecutor这些Executor都继承自BaseExecutor。

  • SimpleExecutor是最基础的执行器,能够完成基本的增删改查操作;

  • ResueExecutor对JDBC中的statement对象做了缓存,当执行相同SQL语句时,直接从缓存中取出Statement对象进行复用,缪米娜了频繁创建和销毁Statement对象,提升系统性能;

  • BatchExcutor则会对调用同一个Mapper执行的uodate、insert和delete操作,调用statement对象的批量操作功能。

当Mybatis开启了二级缓存功能时,会使用CachingExecutor对SimpleExecutor、ResueExecutor、BatchExecutor进行装饰,为查询操作增加二级缓存功能。

Executor与数据库交互需要Mapper配置信息,Mybatis通过mappedStatement对象描述Mapper配置信息,因此Executor需要一个MappedStatement对象作为参数,Mybatis在应用启动时会解析所有的Mapper配置信息,将Mapper配置解析成MappedStatement对象注册到Configuration组件中,我们可以调用Configuration对象的getMappedStatement()方法获取对应的MappedStatement对象,获取MappedStatement对象后,根据SQL类型调用Executor对象的query或者update方法即可。

MappedStatement详解

Mybatis通过MappedStatement描述 SQL标签或者SQL注解配置的SQL信息。

StatementHandler详解

StatementHandler组件封装了对JDBC Statement的操作,例如设置Statement对象的fetchSize属性、设置查询超时时间、调用JDBC Statement与数据库交互等。

public interface StatementHandler {

  // 用于创建JDBC Statement对象,并完成属性设置
  Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;

  // 该方法使用Mybatis中的ParameterHandler组件为PreparedStatement和CallableStatement参数占位符设置值
  void parameterize(Statement statement)
      throws SQLException;

  // 将SQL命令添加到批量处理执行列表中
  void batch(Statement statement)
      throws SQLException;

  // 调用Statement对象的execute()方法执行更新语句
  int update(Statement statement)
      throws SQLException;

  // 执行查询语句,并使用ResultSetHandler处理查询结果集
  <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException;

  // 带游标的查询,返回Cursor对象,能够通过动态地从数据库中加载数据,适用于查询数据量较大的情况,避免将所有数据加载到内存
  <E> Cursor<E> queryCursor(Statement statement)
      throws SQLException;

  // 获取Mapper中配置的SQL信息,封装了动态SQL解析后的SQL文本和参数映射信息
  BoundSql getBoundSql();

  // 获取ParameterHandler实例
  ParameterHandler getParameterHandler();

}

在这里插入图片描述

TypeHandler详解

使用JDBC API开发应用程序,其中一个比较繁琐的环节是处理JDBC类型与Java类型之间的转换,涉及类型转换的两种情况如下:

  1. PreparedStatement对象为参数占位符设置值时,需要调用PreparedStatement接口中提供的一系列set方法,将java类型转换为对应的JDBC类型并为参数占位符赋值。
  2. 执行SQL语句获取ResultSet对象后,需要调用ResultSet对象的get方法获取字段值,此时会将JDBC类型转化为java类型。
/**
 * @author Clinton Begin
 */
public interface TypeHandler<T> {

  // 为PreparedStatement对象设置参数
  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  /**
   * Gets the result.
   *
   * @param rs
   *          the rs
   * @param columnName
   *          Column name, when configuration <code>useColumnLabel</code> is <code>false</code>
   * @return the result
   * @throws SQLException
   *           the SQL exception
   */
  // 根据列名称获取该列的值
  T getResult(ResultSet rs, String columnName) throws SQLException;

  // 根据列索引获取该列的值
  T getResult(ResultSet rs, int columnIndex) throws SQLException;

  // 获取存储过程调用过程
  T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}

Mybatis中内置了很多TypeHandler,如StringTypeHandler用于java.lang.String类型和JDBC中的CHAR、VARCHAR等类型之间的转换·。

public class StringTypeHandler extends BaseTypeHandler<String> {

  // 调用PreparedStatement对象的setString方法将java中的String类型转换为JDBC类型,并为参数占位符赋值。
  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setString(i, parameter);
  }

  // 调用ResultSet对象的getString方法将JDBC中的字符串类型转为Java中的String类型,并返回的值
  @Override
  public String getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    return rs.getString(columnName);
  }

  @Override
  public String getNullableResult(ResultSet rs, int columnIndex)
      throws SQLException {
    return rs.getString(columnIndex);
  }

  @Override
  public String getNullableResult(CallableStatement cs, int columnIndex)
      throws SQLException {
    return cs.getString(columnIndex);
  }
}

Mybatis通过TypeHandlerRegistry建立JDBC类型、Java类型与TypeHandler之间的映射关系。

public final class TypeHandlerRegistry {

  // JDBC类型<==>TypeHandler
  private final Map<JdbcType, TypeHandler<?>>  jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);

  // Java类型<==> JDBC类型 <==> TypeHandler
 private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
  private final TypeHandler<Object> unknownTypeHandler;

  //  TypeHandler class 对象 <==> TypeHandler
  private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();

  private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();

  private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;

在TypeHandlerRegistry中,通过Map对对象保存JDBC类型、Java类型与TypeHandler之间的关系,在TypeHandlerRegistry类的构造方法中·,通过registry方法注册所有的TypeHandler。
该方法就是讲Java、JDBC类型和TypeHandler的对应关系添加到Map对象中。TypeHandlerRegistry提供了一系列重载的getTypeHandler方法,该方法能够根据Java类型或者JDBC类型获取的TypeHandler对象。

  private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
    if (javaType != null) {
      Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
      if (map == null || map == NULL_TYPE_HANDLER_MAP) {
        map = new HashMap<>();
      }
      map.put(jdbcType, handler);
      typeHandlerMap.put(javaType, map);
    }
    allTypeHandlersMap.put(handler.getClass(), handler);
  }

ParameterHandler详解

当使用PreparedStatement或者CallableStatement对象时,如果SQL语句中有参数占位符,在执行SQL之前,就需要为参数占位符设置值,ParameterHandler的作用是在PreparedStatementHandler 和 CallableStatementHandler操作对应的Statement执行数据库交互之前为参数占位符设置值。

public interface ParameterHandler {

  // 用于获取执行Mapper时传入的参数对象
  Object getParameterObject();

  // 用于为JDBC PreparedStatement或者CallableStatement对象设置参数
  void setParameters(PreparedStatement ps) throws SQLException;

}
public class DefaultParameterHandler implements ParameterHandler {

  private final TypeHandlerRegistry typeHandlerRegistry;

  private final MappedStatement mappedStatement;
  private final Object parameterObject;
  private final BoundSql boundSql;
  private final Configuration configuration;

  public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    this.mappedStatement = mappedStatement;
    this.configuration = mappedStatement.getConfiguration();
    this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
    this.parameterObject = parameterObject;
    this.boundSql = boundSql;
  }

  @Override
  public Object getParameterObject() {
    return parameterObject;
  }

  @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      // 获取所有参数的映射信息
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          // 参数属性名称
          String propertyName = parameterMapping.getProperty();
          // 根据参数属性名称获取参数值
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          // 获取参数对应的TypeHandler
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            // 调用TypeHandler的setParameter方法为Statement对象参数占位符设置值
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException | SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }

}

Myabtis通过ParameterMapping描述参数映射信息,在DefaultParameterHandler类的setParameters方法中,首先获取Mapper配置中的参数映射,然后对所有参数映射信息进行遍历,接着根据参数名称获取对应的参数值,调用对应的TypeHandler对象的setParameter方法为Statement对象中的参数占位符设置值。

ResultSetHandler详解

ResultSetHandler用于在StatementHandler对象执行完查询操作或者存储过程后,对结果集或存储过程的执行结果进行处理。

public interface ResultSetHandler {

  // 
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;

  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

  void handleOutputParameters(CallableStatement cs) throws SQLException;

}
  • handleResultSets:获取Statement对象中的ResultSet对象,对ResultSet对象进行处理,返回包含结果实体的List对象。
  • handleCursorResultSets:将ResultSet对象包装成Cursor对象,对Cursor进行遍历时,能够动态地从数据库查询数据,避免一次性将所有数据加载到内存中。
  • handleOutputParameters:处理存储过程调用结果。
  @Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<>();

    int resultSetCount = 0;

    // 1、获取ResultSet对象,将ResultSet对象包装为ResultSetWrapper
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    // 2、获取ResultMap信息,一般只有一个ResultMap
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      // 3、调用handleResultSet方法处理结果集
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }
    // 对multipleResults进行处理,如果只有一个结果集,则返回结果集中的元素,否则返回多个结果集
    return collapseSingleResultList(multipleResults);
  }
  1. 从Statement对象中获取ResultSet对象,然后将ResultSet包装成ResultSetWrapper对象,通过ResultSetWrapper对象能够更方便地获取表字段名称、字段对应的TypeHandler信息。
  2. 获取解析Mapper接口及Mapper SQL配置生成的ResultMap信息,一条语句一般对应一个ResultMap。
  3. 调用handleResultSet方法对ResultSetWrapper对象进行处理,将生成的实体对象存放在multipleResults列表中。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值