本文目标:
- 使用纯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加载配置的过程。内容相对粗浅,深入分析在下文。