MyBatis入门及全局配置

初识Mybatis

ORM概览

在了解ORM之前我们先了解什么是持久化,因为所有的ORM框架所做的事情无非就是将对象映射成数据库中的数据,以及将数据库中的对象映射成数据。

什么是持久化

所谓持久化就是把数据(内存中的对象)保存到可永久存储的存储设备中(例如:硬盘),持久化的主要应用是将内存中的数据存储在关系型数据库中。当然也可以保存在磁盘文件中,XML数据文件,JSON文件中等的。

什么是ORM

ORM(Object-Relational Mapping),对象关系映射,它的作用是在关系型数据库和对象之间做一个映射,这样,我们在具体的操作数据库时,就不需要和复杂的SQL语句打交道,像平时操作对象操作数据即可。

在目前的企业应用系统设计中,MVC,即 Model(模型)- View(视图)- Control(控制)为主要的系统架构模式。MVC 中的 Model 包含了复杂的业务逻辑和数据逻辑,以及数据存取机制(如 JDBC的连接、SQL生成和Statement创建、还有ResultSet结果集的读取等)等。将这些复杂的业务逻辑和数据逻辑分离,以将系统的紧耦合关系转化为松耦合关系(即解耦合),是降低系统耦合度迫切要做的,也是持久化要做的工作。MVC 模式实现了架构上将表现层(即View)和数据处理层(即Model)分离的解耦合,而持久化的设计则实现了数据处理层内部的业务逻辑和数据逻辑分离的解耦合。 而 ORM 作为持久化设计中的最重要也最复杂的技术,也是目前业界热点技术。

简单来说,按通常的系统设计,使用 JDBC 操作数据库,业务处理逻辑和数据存取逻辑是混杂在一起的。

一般基本都是如下几个步骤:

  1. 建立数据库连接,获得 Connection 对象。
  2. 根据用户的输入组装查询 SQL 语句。
  3. 根据 SQL 语句建立 Statement 对象 或者 PreparedStatement 对象。
  4. 用 Connection 对象执行 SQL语句,获得结果集 ResultSet 对象。
  5. 然后一条一条读取结果集 ResultSet 对象中的数据。
  6. 根据读取到的数据,按特定的业务逻辑进行计算。
  7. 根据计算得到的结果再组装更新 SQL 语句。
  8. 再使用 Connection 对象执行更新 SQL 语句,以更新数据库中的数据。
  9. 最后依次关闭各个 Statement 对象和 Connection 对象。

由上可看出代码逻辑非常复杂,这还不包括某条语句执行失败的处理逻辑。其中的业务处理逻辑和数据存取逻辑完全混杂在一块。而一个完整的系统要包含成 千上万个这样重复的而又混杂的处理过程,假如要对其中某些业务逻辑或者一些相关联的业务流程做修改,要改动的代码量将不可想象。另一方面,假如要换数据库产品或者运行环境也可能是个不可能完成的任务。而用户的运行环境和要求却千差万别,我们不可能为每一个用户每一种运行环境设计一套一样的系统。

所以就要将一样的处理代码即业务逻辑和可能不一样的处理即数据存取逻辑分离开来,另一方面,关系型数据库中的数据基本都是以一行行的数据进行存取的,而程序 运行却是一个个对象进行处理,而目前大部分数据库驱动技术(如ADO.NET、JDBC、ODBC等等)均是以行集的结果集一条条进行处理的。所以为解决 这一困难,就出现 ORM 这一个对象和数据之间映射技术。常用的ORM框架有Hibernate、Mybatis、Mybatis-plus等,接下来我们以Mybatis为例深入学习。

Mybatis概览

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

下面我们先了解一下Mybatis架构:

  • mybatis-config.xml: Mybatis全局配置文件,用于配置数据库、数据库连接池、POJO别名、映射文件等信息
  • mapper.xml:该文件中用于编写处理数据的SQL语句。并且通过此文件可以将SQL语句与Java代码完全分离
  • SqlSessionFactory:用于创建Java和数据库会话的Session对象
  • Session:与数据库建立会话对象
  • Executor:真正执行Sql语句的对象
  • Statement:mapper文件对应的Statement,因为mapper文件是相互隔离的,可以理解为一个mapper文件就对应一个Statement对象。

Mybatis入门

