原创

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调试技巧

正文到此结束
该篇文章的评论功能已被站长关闭