MyBatis源碼解析 搭建調試環境 1. 相關工具 Maven Git JDK1.8 IntelliJ IDEA 2. 源碼拉取 一般來說,我們直接從https://github.com/mybatis/mybatis 3 Fork到自己的倉庫中,為什麼要Fork呢?我們在之後的源碼分析中,我們可 ...
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協議 進行許可。 可自由轉載、引用,但需署名作者且註明文章出處。如轉載至微信公眾號,請在文末添加作者公眾號二維碼。