在本小节内,将使用Mybatis框架搭建开发环境,并使用Mybatis进行单表的增删改查,其中涉及到的一些概念会在后续章节中陆续讲解。

新建Mybaits项目

在前面的课程中,已经学习了Maven的基础知识,使用Maven可以更好的管理和构建项目,因此使用Maven构建项目。项目结构如下:

首先准数据库:

1
2
3
4
5
6
7
8
9
10
11
12
CREATE DATABASE MYBATIS_DEMO;
USE MYBATIS_DEMO;
CREATE TABLE STUDENT(
STUDENT_ID INT PRIMARY KEY AUTO_INCREMENT,
STUDENT_NAME VARCHAR(20),
STUDENT_AGE INT,
STUDENT_GENDER CHAR(2)
);
INSERT INTO STUDENT (STUDENT_NAME, STUDENT_AGE, STUDENT_GENDER) VALUES ("张无忌",18,"男");
INSERT INTO STUDENT (STUDENT_NAME, STUDENT_AGE, STUDENT_GENDER) VALUES ("张翠山",43,"男");
INSERT INTO STUDENT (STUDENT_NAME, STUDENT_AGE, STUDENT_GENDER) VALUES ("殷素素",40,"男");
INSERT INTO STUDENT (STUDENT_NAME, STUDENT_AGE, STUDENT_GENDER) VALUES ("谢逊",50,"男");

第一步:在pom.xml中导入依赖

1
2
3
4
5
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>

第二步:在resource目录下新建mybatis-config.xml全局配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?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>
<!--配置数据库连接文件-->
<properties resource="data.properties"></properties>
<!--开启驼峰命名映射-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<environments default="development">
<environment id="development">
<!--配置数据库事务管理器-->
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--配置SQL映射文件-->
<mappers>
<mapper resource="mapper/StudentMapper.xml"></mapper>
</mappers>
</configuration>

第三步:创建SQL语句映射文件StudentMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--1.namespace和接口的全限定名一致-->
<!--2.接口方法名和xml中sql语句ID一致-->
<!--3.sql语句resultType返回值类型和方法返回值类型一致-->
<!--4.输入参数类型与方法中一致-->
<mapper namespace="cn.bytecollege.mapper.StudentMapper">
<select id="findAll" resultType="cn.bytecollege.entity.Student">
SELECT STUDENT_ID,STUDENT_NAME,STUDENT_AGE,STUDENT_GENDER
FROM STUDENT
</select>
</mapper>

第四步:在java目录下,新建包cn.bytecollege.entity,并在包中新建实体类Student

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package cn.bytecollege.entity;

import java.util.Objects;

public class Student {
private Integer studentId;
private String studentName;
private Integer studentAge;
private String studentGender;

public Integer getStudentId() {
return studentId;
}

public void setStudentId(Integer studentId) {
this.studentId = studentId;
}

public String getStudentName() {
return studentName;
}

public void setStudentName(String studentName) {
this.studentName = studentName;
}

public Integer getStudentAge() {
return studentAge;
}

public void setStudentAge(Integer studentAge) {
this.studentAge = studentAge;
}

public String getStudentGender() {
return studentGender;
}

public void setStudentGender(String studentGender) {
this.studentGender = studentGender;
}
@Override
public String toString() {
return "Student{" +
"studentId=" + studentId +
", studentName='" + studentName + '\'' +
", studentAge=" + studentAge +
", studentGender='" + studentGender + '\'' +
'}';
}
}

第五步:在java目录下新建cn.bytecollege.mapper包,并在包中新建接口StudentMapper

1
2
3
4
5
6
package cn.bytecollege.mapper;
import cn.bytecollege.entity.Student;
import java.util.List;
public interface StudentMapper {
List<Student> findAll();
}

第六步:新建测试类查询所有信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package cn.bytecollege;

import cn.bytecollege.entity.Student;
import cn.bytecollege.mapper.StudentMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;
import java.util.List;

public class App
{
public static void main( String[] args ){
//读取Mybatis配置文件
InputStream is = App.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
//创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//获取SqlSession
SqlSession session = sqlSessionFactory.openSession();
// List<Student> list = session.selectList("cn.bytecollege.mapper.StudentMapper.findAll");
StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> list = mapper.findAll();
list.forEach(System.out::println);
}
}

运行上面的程序结果如下图:

