MyBatis源码解析-搭建调试环境
MyBatis源码解析-搭建调试环境
1. 相关工具
- Maven
- Git
- JDK1.8
- IntelliJ IDEA
2. 源码拉取
一般来说,我们直接从https://github.com/mybatis/mybatis-3 Fork到自己的仓库中,为什么要Fork呢?我们在之后的源码分析中,我们可能会加一些注释,英文翻译一波,方便自己理解,也方便自己自由提交。
- 版本:3.5.4-SNAPSHOT
3. 调试
啥也不多说,咱们直接看单元测试结构,除了autoconstructor
这个包下是整体调试以外,其他的都是各个模块的单元测试。那咱们直接就从org.apache.ibatis.autoconstructor
这个包下开始调试。
从这个包下主要包含下面几部分,如图所示:
-
MyBatis的配置文件:mybatis-config.xml
-
SQL文件: CreateDB.sql
-
映射及配置:AutoConstructorMapper.java AutoConstructorMapper.xml
-
实体类:AnnotatedSubject.java BadSubject.java ExtensiveSubject.java PrimitiveSubject.java
-
测试类:AutoConstructorTest.java
3.1 MyBatis的配置文件
见mybatis-config.xml
作为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>
<!-- autoMappingBehavior should be set in each test case -->
<!-- 环境配置 -->
<environments default="development">
<environment id="development">
<!-- 事务管理配置 -->
<transactionManager type="JDBC">
<property name="" value=""/>
</transactionManager>
<!-- 数据源配置 -->
<dataSource type="UNPOOLED">
<property name="driver" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:mem:automapping"/>
<property name="username" value="sa"/>
</dataSource>
</environment>
</environments>
<!-- 扫描的Mapper文件 -->
<mappers>
<mapper resource="org/apache/ibatis/autoconstructor/AutoConstructorMapper.xml"/>
</mappers>
</configuration>
- 环境配置:在
</environment>
标签中,配置了事务管理和数据源,数据库选择方面使用了HSQLDB,减少外部依赖。 - 扫描映射:在
<mappers>
配置了要扫描的Mapper文件,这里只配置了AutoConstructorMapper.xml。
3.2 SQL文件
CreateDB.sql
在单元测试中用作初始化数据库的表及数据。如下:
DROP TABLE subject
IF EXISTS;
DROP TABLE extensive_subject
IF EXISTS;
CREATE TABLE subject (
id INT NOT NULL,
name VARCHAR(20),
age INT NOT NULL,
height INT,
weight INT,
active BIT,
dt TIMESTAMP
);
CREATE TABLE extensive_subject (
aByte TINYINT,
aShort SMALLINT,
aChar CHAR,
anInt INT,
aLong BIGINT,
aFloat FLOAT,
aDouble DOUBLE,
aBoolean BIT,
aString VARCHAR(255),
anEnum VARCHAR(50),
aClob LONGVARCHAR,
aBlob LONGVARBINARY,
aTimestamp TIMESTAMP
);
INSERT INTO subject VALUES
(1, 'a', 10, 100, 45, 1, CURRENT_TIMESTAMP),
(2, 'b', 10, NULL, 45, 1, CURRENT_TIMESTAMP),
(2, 'c', 10, NULL, NULL, 0, CURRENT_TIMESTAMP);
INSERT INTO extensive_subject
VALUES
(1, 1, 'a', 1, 1, 1, 1.0, 1, 'a', 'AVALUE', 'ACLOB', 'aaaaaabbbbbb', CURRENT_TIMESTAMP),
(2, 2, 'b', 2, 2, 2, 2.0, 2, 'b', 'BVALUE', 'BCLOB', '010101010101', CURRENT_TIMESTAMP),
(3, 3, 'c', 3, 3, 3, 3.0, 3, 'c', 'CVALUE', 'CCLOB', '777d010078da', CURRENT_TIMESTAMP);
包含两张表:subject
表和extensive_subject
表。
3.3 映射及配置
包含AutoConstructorMapper.java AutoConstructorMapper.xml两个文件
3.3.1 AutoConstructorMapper.java
package org.apache.ibatis.autoconstructor;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface AutoConstructorMapper {
// PrimitiveSubject
@Select("SELECT * FROM subject WHERE id = #{id}")
PrimitiveSubject getSubject(final int id);
// PrimitiveSubject
@Select("SELECT * FROM subject")
List<PrimitiveSubject> getSubjects();
// AnnotatedSubject
@Select("SELECT * FROM subject")
List<AnnotatedSubject> getAnnotatedSubjects();
// BadSubject
@Select("SELECT * FROM subject")
List<BadSubject> getBadSubjects();
// ExtensiveSubject
@Select("SELECT * FROM extensive_subject")
List<ExtensiveSubject> getExtensiveSubjects();
}
- 使用注解方式配置mapper,在开发中基于xml配置的还是用的多一些,比较灵活。推荐xml配置。
3.3.2 AutoConstructorMapper.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="org.apache.ibatis.autoconstructor.AutoConstructorMapper">
</mapper>
- 映射接口:
org.apache.ibatis.autoconstructor.AutoConstructorMapper
3.4 实体类
3.4.1 AnnotatedSubject.java
AnnotatedSubject类中除了字段还有默认的构造函数以外,还提供一个以基本数据包装类作为构造参数的构造函数。注:@AutomapConstructor 这个注解的作用是让框架自动使用这个构造器。【这里重点说一下,我们在使用MyBatis的时候,实体类的字段类型最好使用包装类,最好不要设置默认值,否则xml里处理会有些麻烦。】
package org.apache.ibatis.autoconstructor;
import org.apache.ibatis.annotations.AutomapConstructor;
public class AnnotatedSubject {
private final int id;
private final String name;
private final int age;
private final int height;
private final int weight;
public AnnotatedSubject(final int id, final String name, final int age, final int height, final int weight) {
this.id = id;
this.name = name;
this.age = age;
this.height = height;
this.weight = weight;
}
//自动映射构造器 基本类型包装类作为构造参数
@AutomapConstructor
public AnnotatedSubject(final int id, final String name, final int age, final Integer height, final Integer weight) {
this.id = id;
this.name = name;
this.age = age;
this.height = height == null ? 0 : height;
this.weight = weight == null ? 0 : weight;
}
}
- 对应
subject
表 - 使用自动映射构造器注解
@AutomapConstructor
,开发过程中我很少用到。
3.4.1.1 PrimitiveSubject.java
package org.apache.ibatis.autoconstructor;
import java.util.Date;
public class PrimitiveSubject {
private final int id;
private final String name;
private final int age;
private final int height;
private final int weight;
private final boolean active;
private final Date dt;
public PrimitiveSubject(final int id, final String name, final int age, final int height, final int weight, final boolean active, final Date dt) {
this.id = id;
this.name = name;
this.age = age;
this.height = height;
this.weight = weight;
this.active = active;
this.dt = dt;
}
}
3.4.1.2 BadSubject.java
package org.apache.ibatis.autoconstructor;
public class BadSubject {
private final int id;
private final String name;
private final int age;
private final Height height;
private final Double weight;
public BadSubject(final int id, final String name, final int age, final Height height, final Double weight) {
this.id = id;
this.name = name;
this.age = age;
this.height = height;
this.weight = weight == null ? 0 : weight;
}
private class Height {
}
}
-
对应
subject
表 -
和上面AnnotatedSubject类的构造函数不同,在其构造参数中使用类型 Height,而不是Integer。因为MyBatis找不到Height映射的配置,所以在构造BadSubject对象的时候会报错。
3.4.2 ExtensiveSubject.java
package org.apache.ibatis.autoconstructor; public class ExtensiveSubject { private final byte aByte; private final short aShort; private final char aChar; private final int anInt; private final long aLong; private final float aFloat; private final double aDouble; private final boolean aBoolean; private final String aString; // enum types private final TestEnum anEnum; // array types // string to lob types: private final String aClob; private final String aBlob; public ExtensiveSubject(final byte aByte, final short aShort, final char aChar, final int anInt, final long aLong, final float aFloat, final double aDouble, final boolean aBoolean, final String aString, final TestEnum anEnum, final String aClob, final String aBlob) { this.aByte = aByte; this.aShort = aShort; this.aChar = aChar; this.anInt = anInt; this.aLong = aLong; this.aFloat = aFloat; this.aDouble = aDouble; this.aBoolean = aBoolean; this.aString = aString; this.anEnum = anEnum; this.aClob = aClob; this.aBlob = aBlob; } public enum TestEnum { AVALUE, BVALUE, CVALUE; } }
-
对应
extensive_subject
表 -
相对于AnnotatedSubject这个稍微复杂一些,对象属性几乎包含了所有类型的数据。
3.5 测试类
对应AutoConstructorTest.java文件,单元测试类。
3.5.1 setUp
@BeforeAll
static void setUp() throws Exception {
// create a SqlSessionFactory
//读取 mybatis-config.xml 配置创建 SqlSessionFactory 对象
try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml")) {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
// populate in-memory database
// 读取CreateDB.sql文件初始化数据到内存数据库中
BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(),
"org/apache/ibatis/autoconstructor/CreateDB.sql");
}
- @BeforeAll 这个注解是JUnit5提供的一个注解,作用:注释的静态方法会在执行所有测试用例之前调用,其他详细介绍可以参考 JUnit4与JUnit5注解对比
- 读取 mybatis-config.xml 配置创建 SqlSessionFactory 对象
- 读取CreateDB.sql文件初始化数据到内存数据库中
3.5.2 Run Test
右键任意一个测试方法debug搞起来。IDEA调试技巧
本文由 Janker 创作,采用 CC BY 3.0 CN协议 进行许可。 可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。