mybatis的启动过程

537 阅读9分钟

准备工作

  • 建一个springboot的项目目录结构如下
在这里插入图片描述
在这里插入图片描述
  • 相应的代码展示
 1// UserDao.java
2public interface UserDao {
3    List<User> getUserList();
4}
5
6// User.java
7@Data
8public class User {
9    private String username;
10    private String password;
11}
12
13// MybatisApplication.java
14public class MybatisApplication {
15    public static void main(String[] args) throws Exception{
16        String resource = "mybatis.xml";
17        InputStream resourceAsStream = Resources.getResourceAsStream(resource);
18        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
19        SqlSession sqlSession = factory.openSession();
20        try{
21            UserDao userDao = sqlSession.getMapper(UserDao.class);
22            List<User> userList = userDao.getUserList();
23            System.out.println("**********"+JSON.toJSON(userList));
24        }finally {
25            sqlSession.close();
26        }
27    }
28}
29
30// UserMapper.xml
31<?xml version="1.0" encoding="UTF-8" ?>
32<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
33
34<mapper namespace="com.wyl.mybatis.dao.UserDao">
35    <select id="getUserList" resultType="user">
36        select * from user;
37    </select>
38</mapper>
39
40// db.properties
41driver=com.mysql.cj.jdbc.Driver
42url=jdbc:mysql://127.0.0.1:3306/umi?useUnicode=true&characterEncoding=utf8
43username=root
44password=mysqlpwd
45
46// mybatis.xml
47?xml version="1.0" encoding="UTF-8" ?>
48<!DOCTYPE configuration
49        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
50        "http://mybatis.org/dtd/mybatis-3-config.dtd">
51<configuration>
52    <properties resource="db.properties"> </properties>
53    <typeAliases>
54        <package name="com.wyl.mybatis.model"/>
55    </typeAliases>
56    <environments default="development">
57        <environment id="development">
58            <transactionManager type="JDBC"/>
59            <dataSource type="POOLED">
60                <property name="driver" value="${driver}"/>
61                <property name="url" value="${url}"/>
62                <property name="username" value="${username}"/>
63                <property name="password" value="${password}"/>
64            </dataSource>
65        </environment>
66    </environments>
67    <mappers>
68        <mapper resource="mapper/UserMapper.xml"/>
69    </mappers>
70</configuration>

mybatis到底如何正确的读取配置文件?

1. 把mybatis.xml文件变成流

1String resource = "mybatis.xml";
2        InputStream resourceAsStream = Resources.getResourceAsStream(resource);

2. 通过SqlSessionFactoryBuilder创建SqlSessionFactory

1SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
2.1 解析SqlSessionFactoryBuilder
 1**
2 * Builds {@link SqlSession} instances.
3 *
4 * @author Clinton Begin
5 */
6public class SqlSessionFactoryBuilder {
7
8  public SqlSessionFactory build(Reader reader) {
9    return build(reader, nullnull);
10  }
11
12  public SqlSessionFactory build(Reader reader, String environment) {
13    return build(reader, environment, null);
14  }
15
16  public SqlSessionFactory build(Reader reader, Properties properties) {
17    return build(reader, null, properties);
18  }
19...
20public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
21    try {
22      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
23      return build(parser.parse());
24    } catch (Exception e) {
25      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
26    } finally {
27      ErrorContext.instance().reset();
28      try {
29        inputStream.close();
30      } catch (IOException e) {
31        // Intentionally ignore. Prefer previous error.
32      }
33    }
34  }

通过查看源代码可以看到SqlSessionFactoryBuilder就是用来创建SqlSessionFactory的,并且通过构建XMLConfigBuilder对象解析配置文件。

