0%

mybatis的启动过程

准备工作

  • 建一个springboot的项目目录结构如下

在这里插入图片描述

  • 相应的代码展示
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// UserDao.java
public interface UserDao {
List<User> getUserList();
}

// User.java
@Data
public class User {
private String username;
private String password;
}

// MybatisApplication.java
public class MybatisApplication {
public static void main(String[] args) throws Exception{
String resource = "mybatis.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = factory.openSession();
try{
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.getUserList();
System.out.println("**********"+JSON.toJSON(userList));
}finally {
sqlSession.close();
}
}
}

// UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.wyl.mybatis.dao.UserDao">
<select id="getUserList" resultType="user">
select * from user;
</select>
</mapper>

// db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/umi?useUnicode=true&characterEncoding=utf8
username=root
password=mysqlpwd

// mybatis.xml
?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties"> </properties>
<typeAliases>
<package name="com.wyl.mybatis.model"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>

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

1. 把mybatis.xml文件变成流

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

2. 通过SqlSessionFactoryBuilder创建SqlSessionFactory

1
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
2.1 解析SqlSessionFactoryBuilder
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
**
* Builds {@link SqlSession} instances.
*
* @author Clinton Begin
*/
public class SqlSessionFactoryBuilder {

public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}

public SqlSessionFactory build(Reader reader, String environment) {
return build(reader, environment, null);
}

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

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

2.2 查看XMLConfigBuilder对象的parse()
1
2
3
4
5
6
7
8
9
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}

在这里我们已经可以看到通过解析器解析xml文件的configuration节点了,再往下看parseConfiguration的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}

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

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
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}

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

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

parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}

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

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

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

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

在这里插入图片描述

这图片是SqlSessionFactory的继承结构图yo

最后

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

-------------本文结束感谢你的阅读---------