MyBatis源码分析(一):搭建调试环境

目录

拉取源码

安装环境

调试测试

1、 mybatis-config.xml

2、AutoConstructorMapper.xml

3、AutoConstructorMapper

4、CreateDB.sql

5、POJO

5.1 AnnotatedSubject

5.2、PrimitiveSubject

5.3、BadSubject

5.4、ExtensiveSubject

6、AutoConstructorTest

6.1、setUp

测试

总结


 

拉取源码

github地址: GitHub - mybatis/mybatis-3: MyBatis SQL mapper framework for Java 

克隆速度较慢,耐心等待哈。

安装环境

都是高级的程序猿了,环境这快就不多说了奥。

  • maven
  • jdk
  • IntelliJ IDEA
  • GIT

调试测试

用 IDEA工具打开我们克隆的mybatis源码,由于mybatis框架年代也算是相对比较久远了,所以需要一定的加载时间。别着急哦。

MyBatis源码分析(一):搭建调试环境_第1张图片

其中有一个非常重要的类AutoConstructorTest,这个类里面有很多单元测试的方法,调试的时候只要在任意一个类上点击右键调试即刻。

class AutoConstructorTest {
  private static SqlSessionFactory sqlSessionFactory;

  @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
    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 badMultipleAnnotatedSubject() {
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
      final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
      final PersistenceException ex = assertThrows(PersistenceException.class, mapper::getBadAnnotatedSubjects);
      final ExecutorException cause = (ExecutorException) ex.getCause();
      assertEquals("@AutomapConstructor should be used in only one constructor.", cause.getMessage());
    }
  }

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

测试类所在的包结构如下:

MyBatis源码分析(一):搭建调试环境_第2张图片

配置文件所在的包结构如下:

MyBatis源码分析(一):搭建调试环境_第3张图片

首先从配置文件入手,依次向下分析

1、 mybatis-config.xml

MyBatis 配置文件。XML 如下:

为了更好的理解,每一个配置上边都写了备注哦







    

    
        
            
            
                
            
            
            
                
                
                
            
        
    
    
    
        
    


  • 标签:主要配置事务管理以及数据源。mybatis测试源码中使用了HSQLDB的内存模式作为单元测试数据库(就是数据等都是基于内存)。目的是减少外部依赖,如果应用进程结束,HSQLDB的数据也会丢失,因此这种模式适合做单元测试。
  • 标签:配置了扫描的xml文件

2、AutoConstructorMapper.xml

AutoConstructorMapper.xml配置文件。XML 如下:







  • 指定了命名空间:AutoConstructorMapper

3、AutoConstructorMapper

代码如下:

/*
 *    Copyright 2009-2022 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
 *
 *       https://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.
 */
package org.apache.ibatis.autoconstructor;

import java.util.List;

import org.apache.ibatis.annotations.Select;

public interface AutoConstructorMapper {
  @Select("SELECT * FROM subject WHERE id = #{id}")
  PrimitiveSubject getSubject(final int id);

  @Select("SELECT * FROM subject")
  List getSubjects();

  @Select("SELECT * FROM subject")
  List getAnnotatedSubjects();

  @Select("SELECT * FROM subject")
  List getBadAnnotatedSubjects();

  @Select("SELECT * FROM subject")
  List getBadSubjects();

  @Select("SELECT * FROM extensive_subject")
  List getExtensiveSubjects();
}

配置了数据源,写好了sql,那我们元数据在哪里呢?往下来

4、CreateDB.sql

该文件里存储的就是初始化数据库的数据,脚本如下:

--
--    Copyright 2009-2023 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
--
--       https://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;
/*创建student表*/
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
);

/*创建extensive_subject表*/
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类,你这咋就创建两张表啊?

别急别急

MyBatis源码分析(一):搭建调试环境_第4张图片

5、POJO

在AutoConstructorMapper中,我们确实可以看到五个POJO类,分别是PrimitiveSubject、AnnotatedSubject、BadAnnotatedSubject、BadSubject、ExtensiveSubject,但是,从 CreateDB.sql 中,实际只有两个表。这个是为什么呢?继续往下看。

剧透:有好几个POJO其实对应该很的是同一张表哦。

5.1 AnnotatedSubject

/*
 *    Copyright 2009-2022 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
 *
 *       https://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.
 */
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;
  }
}
  • AnnotatedSubject类对应的student表
  • @AutomapConstructor注解,大家应该比较陌生,我也没用过,查了些资料,这个注解表示表示 MyBatis 查询后,再创建 AnnotatedSubject 对象

5.2、PrimitiveSubject

/*
 *    Copyright 2009-2023 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
 *
 *       https://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.
 */
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;
  }
}
  • 对应的也是student表
  • 和 AnnotatedSubject 不同,在其构造方法上,weight 和 height 方法参数的类型是 int ,而不是 Integer 。所以,如果 subject 表中的记录,这两个字段为 NULL 时,会报错

5.3、BadSubject

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

5.4、ExtensiveSubject

/*
 *    Copyright 2009-2023 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
 *
 *       https://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.
 */
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 表。
  • 这是个复杂对象,基本涵盖了各种类型的数据。

6、AutoConstructorTest

单元测试类

6.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 SQL 文件,初始化数据到内存数据库。
    BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(),
        "org/apache/ibatis/autoconstructor/CreateDB.sql");
  }
  • 创建 SqlSessionFactory 对象,基于 mybatis-config.xml 配置文件。
  • 初始化数据到内存数据库,基于 CreateDB.sql SQL 文件。

测试

在任意单元测试方法上右键,都可以调试哦!

总结

Mybatis调试环境的搭建还是比较容易的,为了让大家对MyBatis的整体结构有一定的了解,下一篇将会讲解MyBatis的项目结构,敬请期待哦!

传送门:MyBatis 源码分析(二):项目结构-CSDN博客

 

 

 

你可能感兴趣的:(源码解读,mybatis)