MyBatis学习总结(二)

1 MyBatis 是如何将 sql 执行结果封装为目标对象并返回的?都有哪些映射形式?

  1. 使用 标签,逐一定义列名和对象属性名之间的映射关系;

  2. 使用 sql 列的别名功能,将列别名书写为对象属性名,比如 T_NAME AS NAME,对象属性名一般是 name;

有了列名与属性名的映射关系后,MyBatis 通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。

2 MyBatis 是否支持延迟加载?

延迟加载是指在需要访问某个对象的属性时,才去初始化该属性。在 MyBatis 中,延迟加载可以用于嵌套查询中避免产生大量的 SQL 查询语句,提高查询效率。

MyBatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询。在 MyBatis 配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled=true|false。

3 MyBatis延迟加载实现原理

使用 CGLIB 创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用 a.getB().getName() ,拦截器 invoke() 方法发现 a.getB() 是 null 值,那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,然后调用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成 a.getB().getName() 方法的调用。这就是延迟加载的基本原理。

4 MyBatis 中如何执行批处理?

使用 BatchExecutor 完成批处理。

批处理是指在执行数据库操作时,将多个 SQL 语句一次性发送到数据库中执行,而不是每次仅发送一个 SQL 语句。

批处理可以提高数据库操作的效率,因为它可以将多个数据库操作合并为一个事务来执行,从而减少了数据库连接和网络通信的开销。同时,批处理还可以减少锁表的时间,提高并发性能。在数据批量插入、更新、删除等场景中,批处理尤为重要,因为这些操作可能会涉及到大量的数据,如果使用逐条操作,效率会非常低下。

5 Mybatis中的Executor是什么

在 MyBatis 中,Executor 是 MyBatis 的执行器,用于执行 SQL 语句。它通过 SqlSession.getMapper(Class type) 方法获取指定 Mapper 的代理实现类,然后执行相应的方法,实现数据库的访问操作。在进行数据库操作时,Executor 的作用非常重要。MyBatis 中共有三种 Executor:

  1. SimpleExecutor:每执行一次 update 或者 query 操作,就开启一个 Statement 对象,用完立刻关闭 Statement 对象,在返回结果之前,不会开启下一个 SQL 操作的 Statement 对象。

  2. ReuseExecutor:缓存 Statement 对象,在下一次操作相同 SQL 语句时,从缓存中获取 Statement 对象,而不是新建一个 Statement 对象。

  3. BatchExecutor:将多个 SQL 语句一次性发送到数据库中执行,以减少数据库连接、开销和锁表时间。

在 MyBatis 的配置文件中,可以通过设置 标签下的 defaultExecutorType 属性来设置默认的执行器类型,也可以通过 SqlSession 的方法来指定不同的执行器类型。

Executor 的作用不仅仅是执行 SQL 语句,还有以下几个方面:

  1. 封装数据库连接的获取和释放过程,屏蔽底层 JDBC API 的细节,使开发者专注于 SQL 操作。

  2. 处理缓存功能,避免因多次从数据库中查询同一个对象而导致的性能浪费。

  3. 处理返回结果集的类型和数量,对查询结果进行处理和封装,返回与 Mapper 方法声明相同类型的结果对象。

需要注意的是,不同的执行器类型有各自的优缺点,需要根据实际的业务场景和系统性能做出选择。简单来说,SimpleExecutor 适用于单线程的短时连接,系统并发度不高的场景;ReuseExecutor 适用于多线程、高并发、请求量大的场景;BatchExecutor 适用于数据量大、插入、更新、删除等批量操作。

6 MyBatis 中如何指定使用哪一种 Executor 执行器?

在 MyBatis 配置文件中,可以指定默认的 ExecutorType 执行器类型,也可以手动给 DefaultSqlSessionFactory 的创建 SqlSession 的方法传递 ExecutorType 类型参数。

7 MyBatis 是否可以映射 Enum 枚举类?

MyBatis 可以映射 Enum 枚举类。

在 MyBatis 的 Mapper 文件中,可以声明一个 parameterType 或 resultMap,将 Enum 枚举类作为参数或返回结果:

  1. 将 Enum 枚举类作为参数

在 Mapper 文件中,可以直接声明一个 Enum 枚举类的参数类型:


  1. 将 Enum 枚举类作为返回结果

在 resultMap 中,可以配置对应枚举类的转换器类型,将数据库存储的字符串类型转换成相应的枚举类型:


  
  
  

其中,typeHandler 指定转换器的类型,GenderTypeHandler 是自定义的实现了 TypeHandler 接口的类型转换器类。需要注意的是,TypeHandler 的泛型类型需要和枚举类的类型保持一致:

public class GenderTypeHandler implements TypeHandler {
  @Override
  public void setParameter(PreparedStatement ps, int i, Gender parameter, JdbcType jdbcType) throws SQLException {
    ps.setString(i, parameter.name());
  }
 
  @Override
  public Gender getResult(ResultSet rs, String columnName) throws SQLException {
    return Gender.valueOf(rs.getString(columnName));
  }
 
  @Override
  public Gender getResult(ResultSet rs, int columnIndex) throws SQLException {
    return Gender.valueOf(rs.getString(columnIndex));
  }
 
  @Override
  public Gender getResult(CallableStatement cs, int columnIndex) throws SQLException {
    return Gender.valueOf(cs.getString(columnIndex));
  }
}

这样,MyBatis 就可以很好的支持枚举类型的映射了。

7 MyBatis 映射文件中,如果 A 标签通过 include 引用了 B 标签的内容,请问,B 标签能否定义在 A 标签的后面,还是说必须定义在 A 标签的前面?

虽然 MyBatis 解析 xml 映射文件是按照顺序解析的,但是,被引用的 B 标签依然可以定义在任何地方,MyBatis 都可以正确识别。原理是,MyBatis 解析 A 标签,发现 A 标签引用了 B 标签,但是 B 标签尚未解析到,尚不存在,此时,MyBatis 会将 A 标签标记为未解析状态,然后继续解析余下的标签,包含 B 标签,待所有标签解析完毕,MyBatis 会重新解析那些被标记为未解析的标签,此时再解析 A 标签时,B 标签已经存在,A 标签也就可以正常解析完成了。

8 简述 MyBatis 的 xml 映射文件和 MyBatis 内部数据结构之间的映射关系?

MyBatis 将所有 xml 配置信息都封装到 All-In-One 重量级对象 Configuration 内部。在 xml 映射文件中, 标签会被解析为 ParameterMap 对象,其每个子元素会被解析为 ParameterMapping 对象。 标签会被解析为 ResultMap 对象,其每个子元素会被解析为 ResultMapping 对象。每一个