目录
?
github地址:?GitHub - mybatis/mybatis-3: MyBatis SQL mapper framework for Java?
克隆速度较慢,耐心等待哈。
都是高级的程序猿了,环境这快就不多说了奥。
- maven
- jdk
- IntelliJ IDEA
- GIT
用 IDEA工具打开我们克隆的mybatis源码,由于mybatis框架年代也算是相对比较久远了,所以需要一定的加载时间。别着急哦。
其中有一个非常重要的类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 配置文件。XML 如下:
为了更好的理解,每一个配置上边都写了备注哦
<?xml version="1.0" encoding="UTF-8" ?>
<!--
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.
-->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://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>
AutoConstructorMapper.xml配置文件。XML 如下:
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.apache.ibatis.autoconstructor.AutoConstructorMapper">
</mapper>
代码如下:
/*
* 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<PrimitiveSubject> getSubjects();
@Select("SELECT * FROM subject")
List<AnnotatedSubject> getAnnotatedSubjects();
@Select("SELECT * FROM subject")
List<BadAnnotatedSubject> getBadAnnotatedSubjects();
@Select("SELECT * FROM subject")
List<BadSubject> getBadSubjects();
@Select("SELECT * FROM extensive_subject")
List<ExtensiveSubject> getExtensiveSubjects();
}
配置了数据源,写好了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类,你这咋就创建两张表啊?
别急别急
在AutoConstructorMapper中,我们确实可以看到五个POJO类,分别是PrimitiveSubject、AnnotatedSubject、BadAnnotatedSubject、BadSubject、ExtensiveSubject,但是,从?CreateDB.sql
?中,实际只有两个表。这个是为什么呢?继续往下看。
剧透:有好几个POJO其实对应该很的是同一张表哦。
/*
* 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;
}
}
/*
* 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;
}
}
weight
?和?height
?方法参数的类型是?int
?,而不是 Integer 。所以,如果?subject
?表中的记录,这两个字段为?NULL
?时,会报错/*
* 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
?表。height
?方法参数的类型是 Height ,而不是 Integer 。因为 MyBatis 无法识别 Height 类,所以会创建 BadSubject 对象报错。/*
* 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
?表。单元测试类
@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");
}
mybatis-config.xml
?配置文件。CreateDB.sql
?SQL 文件。在任意单元测试方法上右键,都可以调试哦!
Mybatis调试环境的搭建还是比较容易的,为了让大家对MyBatis的整体结构有一定的了解,下一篇将会讲解MyBatis的项目结构,敬请期待哦!
传送门:MyBatis 源码分析(二):项目结构-CSDN博客
?
?
?