2.2 查看XMLConfigBuilder对象的parse()
 1public Configuration parse() {
2    if (parsed) {
3      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
4    }
5    parsed = true;
6    parseConfiguration(parser.evalNode("/configuration"));
7    return configuration;
8  }
9​```
10在这里我们已经可以看到通过解析器解析xml文件的**configuration**节点了,再往下看parseConfiguration的方法
11​```java
12private void parseConfiguration(XNode root) 
{
13    try {
14      //issue #117 read properties first
15      propertiesElement(root.evalNode("properties"));
16      Properties settings = settingsAsProperties(root.evalNode("settings"));
17      loadCustomVfs(settings);
18      typeAliasesElement(root.evalNode("typeAliases"));
19      pluginElement(root.evalNode("plugins"));
20      objectFactoryElement(root.evalNode("objectFactory"));
21      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
22      reflectorFactoryElement(root.evalNode("reflectorFactory"));
23      settingsElement(settings);
24      // read it after objectFactory and objectWrapperFactory issue #631
25      environmentsElement(root.evalNode("environments"));
26      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
27      typeHandlerElement(root.evalNode("typeHandlers"));
28      mapperElement(root.evalNode("mappers"));
29    } catch (Exception e) {
30      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
31    }
32  }

很明显我们可以看到这里就是通过调用不同的方法解析不同的标签,到这里已经解析成了mybatis。xml了,下一步就是看在哪里解析UserMapper.xml
进入上面的mapperElement方法

 1private void mapperElement(XNode parent) throws Exception {
2    if (parent != null) {
3      for (XNode child : parent.getChildren()) {
4        if ("package".equals(child.getName())) {
5          String mapperPackage = child.getStringAttribute("name");
6          configuration.addMappers(mapperPackage);
7        } else {
8          String resource = child.getStringAttribute("resource");
9          String url = child.getStringAttribute("url");
10          String mapperClass = child.getStringAttribute("class");
11          if (resource != null && url == null && mapperClass == null) {
12            ErrorContext.instance().resource(resource);
13            InputStream inputStream = Resources.getResourceAsStream(resource);
14            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
15            mapperParser.parse();
16          } else if (resource == null && url != null && mapperClass == null) {
17            ErrorContext.instance().resource(url);
18            InputStream inputStream = Resources.getUrlAsStream(url);
19            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
20            mapperParser.parse();
21          } else if (resource == null && url == null && mapperClass != null) {
22            Class<?> mapperInterface = Resources.classForName(mapperClass);
23            configuration.addMapper(mapperInterface);
24          } else {
25            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
26          }
27        }
28      }
29    }
30  }

从这里我们可以看到mapper是根据不同属性进行适配的,我们用的是第一种通过构建XMLMapperBuilder去映射mapper.xml文件,这时点击查看mapperParser.parse();

 1public void parse() {
2    if (!configuration.isResourceLoaded(resource)) {
3      configurationElement(parser.evalNode("/mapper"));
4      configuration.addLoadedResource(resource);
5      bindMapperForNamespace();
6    }
7
8    parsePendingResultMaps();
9    parsePendingCacheRefs();
10    parsePendingStatements();
11  }

在这里我们可以看到是解析mapper的地,继续点击configurationElement方法,
·

 1private void configurationElement(XNode context) {
2    try {
3      String namespace = context.getStringAttribute("namespace");
4      if (namespace == null || namespace.equals("")) {
5        throw new BuilderException("Mapper's namespace cannot be empty");
6      }
7      builderAssistant.setCurrentNamespace(namespace);
8      cacheRefElement(context.evalNode("cache-ref"));
9      cacheElement(context.evalNode("cache"));
10      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
11      resultMapElements(context.evalNodes("/mapper/resultMap"));
12      sqlElement(context.evalNodes("/mapper/sql"));
13      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
14    } catch (Exception e) {
15      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
16    }
17  }

这里就是对UserMapper.xml的各部分的解析,包括sql的增删改查。

全部映射成功后返回一个DefaultSqlSessionFactory对象,

在这里插入图片描述
在这里插入图片描述

这图片是SqlSessionFactory的继承结构图

最后

后面就是通过SqlSessionFactory创建一个sql会话,开启事务,进行一系列的数据库操作,操作完毕后,提交事务,关闭会话。