从上面的代码可以看出使用Mybatis的整个过程可以分为以下 步:

  1. 读取Mybatis全局配置文件
  2. 构建SqlSessionFactory对象
  3. 创建SqlSession对象
  4. 获取自定义Mapper
  5. 执行SQL语句
  6. 获取执行结果

Mybatis对象简介

在上面步骤中出现了SqlSessionFactory、SqlSession等对象,下面就这些对象进行简单的介绍。

  • SqlSessionFactory:每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。需要注意SqlSessionFactory是一个工厂接口而不是现实类,它的任务是创建SqlSession。
  • SqlSession:SqlSession类似于一个JDBC的Connection对象。MyBatis提供了两种模式去创建SqlSessionFactory:一种是XML配置的方式,另一种是代码的方式。为了避免硬编码以及利于项目维护,通常推荐使用第一种方式,将配置和代码进行分离。
  • Mapper(映射器):映射器是由Java接口和XML文件(或注解)共同组成的,它的作用有:定义参数类型、描述缓存、描述SQL语句、

Mybatis全局配置

在第一章中粗浅地讨论了MyBatis的组成和它们大致的用法,这章的任务是详细讨论MyBatis的配置。

先来看一下MyBatis配置XML文件的层次结构。注意,这些层次是不能够颠倒顺序的,如果颠倒顺序,MyBatis在解析XML文件的时候就会出现异常。先来了解一下MyBatis配置XML文件的层次结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?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>
<!--设置属性-->
<properties></properties>
<!--全局设置-->
<settings></settings>
<!--类型别名-->
<typeAliases></typeAliases>
<!--类型处理器-->
<typeHandlers></typeHandlers>
<!--对象工厂-->
<objectFactory></objectFactory>
<!--插件-->
<plugins></plugins>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
</dataSource>
</environment>
</environments>
<databaseIdProvider></databaseIdProvider>
<mappers></mappers>
</configuration>

需要注意的是在上面的配置文件中所有属性的层次顺序是不能颠倒的,也就是说在configuration标签中第一个必须是properties标签,其他的标签依次向下不能调换顺序。这是因为当使用Mybatis时,Mybatis会先读取该配置文件并按照顺序依次解析将配置值封装进Configuration对象,如果顺序不对,则会导致Mybatis解析配置文件异常。

下面将接每个属性进行解释。

Properties属性

Properties属性用于配置连接数据库的信息,在JDBC的学习中可以知道在开发过程中为了方便程序维护及扩展,通常将数据库连接信息单独写在配置文件中,Properties属性为开发者提供了2种方式,用于配置数据库连接信息。

Properties子元素

通过Properties子元素可以配置数据库连接信息,由于项目打包时会将配置文件拷贝到classes目录下,当需要更换数据库或者项目维护时就需要重写修改该文件,并重写打包,因此并不推荐使用这种方式。

1
2
3
4
5
6
<properties>
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo?useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>

在上面的代码中已经配置好了数据库连接信息,这些信息一般会在datasource标签中使用,使用方式如下:

1
2
3
4
5
6
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>

Properties配置文件

更多的时候会使用Properties配置文件来配置属性,以方便程序的扩展和维护,配置文件中内容如下:

1
2
3
4
url = jdbc:mysql://localhost:3306/mybatis_demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
driver = com.mysql.cj.jdbc.Driver
username = root
password = root

配置文件编写完毕后,将该文件放置在resource目录下(Maven项目中resource目录专门用于存放配置文件)。并在properties标签中进行配置:

1
<properties resource="data.properties"></properties>

当Mybatis运行时会自动寻找该文件,并解析文件中配置的数据。使用时同上一小节的使用方式一致。

在上述的两种配置方式中使用resource/url属性中指定的配置文件的优先级要高于properties元素中指定的属性。

从Mybatis 3.4.2开始,可以为占位符指定一个默认值。

1
2
3
4
<dataSource type="POOLED">
<!-- 如果属性 'username' 没有被配置,'username' 属性的值将为 'ut_user' -->
<property name="username" value="${username:ut_user}"/>
</dataSource>

这个特性默认是关闭的。要启用这个特性,需要添加一个特定的属性来开启这个特性。例如:

1
2
3
4
<properties resource="org/mybatis/example/config.properties">
<!-- 启用默认值特性 -->
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
</properties>

Settings设置属性

