准备工作
- 建一个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, null, null);
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会话,开启事务,进行一系列的数据库操作,操作完毕后,提交事务,关闭会话。