直接從官方倉庫 Fork 到本身的倉庫,Fork 出來以後,在學習源碼的過程當中咱們能夠方便的寫一些註釋,自由的提交。java
這裏我 Fork 出來的 Mybatis 版本是 3.5.2-SNAPSHOT 。git
調試 MyBatis 源碼很是簡單,只須要打開 org.apache.ibatis.autoconstructor.AutoConstructorTest 單元測試類,運行任意一個方法便可。該類所在的包總體結構以下:github
下面對該包中的結構作下介紹。sql
Mybatis 的配置文件,內容以下:數據庫
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <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>
Mapper 配置文件,代碼以下:apache
<!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>
Mapper 文件,對應上述配置文件,代碼以下:api
package org.apache.ibatis.autoconstructor; import org.apache.ibatis.annotations.Select; import java.util.List; public interface AutoConstructorMapper { @Select("SELECT * FROM subject WHERE id = #{id}") PrimitiveSubject getSubject(final int id); @Select("SELECT * FROM subject") List<PrimitiveSubject> getSubjects(); @Select("SELECT * FROM subject") List<AnnotatedSubject> getAnnotatedSubjects(); @Select("SELECT * FROM subject") List<BadSubject> getBadSubjects(); @Select("SELECT * FROM extensive_subject") List<ExtensiveSubject> getExtensiveSubject(); }
這裏其實並無用到上述的 AutoConstructorMapper.xml Mapper 配置文件,而是直接使用註解 SQL 的方式。session
用於單元測試裏,初始化數據庫的數據。以下:mybatis
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);
在 AutoConstructorMapper 中,咱們能夠看到有四個 POJO 類。可是,從 CreateDB.sql 中,實際只有兩個表。這個是爲何呢?繼續往下看噢。app
對應 Subject 表,內容以下:
ackage 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; } }
@AutomapConstructor 註解,表示在 MyBatis 查詢後使用該構造方法來建立 AnnotatedSubject 對象。
對應的也是 Subject 表。
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; } }
和 AnnotatedSubject 不一樣,在其構造方法上, weight 和 height 方法參數的類型是 int ,而不是 Integer 。那麼,若是 Subject 表中的記錄,這兩個字段爲 NULL 時,建立 PrimitiveSubject 對象會報錯。
依舊對應 Subject 表。
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 { } }
和 AnnotatedSubject 不一樣,在其構造方法上, height 參數的類型是 Height ,而不是 Integer 。由於 MyBatis 沒法識別 Height 類,因此會建立 BadSubject 對象報錯。
對應 extensive_subject 表,內容以下:
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; } }
這是個複雜對象,基本涵蓋了各類類型的數據。
單元測試類,內容以下:
package org.apache.ibatis.autoconstructor; import org.apache.ibatis.BaseDataTest; import org.apache.ibatis.exceptions.PersistenceException; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.io.Reader; import java.util.List; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; class AutoConstructorTest { private static SqlSessionFactory sqlSessionFactory; @BeforeAll static void setUp() throws Exception { // 基於 mybatis-config.xml 配置文件建立 SqlSessionFactory 對象 try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml")) { sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); } // 基於 CreateDB.sql 初始化數據到內存數據庫 BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), "org/apache/ibatis/autoconstructor/CreateDB.sql"); } @Test void fullyPopulatedSubject() { try (SqlSession sqlSession = sqlSessionFactory.openSession()) { final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class); final Object subject = mapper.getSubject(1); assertNotNull(subject); } } @Test void primitiveSubjects() { try (SqlSession sqlSession = sqlSessionFactory.openSession()) { final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class); assertThrows(PersistenceException.class, mapper::getSubjects); } } @Test void annotatedSubject() { try (SqlSession sqlSession = sqlSessionFactory.openSession()) { final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class); verifySubjects(mapper.getAnnotatedSubjects()); } } @Test void badSubject() { try (SqlSession sqlSession = sqlSessionFactory.openSession()) { final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class); assertThrows(PersistenceException.class, mapper::getBadSubjects); } } @Test void extensiveSubject() { try (SqlSession sqlSession = sqlSessionFactory.openSession()) { final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class); verifySubjects(mapper.getExtensiveSubject()); } } private void verifySubjects(final List<?> subjects) { assertNotNull(subjects); Assertions.assertThat(subjects.size()).isEqualTo(3); } }
執行任意一個測試方法,就能夠開始調試了~~~