Settings是Mybatis中最重要的配置内容之一,它会改变Mybatis运行时的行为,即使不配置settings,Mybatis也可以正常的工作,但是了解Settings的配置内容以及作用是十分必要的。

设置名 描述 有效值 默认值
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true | false false
aggressiveLazyLoading 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。 true | false false (在 3.4.1 及之前的版本中默认为 true)
multipleResultSetsEnabled 是否允许单个语句返回多结果集(需要数据库驱动支持)。 true | false true
useColumnLabel 使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。 true | false true
useGeneratedKeys 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 true | false False
autoMappingBehavior 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。 NONE, PARTIAL, FULL PARTIAL
autoMappingUnknownColumnBehavior 指定发现自动映射目标未知列(或未知属性类型)的行为。NONE: 不做任何反应WARNING: 输出警告日志(’org.apache.ibatis.session.AutoMappingUnknownColumnBehavior’ 的日志等级必须设置为 WARN)FAILING: 映射失败 (抛出 SqlSessionException) NONE, WARNING, FAILING NONE
defaultExecutorType 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。 SIMPLE REUSE BATCH SIMPLE
defaultStatementTimeout 设置超时时间,它决定数据库驱动等待数据库响应的秒数。 任意正整数 未设置 (null)
defaultFetchSize 为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。 任意正整数 未设置 (null)
defaultResultSetType 指定语句默认的滚动策略。(新增于 3.5.2) FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置) 未设置 (null)
safeRowBoundsEnabled 是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。 true | false False
safeResultHandlerEnabled 是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。 true | false True
mapUnderscoreToCamelCase 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 true | false False
localCacheScope MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 SESSION | STATEMENT SESSION
jdbcTypeForNull 当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。 OTHER
lazyLoadTriggerMethods 指定对象的哪些方法触发一次延迟加载。 用逗号分隔的方法列表。 equals,clone,hashCode,toString
defaultScriptingLanguage 指定动态 SQL 生成使用的默认脚本语言。 一个类型别名或全限定类名。 org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler 指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5) 一个类型别名或全限定类名。 org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。 true | false false
returnInstanceForEmptyRow 当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2) true | false false
logPrefix 指定 MyBatis 增加到日志名称的前缀。 任何字符串 未设置
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置
proxyFactory 指定 Mybatis 创建可延迟加载对象所用到的代理工具。 CGLIB | JAVASSIST JAVASSIST (MyBatis 3.3 以上)
vfsImpl 指定 VFS 的实现 自定义 VFS 的实现的类全限定名,以逗号分隔。 未设置
useActualParamName 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1) true | false true
configurationFactory 指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3) 一个类型别名或完全限定类名。 未设置
shrinkWhitespacesInSql 从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5) true | false false
defaultSqlProviderType Specifies an sql provider class that holds provider method (Since 3.5.6). This class apply to the type(or value) attribute on sql provider annotation(e.g. @SelectProvider), when these attribute was omitted. A type alias or fully qualified class name Not set

一个完整的settings元素的示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

关于Settings的常用配置,会在后续的章节中讲解,此处做了解即可。

typeAliases别名属性

别名是一个指代的名称,因为在开发中在很多地方需要配置类的全限定名,例如在使用Mybatis做查询时需要告诉Mybatis将查询结果映射成哪个实体类对象。但是通常类的全限定名比较长,需要有一个简短的名称来替代这全限定名。别名在Mybatis中分为系统定义别名和自定义别名两类。需要注意的是Mybatis中别名是不区分大小写的。一个typeAliases的实例是在解析配置文件时生成的,然后长期保存在Configuration对象中。

系统定义别名

Mybatis系统定义了一些常用的类型的别名,例如:数值、字符串、日期和集合等,可以在Mybatis中直接使用。

首先来看一下Mybatis已经定义好的别名(数组类型的只要加“[]”即可),如下表所示:

别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

