1、Mapper 接口方法如何与注解里的 SQL 进行绑定的?

  • 根据 Mapper 接口、其方法、方法上的注解,生成 mappedStatementId 与 MapperStatement,注册到 configuration 对象中

  • 根据 Mapper 接口方法查到并调用对应的 MappedStatement,执行 SQL

流程与 Mapper 接口与 xml 绑定类似。


分析:

解析生成注册 MapperStatement 的代码入口在 MapperRegistry addMapper 方法

//使用 MapperProxyFactory 包装 Mapper 接口 Class 对象
knownMappers.put(typenew MapperProxyFactory<>(type));

//解析 Mapper 接口方法上的注解,生成对应的 MapperStatement
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();


获取 Mapper 接口的动态代理对象的代码入口在 MapperRegistry getMapper 方法

public <T> T getMapper(Class<TtypeSqlSession 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);
    } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}



2、Mapper 接口并没有实现类,它是如何工作的?

  • Mapper 接口的 Class 对象,被解析包装成 MapperProxyFactory 对象

  • SqlSession 获取 Mapper 接口时,通过 MapperProxyFactory 对象实例化 MapperProxy 动态代理 Mapper 接口

  • 执行 Mapper 接口的方法时,动态代理反射调用 MapperProxy 的 invoke 方法,根据接口与方法找到对应 MappedStatement 执行 SQL

源码入口与上题同。



3、Mapper 接口中能不能根据参数不同进行重载?

不能

  • MapperedStatement 的 id 属性值等于 Mapper 接口的 包名.接口名.方法名作为 key 添加到 Configuration 对象的 Map 结构的 mappedStatements 属性里

  • 查找 MapperedStatement 执行 SQL 时,也是根据 Mapper 接口的 包名.接口名.方法名 作为 SqlCommand 的 name 属性值,在 Configuration 对象的 mappedStatements 找到对应的 MapperedStatement 对象

  • 即接口中方法名相同 key 就相同,只能获取一个 MapperedStatement 对象,无法重载



4、MyBatis 有哪些分页的方式?分页插件的原理是什么?

  • 使用 RowBounds 对象进行分页,它是对 ResultSet 结果集进行内存分页

  • 在 xml 或者 注解的 SQL 中传递分页参数

  • 使用分页插件 Mybatis-PageHelper

其中分页插件的原理是,使用 MyBatis 提供的插件接口,拦截待执行的 SQL,根据数据库种类的配置与分页参数,生成带 SQL 语句,执行。



5、MyBatis 是如何将 sql 执行结果转换为目标对象并返回的?有哪些映射形式?

  • 方式一、<select> 标签使用 resultType 参数,传递 Java 类,sql 中 select 的字段名保持与 Java 类属性名称一致

  • 方式二、使用 <resultMap> 标签,定义数据库列名和对象属性名之间的映射关系

  • 方式三、使用注解 select 的字段名保持与接口方法返回的 Java 类或集合的元素类的属性名称一致

方式一
<select id="selectUser" resultType="constxiong.po.User" parameterType="constxiong.po.User">
    select * from user where id = #{id}
</select>


方式二
<select id="selectUserByResultMap" resultMap="userMap" parameterType="constxiong.po.User">
    select * from user where id = #{id}
</select>
<resultMap id="userMap" type="constxiong.po.User">
    <id property="id" column="id" />
    <result property="mc" column="name"/>
</resultMap>


方式三
@Select("select * from user")
List<User>  selectAllUsers();


根据解析得到 ResultMap 结合 sql 执行结果,通过反射创建对象,根据映射关系反射填充返回对象的属性

源码体现在 DefaultResultSetHandler 的 handleResultSets 方法

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;
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      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++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }



汇总于:Java 面试题集


更多相关文章

  1. 详解第三种创建线程的方式-Callable接口
  2. Spring Ioc 实例化 Bean 对象有几种方式?
  3. Javascript面向对象入门
  4. 【编测编学】接口测试面试题必背(下)
  5. 如何在 Java 中构造对象(学习 Java 编程语言 034)
  6. 快速测试 API 接口的新技能
  7. java创建对象的过程(内存角度分析)

随机推荐

  1. android实现本程序数据的备份与恢复
  2. andorid 记录,以后看
  3. Android四大功能组件深入分析
  4. Android 之 Eclipse 导入 Android 源码
  5. TextView过长显示省略号, TextView文字中
  6. android:textAppearance解析
  7. Android异步处理三:Handler+Looper+Messag
  8. android setGravity()的使用
  9. Android CTS的使用
  10. Android中的Selector的用法