MapperXML的解析和注册使用

  • 首先需要定义 SqlSessionFactoryBuilder 工厂建造者模式类,通过入口 IO 的方式对 XML 文件进行解析。当前我们主要以解析 SQL 部分为主,并注册映射器,串联出整个核心流程的脉络。

  • 文件解析以后会存放到 Configuration 配置类中,接下来会看到这个配置类会被串联到整个 Mybatis 流程中,所有内容存放和读取都离不开这个类。如我们在 DefaultSqlSession 中获取 Mapper 和执行 selectOne 也同样是需要在 Configuration 配置类中进行读取操作。

  • SqlSessionFactoryBuilder 作为整个 Mybatis 的入口,提供建造者工厂,包装 XML 解析处理,并返回对应 SqlSessionFactory 处理类。

  • 通过解析把 XML 信息注册到 Configuration 配置类中,再通过传递 Configuration 配置类到各个逻辑处理类里,包括 DefaultSqlSession 中,这样就可以在获取映射器和执行SQL的时候,从配置类中拿到对应的内容了。

构建SqlSessionFactory建造者工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* sqlSessionFactory建造者工厂
*
* @author:zzc
* @date: 2022/4/11
*/
public class SqlSessionFactoryBuilder {

public SqlSessionFactory build(Reader reader) {
XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(reader);
return build(xmlConfigBuilder.parse());
}

private SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
  • SqlSessionFactoryBuilder 是作为整个 Mybatis 的入口类,通过指定解析XML的IO,引导整个流程的启动。
  • 从这个类开始新增加了 XMLConfigBuilder、Configuration 两个处理类,分别用于解析 XML 和串联整个流程的对象保存操作。接下来会分别介绍这些新引入的对象。

XML 解析处理

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
47
48
49
50
51
/**
* xml解析处理
*
* @author:zzc
* @date: 2022/4/11
*/
public class XMLConfigBuilder extends BaseBuilder {

private Element root;

public XMLConfigBuilder(Reader reader) {
//1、调用父类初始化
super(new Configuration());
//2、dom4j处理xml
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(new InputSource(reader));
root = document.getRootElement();
} catch (DocumentException e) {
e.printStackTrace();
}

}


public Configuration parse() {

//解析映射器
try {
mapperElement(root.element("mappers"));
} catch (Exception e) {
e.printStackTrace();
}
return configuration;
}

private void mapperElement(Element mappers) throws IOException, DocumentException, ClassNotFoundException {
List<Element> mapperList = mappers.elements("mapper");
for (Element e : mapperList) {
// 解析处理,具体参考源码

//添加解析sql
configuration.addMappedStatement(mappedStatement);
}

//注册Mapper映射器
configuration.addMapper(Resources.classForName(namespace));
}

}
}
  • XMLConfigBuilder 核心操作在于初始化 Configuration,因为 Configuration 的使用离解析 XML 和存放是最近的操作,所以放在这里比较适合。
  • 之后就是具体的 parse() 解析操作,并把解析后的信息,通过 Configuration 配置类进行存放,包括:添加解析 SQL、注册Mapper映射器。
  • 解析配置整体包括:类型别名、插件、对象工厂、对象包装工厂、设置、环境、类型转换、映射器,但目前我们还不需要那么多,所以只做一些必要的 SQL 解析处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 构造器的基类,建造者基类
*
* @author:zzc
* @date: 2022/4/11
*/
public class BaseBuilder {

protected final Configuration configuration;

public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
}

public Configuration getConfiguration() {
return configuration;
}
}

通过配置类包装注册机和SQL语句

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
/**
* 配置项
*
* @author:zzc
* @date: 2022/4/11
*/
public class Configuration {

/**
* 映射注册机
*/
protected MapperRegistry mapperRegistry = new MapperRegistry(this);

protected final Map<String, MappedStatement> mappedStatements = new HashMap<>();

public void addMappers(String packageName) {
mapperRegistry.addMappers(packageName);
}

public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}

public boolean hasMapper(Class<?> type) {
return mapperRegistry.hasMapper(type);
}

public void addMappedStatement(MappedStatement ms) {
mappedStatements.put(ms.getId(), ms);
}

public MappedStatement getMappedStatement(String id) {
return mappedStatements.get(id);
}

}

在配置类中添加映射器注册机和映射语句的存放;

  • 映射器注册机是我们上一章节实现的内容,用于注册 Mapper 映射器所提供的操作类。
  • 另外一个 MappedStatement 是本章节新添加的 SQL 信息记录对象,包括记录:SQL类型、SQL语句、入参类型、出参类型等。

DefaultSqlSession结合配置项获取信息

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
/**
* @author:zzc
* @date: 2022/4/10
*/
public class DefaultSqlSession implements SqlSession {

private Configuration configuration;

public DefaultSqlSession(Configuration configuration) {
this.configuration = configuration;
}

@Override
public <T> T selectOne(String statement) {
return (T) ("你被代理了!" + statement);
}

@Override
public <T> T selectOne(String statement, Object parameter) {
MappedStatement mappedStatement = configuration.getMappedStatement(statement);
return (T) ("你被代理了!" + "\n方法:" + statement + "\n入参:" + parameter + "\n待执行SQL:" + mappedStatement.getSql());
}

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

@Override
public Configuration getConfiguration() {
return configuration;
}
}
  • DefaultSqlSession 相对于上一章节,这里把 MapperRegistry mapperRegistry 替换为 Configuration configuration,这样才能传递更丰富的信息内容,而不只是注册器操作。
  • 之后在 DefaultSqlSession#selectOne、DefaultSqlSession#getMapper 两个方法中都使用 configuration 来获取对应的信息。
  • 目前 selectOne 方法中只是把获取的信息进行打印,后续将引入 SQL 执行器进行结果查询并返回。

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* @author:zzc
* @date: 2022/4/10
*/
public class ApiTest {

private Logger logger = LoggerFactory.getLogger(ApiTest.class);

@Test
public void test_MapperProxyFactory() throws IOException {
//1、从SqlSessionFactory中获取SqlSession
Reader reader = Resources.getResourceAsReader("mybatis-config-datasource.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();

//2、获取映射器对象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);

//3、测试验证
String info = userDao.queryUserInfoById("10001");
logger.info("测试结果:{}", info);
}
}