这些配置信息都在Mybatis源码org.apache.ibatis.type.TypeAliasRegistry中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class TypeAliasRegistry {
public TypeAliasRegistry() {
registerAlias("string", String.class);

registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
registerAlias("integer", Integer.class);
registerAlias("double", Double.class);
registerAlias("float", Float.class);
registerAlias("boolean", Boolean.class);

registerAlias("byte[]", Byte[].class);
registerAlias("long[]", Long[].class);
registerAlias("short[]", Short[].class);
registerAlias("int[]", Integer[].class);
registerAlias("integer[]", Integer[].class);
registerAlias("double[]", Double[].class);
registerAlias("float[]", Float[].class);
registerAlias("boolean[]", Boolean[].class);

registerAlias("_byte", byte.class);
registerAlias("_long", long.class);
registerAlias("_short", short.class);
registerAlias("_int", int.class);
registerAlias("_integer", int.class);
registerAlias("_double", double.class);
registerAlias("_float", float.class);
registerAlias("_boolean", boolean.class);

registerAlias("_byte[]", byte[].class);
registerAlias("_long[]", long[].class);
registerAlias("_short[]", short[].class);
registerAlias("_int[]", int[].class);
registerAlias("_integer[]", int[].class);
registerAlias("_double[]", double[].class);
registerAlias("_float[]", float[].class);
registerAlias("_boolean[]", boolean[].class);

registerAlias("date", Date.class);
registerAlias("decimal", BigDecimal.class);
registerAlias("bigdecimal", BigDecimal.class);
registerAlias("biginteger", BigInteger.class);
registerAlias("object", Object.class);

registerAlias("date[]", Date[].class);
registerAlias("decimal[]", BigDecimal[].class);
registerAlias("bigdecimal[]", BigDecimal[].class);
registerAlias("biginteger[]", BigInteger[].class);
registerAlias("object[]", Object[].class);

registerAlias("map", Map.class);
registerAlias("hashmap", HashMap.class);
registerAlias("list", List.class);
registerAlias("arraylist", ArrayList.class);
registerAlias("collection", Collection.class);
registerAlias("iterator", Iterator.class);

registerAlias("ResultSet", ResultSet.class);
}

}

自定义别名

系统定义的别名通常是不够用的,因为不同的应用有不同的需要,Mybatis也允许自定义别名,配置方式如下,例如第一章中的查询,就可以使用别名来替代全限定名。配置方式如下:

1
2
3
<typeAliases>
<typeAlias alias="student" type="cn.bytecollege.entity.Student"/>
</typeAliases>

为了方便开发,通常会为所有的实体类定义别名,如果实体类较少的情况下使用上述方式配置别名不会有任何问题,但是当实体类较多时上述的配置方式就显得很烦琐,因此Mybatis又提供了一种配置方式,可以直接指定一个包名,Mybatis会为配置包下所有类自动定义别名,别名为类名首字母小写。配置方式如下:

1
2
3
<typeAliases>
<package name="cn.bytecollege.entity"/>
</typeAliases>

如果通过上述方式配置了别名,会为cn.bytecollege.entity包下的所有的类定义别名,例如cn.bytecollege.entity.Student类的别名就是student。

除此以外,Mybatis还提供了注解的方式配置别名,配置方式如下:

1
2
3
4
@Alias("student")
public class Student {
...
}

虽然Mybatis提供了以上3种方式配置类的别名,但是切记不要混用,否则很容易导致映射文件解析异常。

typeHandlers属性

在前面的章节中知道Mybatis可以将查询出的数据自动映射进配置文件中绑定的实体类对象,在映射的过程中就存在一个问题,如何将数据库的数据类型和Java类型对应起来,并将数据库的数据类型转换成Java的数据类型,而typeHandlers的作用就是将从数据库取出的数据以合适的方式转换成Java类型。Mybatis自带了一些默认的类型处理器,如下表所示:

