1 Star 0 Fork 165

shk / Java-Review

forked from icanci / Java-Review 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
SSM-Mybatis3-源码解读-第1篇-开篇.md 12.00 KB
一键复制 编辑 原始数据 按行查看 历史
icanci 提交于 2020-09-07 10:15 . :fire:更新readme.md 相对路径问题

SSM - Mybatis3 - 源码解读 - 第1篇 - 开篇

搭建环境

  • 后面的是我自己电脑版本,其他的版本不影响

  • Maven (3.5.4)

  • Git (2.22.0)

  • JDK (9.0.1)

  • IDEA (2020.1)

拉取源码

调试测试

  • Mybatis的调试十分方便,只需要打开 org.apache.ibatis.autoconstructor.AutoConstructorTest 单元测试即可。
  • 下图是测试 autoconstructor 下的文件

1598928293123

  • mybatis-config.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>
            <!-- 配置数据源 -->
            <!-- UNPOOLED 指的是每次都创建新的连接 POOLED 指的是使用同一个 -->
            <dataSource type="UNPOOLED">
                <!-- 数据库驱动 -->
                <property name="driver" value="org.hsqldb.jdbcDriver"/>
                <!-- 数据库URL地址 -->
                <property name="url" value="jdbc:hsqldb:mem:automapping"/>
                <!-- 数据库用户名 -->
                <property name="username" value="sa"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 接口映射文件 -->
    <mappers>
        <!-- 具体映射文件路径名 -->
        <mapper resource="org/apache/ibatis/autoconstructor/AutoConstructorMapper.xml"/>
    </mappers>

</configuration>
  • <environments/> 标签中,配置了事务管理和数据源。考虑到减少外部依赖,所以使用了 HSQLDB

  • <mappers> 标签中,配置了需要扫描的 Mapper 文件。目前,仅仅扫描 AutoConstructorMapper.xml 文件。

  • 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">

<!-- 对应 org.apache.ibatis.autoconstructor.AutoConstructorMapper 接口 -->
<mapper namespace="org.apache.ibatis.autoconstructor.AutoConstructorMapper">
</mapper>
  • AutoConstructorMapper
// 此处是使用注解的方式,来编写SQL
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> getExtensiveSubjects();
}
  • CreateDB.sql
--
--    Copyright 2009-2018 the original author or authors.
--
--    Licensed under the Apache License, Version 2.0 (the "License");
--    you may not use this file except in compliance with the License.
--    You may obtain a copy of the License at
--
--       http://www.apache.org/licenses/LICENSE-2.0
--
--    Unless required by applicable law or agreed to in writing, software
--    distributed under the License is distributed on an "AS IS" BASIS,
--    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--    See the License for the specific language governing permissions and
--    limitations under the License.
--

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 表,并且初始化三条数据

  • POJO

  • AnnotatedSubject

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注解,表示Mybatis查询之后,再创建 AnnotatedSubject对象,使用该构造方法,实际用处不多
  • PrimitiveSubject
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;
    }
}
  • 对应的也是 subject

  • 和AnnotatedSubject不同,在其构造方法上,weightheight 方法参数的类型是int,而不是Integer,那么如果subject表中的记录,这两个字段为 NULL时,会创建PrimitiveSubject对象报错

  • BadSubject

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 方法参数的类型是 Height ,而不是 Integer 。因为 MyBatis 无法识别 Height 类,所以会创建 BadSubject 对象报错。

  • ExtensiveSubject

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 表。

  • 这是个复杂对象,基本涵盖了各种类型的数据。

  • AutoConstructorTest 单元测试类

class AutoConstructorTest {
    private static SqlSessionFactory sqlSessionFactory;

    // 创建SqlSessionFactory对象,基于mybatis-config.xml 配置文件
    @BeforeAll
    static void setUp() throws Exception {
        // create a 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");
    }

    @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.getExtensiveSubjects());
        }
    }

    private void verifySubjects(final List<?> subjects) {
        assertNotNull(subjects);
        Assertions.assertThat(subjects.size()).isEqualTo(3);
    }
}
  • 创建 SqlSessionFactory 对象,基于 mybatis-config.xml 配置文件。
  • 初始化数据到内存数据库,基于 CreateDB.sql SQL 文件。

测试方法

  • 右键任一单元测试方法,开始调试
1
https://gitee.com/shokaku/Java-Review.git
git@gitee.com:shokaku/Java-Review.git
shokaku
Java-Review
Java-Review
master

搜索帮助