乐趣区

Mybatis是如何工作的(一)

本文目标:

使用纯 Mybatis 框架获取数据;
理清 Mybatis 的工作过程。

创建项目并运行
首先创建 maven 项目,过程不再赘述。依赖如下:
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
</dependencies>
下面准备一张表:
CREATE TABLE `clips` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT ‘ 主键 ’,
`content` varchar(256) NOT NULL DEFAULT ” COMMENT ‘ 内容 ’,
`deleted` tinyint(1) NOT NULL DEFAULT ‘0’ COMMENT ‘ 删除标识:0 正常,1 删除 ’,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ‘ 创建时间 ’,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT ‘ 更新时间 ’,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=’clips’;
添加一条数据:

对应的实体类:
public class ClipsEntity {
private Integer id;
private String content;
private Integer deleted;
private LocalDateTime createTime;
private LocalDateTime updateTime;

// 省略 getter 和 setter
}
DAO:
public interface ClipsDAO {
ClipsEntity selectById(@Param(“id”) Integer id);
}
mapper 文件:
<?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.chunrun.dao.ClipsDAO”>

<select id=”selectById” resultType=”com.chunrun.entity.ClipsEntity”>
select * from clips where id = #{id}
</select>
</mapper>
Mybatis 配置文件:
<?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>
<settings>
<!– 使用 jdbc 的 getGeneratekeys 获取自增主键值 –>
<setting name=”useGeneratedKeys” value=”true”/>
<!– 使用列别名替换别名 默认 true–>
<setting name=”useColumnLabel” value=”true”/>
<!– 开启驼峰命名转换 Table:create_time 到 Entity(createTime)–>
<setting name=”mapUnderscoreToCamelCase” value=”true”/>
<!– 打印查询语句 –>
<setting name=”logImpl” value=”org.apache.ibatis.logging.stdout.StdOutImpl” />
</settings>
<typeAliases>
<typeAlias alias=”ClipsEntity” type=”com.chunrun.entity.ClipsEntity”/>
</typeAliases>
<environments default=”development”>
<environment id=”development”>
<transactionManager type=”JDBC”/>
<dataSource type=”POOLED”>
<property name=”driver” value=”com.mysql.jdbc.Driver”/>
<property name=”url” value=”jdbc:mysql://localhost:3306/bizu”/>
<property name=”username” value=”chunrun”/>
<property name=”password” value=”chunrun1s”/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource=”mapper/ClipsDAO.xml”/>
</mappers>
</configuration>
下面写个测试:
public class Main {

public static void main(String[] args) {
String resource = “mybatis-config.xml”;
try {
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
ClipsDAO clipsDAO = session.getMapper(ClipsDAO.class);
ClipsEntity clipsEntity = clipsDAO.selectById(1);
System.out.println(clipsEntity);
} catch (Exception e) {
System.out.println(e);
}
}
}
运行结果:

运行成功。那么,在这个过程中,程序具体做了什么事呢?一步一步来看。首先,我们用配置文件生成了一个 InputStream;然后用 InputStream 生成了生成 SqlSessionFactory;然后获取 Session;获取对应的 mapper,执行 SQL 获取结果。Mybatis 做的事情主要有三步:

从配置文件中生成 SqlSessionFactory;
从 SqlSessionFactory 中获取 session;
获取对应的 mapper,执行 SQL。

下面逐步看源码。
加载 mybatis 配置,生成 SqlSessionFactory
// 首先调用的是这个方法:
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
// 然后是这个:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 根据参数获取一个 XMLConfigBuilder,这部分是重点
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.
}
}
}

// 返回的 build 方法如下,可以看出实现是 DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
注释部分已经很清楚了,下面重点看下 XMLConfigBuilder,Mybatis 通过这个类来解析 mybatis 对应的配置。
// 解析 configuration 节点下面的子节点,并返回最终的配置。
public Configuration parse() {
if (parsed) {
throw new BuilderException(“Each XMLConfigBuilder can only be used once.”);
}
parsed = true;
parseConfiguration(parser.evalNode(“/configuration”));
return configuration;
}

private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
// 加载 properties 节点下的属性,
propertiesElement(root.evalNode(“properties”));
// 加载 settings 节点下的属性
Properties settings = settingsAsProperties(root.evalNode(“settings”));
loadCustomVfs(settings);
// 加载别名配置
typeAliasesElement(root.evalNode(“typeAliases”));
// 加载插件配置
pluginElement(root.evalNode(“plugins”));
// 加载 objectFactory 配置
objectFactoryElement(root.evalNode(“objectFactory”));
// 加载 objectWrapperFactory 配置
objectWrapperFactoryElement(root.evalNode(“objectWrapperFactory”));
// 加载 reflectorFactory 配置
reflectorFactoryElement(root.evalNode(“reflectorFactory”));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// 加载 environment 配置,这里会配置事务管理器
environmentsElement(root.evalNode(“environments”));
// 加载 databaseIdProvider 配置
databaseIdProviderElement(root.evalNode(“databaseIdProvider”));
// 加载 typeHandler 是配置,自定义的 typeHandler 会在这注册
typeHandlerElement(root.evalNode(“typeHandlers”));
// 加载 mapper 配置
mapperElement(root.evalNode(“mappers”));
} catch (Exception e) {
throw new BuilderException(“Error parsing SQL Mapper Configuration. Cause: ” + e, e);
}
}

至此,mybatis 配置加载完成。
小结
本文主要介绍了如何使用纯 Mybatis 操作数据库,然后介绍了 Mybatis 加载配置的过程。内容相对粗浅,深入分析在下文。

退出移动版