类型处理器 Java 类型 JDBC 类型
BooleanTypeHandler java.lang.Boolean, boolean 数据库兼容的 BOOLEAN
ByteTypeHandler java.lang.Byte, byte 数据库兼容的 NUMERIC 或 BYTE
ShortTypeHandler java.lang.Short, short 数据库兼容的 NUMERIC 或 SMALLINT
IntegerTypeHandler java.lang.Integer, int 数据库兼容的 NUMERIC 或 INTEGER
LongTypeHandler java.lang.Long, long 数据库兼容的 NUMERIC 或 BIGINT
FloatTypeHandler java.lang.Float, float 数据库兼容的 NUMERIC 或 FLOAT
DoubleTypeHandler java.lang.Double, double 数据库兼容的 NUMERIC 或 DOUBLE
BigDecimalTypeHandler java.math.BigDecimal 数据库兼容的 NUMERIC 或 DECIMAL
StringTypeHandler java.lang.String CHAR, VARCHAR
ClobReaderTypeHandler java.io.Reader -
ClobTypeHandler java.lang.String CLOB, LONGVARCHAR
NStringTypeHandler java.lang.String NVARCHAR, NCHAR
NClobTypeHandler java.lang.String NCLOB
BlobInputStreamTypeHandler java.io.InputStream -
ByteArrayTypeHandler byte[] 数据库兼容的字节流类型
BlobTypeHandler byte[] BLOB, LONGVARBINARY
DateTypeHandler java.util.Date TIMESTAMP
DateOnlyTypeHandler java.util.Date DATE
TimeOnlyTypeHandler java.util.Date TIME
SqlTimestampTypeHandler java.sql.Timestamp TIMESTAMP
SqlDateTypeHandler java.sql.Date DATE
SqlTimeTypeHandler java.sql.Time TIME
ObjectTypeHandler Any OTHER 或未指定类型
EnumTypeHandler Enumeration Type VARCHAR 或任何兼容的字符串类型,用来存储枚举的名称(而不是索引序数值)
EnumOrdinalTypeHandler Enumeration Type 任何兼容的 NUMERIC 或 DOUBLE 类型,用来存储枚举的序数值(而不是名称)。
SqlxmlTypeHandler java.lang.String SQLXML
InstantTypeHandler java.time.Instant TIMESTAMP
LocalDateTimeTypeHandler java.time.LocalDateTime TIMESTAMP
LocalDateTypeHandler java.time.LocalDate DATE
LocalTimeTypeHandler java.time.LocalTime TIME
OffsetDateTimeTypeHandler java.time.OffsetDateTime TIMESTAMP
OffsetTimeTypeHandler java.time.OffsetTime TIME
ZonedDateTimeTypeHandler java.time.ZonedDateTime TIMESTAMP
YearTypeHandler java.time.Year INTEGER
MonthTypeHandler java.time.Month INTEGER
YearMonthTypeHandler java.time.YearMonth VARCHAR 或 LONGVARCHAR
JapaneseDateTypeHandler java.time.chrono.JapaneseDate DATE

自定义typeHandlers

通常而言,Mybatis系统提供使用的TypeHandler已经能给应对绝大部分使用场景,但是并不能保证不会出现不够用的情况,如果出现这种情况,开发者就可以自定义typeHandler进行转换,自定义typeHandler通常需要继承TypeHandler接口,同时Mybatis也提供了BaseTypeHandler继承了TypeHandler接口,也就是说,要实现自定义typeHandler,只需要集成BaseTypeHandler抽象类即可。下面通过示例来学习如何自定义typeHandlers。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package cn.bytecollege.typeHandler;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.log4j.Logger;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
* @author MR.W
*/
@MappedJdbcTypes(JdbcType.VARCHAR)
public class MyStringHandler extends BaseTypeHandler<String> {
Logger logger = Logger.getLogger(MyStringHandler.class);
//1
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
logger.info("setNonNullParameter 为占位符赋值");
ps.setString(i,parameter);
}
//2
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
logger.info("setNonNullParameter 根据列名获取值");
return rs.getString(columnName);
}
//3
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
logger.info("getNullableResult 根据列索引获取值");
return rs.getString(columnIndex);
}
//4.
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
logger.info("getNullableResult");
return cs.getString(columnIndex);
}
}

在上例中自定义了String类型的转换器,为了简单起见,方法内只是进行了简单标记,并调用了父类的同名方法。代码注释处方法含义如下:

  1. 根据占位符的索引,为占位符赋值
  2. 根据数据库字段名称从结果集中获取值
  3. 根据字段索引从结果集中获取数据
  4. 根据字段索引从存储过程结果中获取值

在定义完typeHandler后该typeHandler并不能正常工作,还需要进行配置,配置有两步:

  1. 在自定义typeHandler类上添加注解@MappedJdbcTypes,注解内的值是该typeHandler可以转换的数据库数据类型
  2. 在全局配置文件中进行配置,告诉Mybatis自定义的typeHandler可以将数据库的数据类型转换成Java的何种类型。
1
2
3
<typeHandlers>
<typeHandler handler="cn.bytecollege.typeHandler.MyStringHandler" jdbcType="VARCHAR"></typeHandler>
</typeHandlers>

ObjectFactory对象工厂属性

在第一章的示例中可以看出,当要做查询操作时,只需要在