MyBatis:SqlSessionFactory构建过程

SqlSessionFactory 是 MyBatis 的核心类,从这类名可以看出这是个 SqlSession 工厂类,其最重要的功能就是提供创建 MyBatis 的核心接口 SqlSession,所以需要先创建 SqlSessionFactory,为此需要提供配置文件和相关参数。

MyBatis 采用构造模式去创建 SqlSessionFactory,可以通过 SqlSessionFactoryBuild 去创建。构建分两步。

构建SqlSessionFactory

  1. 第一步,通过 org.apache.ibatis.builder.xml.XMLConfigBuilder 解析配置 XML 文件,读出配置参数,并将读取的数据存入 org.apache.ibatis.session.Configuration 类中。

    注意:MyBatis 几乎所有的配置都存在这里。

  2. 第二步,使用 Configuration 对象去创建 SqlSessionFactory。SqlSessionFactory 是个接口,而不是实现类,MyBatis 提供了一个默认的实现类 org.apache.ibatis.session.defaults.DefaultSqlSessionFactory。

    注意:在大部分情况下我们都没必要自己去创建新的 SqlSessionFactory 的实现类。

SqlSessionFactory创建方式是一种 Builder模式。对于复杂的对象而言,直接使用构造方法创建会有些困难,这会导致大量逻辑放在构造方法 ,由于对象的复杂性,在构建的时候更希望一步步有秩序的构建它,从而降低其复杂性。这时候使用一个参数总领全局,例如 Configuration 类,然后分步构建。

MyBatis 提供了创建 SqlSession 的静态工具类 org.apache.ibatis.session.SqlSessionManager,继承了SqlSessionFactory 接口 ,提供了一系列的 newInstance 静态方法用于 创建 SqlSessionFactory 。

构建SqlSessionFactory源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class SqlSessionFactoryBuilder {

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
}

public class XMLConfigBuilder extends BaseBuilder {

private boolean parsed;
private final XPathParser parser;
private String environment;
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
//省略.....
private XMLConfigBuilder(Class<? extends Configuration> configClass, XPathParser parser, String environment,
Properties props) {
super(newConfig(configClass));
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
private static Configuration newConfig(Class<? extends Configuration> configClass) {
try {
// 反射创建对象
return configClass.getDeclaredConstructor().newInstance();
} catch (Exception ex) {
throw new BuilderException("Failed to create a new Configuration instance.", ex);
}
}
}

构建 Configuration

构建 SqlSessionFactory 中,Configuration 是最重要的,它的作用如下:

  • 读入配置文件,包括基础配置 XML 文件和映射器的 XML 文件。
  • 初始化基础配置,比如 MyBatis 别名等,一些重要的类对象,例如,插件,映射器, ObjectFactory 和 typeHandler 对象。
  • 提供单例,为后续创建 SessionFactory 服务并提供配置的参数。
  • 执行一些重要的对象方法,初始化配置信息。

Configuration 是通过 XMLConfigBuilder 构建的。MyBatis 会读出所有 XML 配置的信息。然后,将这些信息保存到 Configuration 类的单例中。它会做如下初始化。

  • properties 全局参数
  • settings 设置
  • typeAliases 别名
  • typeHandler 类型处理器
  • ObjectFactory 对象
  • Plugin 插件
  • environment 环境
  • DatabaseIdProvider 数据库标识
  • Mapper 映射器

映射器的内部组成

一般而言,一个映射器是由 3 个部分组成:

  • MappedStatement,它保存映射器的一个节点(select | insert | delete | update)。包括许多我们配置的 SQL,SQL 的 id,缓存信息,resultMap,parameterMap,resultType,languangeDriver等重要配置内容。
  • SqlSource,它是提供 BoundSql对象的地方,它是 MappedStatement 的一个属性。
  • BoundSql,它是建立 SQL 和参数的地方。它有 3 个常用的属性: SQL,parameterObject,parameterMappings。

MappedStatement 对象涉及的东西较多,一般都不会修改它,因为容易产生不必要的错误。

SqlSource 是一个接口,它主要作用是根据参数和其他的规则组装 SQL,也比较复杂,一般也不需要修改它。

BoundSql ,对于参数和 SQL 而言,主要的规则反映在 BoundSql 类对象上,在插件中往往需要拿到它进而拿到当前运行的 SQL 和参数以及参数规则,做出适当的修改,来满足我们特殊的需求。

BoundSql 会提供 3 个主要属性: parameterMappings,parameterObject 和 sql。

  • parameterObject ,为参数本身,可以传递简单对象,POJO,Map,或者 @Param 注解的参数,在插件中常用到。

  • 传递简单对象(包括 int,String,float,double等),MyBatis 会把参数变为 Integer 对象传递,类似的 long,String,float,double也是如此。

  • 传递的是 POJO 或 Map,parameterObject 就是传入的 POJO 或 Map 不变。

  • 也可以传递多个参数,如果没有 @Param 注解,那么 MyBatis 就会把 parameterObject 变为一个 Map<String, Object> 对象,其键值的关系是按顺序来规划的,类似于这样的形式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    "1":p1,
    "2":p2,
    "3":p3,
    ....
    "param1":p1,
    "param2":p2,
    "param3":p3
    }

    所以在编写的时候,可以使用 #{param1}#{1} 去引用第一个参数。

  • 如果使用 @Param 注解,那么 MyBatis 就会把 parameterObject 变为一个 Map<String, Object> 对象,类似于没有 @Param 注解,只是把其数字的键值对应置换为了 @Param 注解的键值。比如注解 @Param("key1") String p1

    1
    2
    3
    {
    "key1":p1
    }
  • parameterMappings,它是一个 List,每一个元素都是 ParameterMapping 的对象。这个对象会描述我们的参数,包括属性,名称,表达式,javaType,jdbcType,typeHandler 等重要信息。一般不需要改变它。

    通过它可以实现参数和 SQL 的结合,以便 PreparedStatement 能够通过它找到 parameterObject 对象的属性并设置参数,使得程序准确运行。

  • sql 属性就是我们书写在映射器里面的一条 SQL,在大多数时候无需修改它,只有在插件的情况下,可以根据需要进行改写,但一定要谨慎行事。

作者

光星

发布于

2023-06-05

更新于

2023-07-04

许可协议

评论