1、configuration(配置)
在创建Web工程时,最重要的一步就是配置工程的全局参数信息,以及数据库连接信息。对于MyBatis,最核心的全局配置文件就是SqlMapConfig.xml(mybatis-config.xml),其中不仅包含了数据库连接信息,同时还包含了Mapper映射文件的加载路径、全局参数以及类别别名等一系列MyBatis的核心配置信息。SqlMapConfig中的配置信息必须严格按照格式标准进行配置。
MyBatis全局配置文件信息:
配置名称 | 配置含义 | 配置简介 |
configuration | 包裹所有配置标签 | 整个配置文件的顶级标签。 |
properties | 属性 | 该标签可以引入外部配置的属性,也可以自己配置。该配置标签所在的同一个配置文件中的其他配置均可引用此配置中的属性。 |
settings | 全局配置参数 | 用来配置一些改变运行时行为的信息。例如是否使用缓存机制,是否使用延迟加载,是否使用错误处理机制等。并且可以设置最大并发请求数量、最大并发事务数量,以及是否启用命名空间等。 |
typeAliases | 类型别名 | 用来设置一些别名来替代Java的长类型声明(如: java.lang.int变为int),减少配置编码的冗余。 |
typeHandlers | 类型处理器 | 将SQL中返回的数据库类型转换为相应的Java类型的处理器配置。 |
objectFactory | 对象工厂 | 实例化目标类的工厂类配置。 |
plugins | 插件 | 可以通过插件修改MyBatis的核心行为,例如对语句执行的某一点进行拦截调用。 |
environments | 环境集合属性对象 | 数据库环境信息的集合。在一个配置文件中,可以有多种数据库环境集合,这样可以使MyBatis将SQL同时映射至多个数据库。 |
environment | 环境子属性对象 | 数据库环境配置的详细配置。 |
transactionManager | 事务管理 | 指定MyBatis的事务管理器。 |
dataSource | 数据源 | 使用其中的type指定数据源的连接类型,在标签对中可以使用property属性指定数据库连接池的其他信息。 |
mappers | 映射器 | 配置SQL映射文件的位置,告知MyBatis去哪里加载SQL映射配置。 |
【示例】下面按照MyBatis的全局配置文件中的配置顺序,给出一个配置了全部参数的样例配置文件。
<?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>
<!-- 1.properties属性引入外部配置文件 -->
<properties resource="config/db.properties">
<!-- property里面的属性全局均可使用 -->
<property name="jdbc.username" value="root"/>
<property name="jdbc.password" value="123456"/>
<!-- 启用默认值特性,这样${}拼接符才可以设置默认值 -->
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
</properties>
<!-- 2.全局配置参数 -->
<settings>
<!-- 指定 MyBatis 所用日志的具体实现,未指定时将自动查找 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 开启自动驼峰命名规则(camel case)映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启延迟加载开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 将积极加载改为消极加载(即按需加载),默认值就是false -->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 打开全局缓存开关(二级环境),默认值就是true -->
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- 3.别名设置 -->
<typeAliases>
<typeAlias alias="user" type="com.pjb.mybatis.po.User"/>
<typeAlias alias="teacher" type="com.pjb.mybatis.po.Teacher"/>
<typeAlias alias="integer" type="java.lang.Integer"/>
</typeAliases>
<!-- 4.类型转换器 -->
<typeHandlers>
<!-- 一个简单的类型转换器 -->
<typeHandler handler="com.pjb.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>
<!-- 5.对象工厂 -->
<objectFactory type="com.pjb.mybatis.example.ExampleObjecFactory">
<!-- 对象工厂注入参数 -->
<property name="someProperty" value="100"/>
</objectFactory>
<!-- 6.插件 -->
<plugins>
<plugin interceptor="com.pjb.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
<!-- 7.environments数据库环境配置 -->
<!-- 和Spring整合后environments配置将被废除 -->
<environments default="development">
<environment id="development">
<!-- 使用JDBC事务管理 -->
<transactionManager type="JDBC"/>
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username:root}"/>
<property name="password" value="${jdbc.password:123456}"/>
</dataSource>
</environment>
</environments>
<!-- 8.加载映射文件 -->
<mappers>
<mapper resource="com.pjb.mybatis.sqlmap.UserMapper.xml"/>
<mapper resource="com.pjb.mybatis.sqlmap.OtherMapper.xml"/>
</mappers>
</configuration>
特别注意:
MyBatis中的配置,不但有类型限制,还有顺序限制。
必须按照:<properties>、<settings>、<typeAliases>、<typeHandlers>、…顺序排放。
2、properties(属性)
2.1 properties的配置
在SqlMapConfig配置文件中,properties标签中的数据可以供整个配置文件中的其他配置使用。properties标签可以可以引入一个动态变换的外部配置,如一个传统的Java配置文件,或者是一个properties参数配置文件。当然,在properties标签内部也可以放置property标签,来配置子元素信息。对于配置文件中的其他配置,可以通过property子标签的name属性来取得相应的value值。
一个properties标签配置如下(引入一个db.properties文件):
<!-- properties属性引入外部配置文件 -->
<properties resource="config/db.properties">
<!-- property里面的属性全局均可使用 -->
<property name="jdbc.username" value="root"/>
<property name="jdbc.password" value="123456"/>
</properties>
可以看到,其中引入了一个db.properties文件,该文件配置如下:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_admin?useSSL=false&
jdbc.username=root
jdbc.password=123456
友情提示:
db.properties配置文件中的 jdbc.username,如果写成username,可能会与系统环境中的username变量冲突,所以到时候真正连接数据库的时候,用户名就被替换成系统中的用户名(有得可能是administrator),那肯定是连接不成功的。
db.properties配置文件中配置了数据库的详细连接信息,properties标签这样引入它避免了数据库信息的“硬编码”。当需要连接其他数据库时,只需要更改要连接的数据库的配置文件的路径即可。
当然properties标签也可以包含property子标签,该子标签中的值也可以被配置文件中的其他配置使用,属于一种全局参数。可以看到,这里虽然引入了db.properties文件,但是其中的 jdbc.username 和 jdbc.password 还可以重新在property子标签中配置,这个时候取的值就是property子标签中的value值。这样配置是因为有时候有些模块的数据库连接的用户可能需要以不同的角色登录,这样可以在property子标签中动态分配数据库连接的用户名 jdbc.username 和密码 jdbc.password。
在properties标签中引入的配置文件信息以及property子标签中的配置信息,在其他标签中可以使用“${}”拼接符的方式来获取,在“${}”中填写引入的配置文件中的参数的name或者property子标签的name,样例配置中的数据源信息配置其实就是获取properties标签中的配置信息:
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
2.2 properties设置默认值
在MyBatis 3.4.2之后,还可以在“${}”拼接符中设置一个默认值,格式如下:
${属性:默认值}
即在所需引入的属性名的后面添加“:”引号,然后紧跟着填写属性不存在或为空时的默认值。
当然,首先要在properties标签中设置一个启用拼接符默认值的配置,该配置如下:
然后在其他属性引入properties标签中的参数时,可以设置一个默认值:
提示:由于在日常开发中数据库可能会发生变动,所以应避免将数据库配置信息硬编码。当然,过于集中的配置也不利于维护,所以这里单独为数据库配置一个db.properties文件,当需要更改数据库信息时,只需要更改db.properties文件即可。
3、settings(设置)
settings配置也是MyBatis全局配置文件中比较重要的配置,它影响MyBatiis框架在运行时的一些行为。settings配置缓存、延迟加载、结果集控制、执行器、分页设置、命名规则等一系列控制性参数,与MyBatis的运行性能息息相关。所有的setting配置都被包裹在settings标签对中。
setting配置参数:
属性名 | 简介 | 有效值 | 默认值 |
cacheEnabled | 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType属性来覆盖该项的开关状态。 | true | false | false |
aggressiveLazyLoading | 当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载(参考 lazyLoadTriggerMethods)。 | true | false | false |
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: 输出提醒日志; FAILING: 映射失败 (抛出 SqlSessionException); | NONE, WARNING, FAILING | NONE |
defaultExecutorType | 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定驱动等待数据库响应的秒数。 | 任意正整数 | 无 |
defaultFetchSize | 为了防止从数据库查询出来的结果过多,而导致内存溢出,可以通过设置 fetchSize 参数来控制结果集的数量。 | 任意正整数 | 无 |
safeRowBoundsEnabled | 允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。 | true | false | false |
safeResultHandlerEnabled | 允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为 false。 | true | false | true |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 | true | false | false |
localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 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 |
vfsImpl | 指定 VFS 的实现 | 自定义 VFS 的实现的类全限定名,以逗号分隔。 | 无 |
useActualParamName | 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1) | true | false | true |
configurationFactory | 指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3) | 类型别名或者全类名. | 无 |
一个配置完整的 settings 元素的示例如下:
<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>
提示:setting配置常用来实现缓存、延迟加载和分页设置。
4、typeAliases(类型别名)
在MyBatis的SQL映射配置文件中,常使用 parameterType、resultType之类的参数设置SQL语句的输入/输出参数,一般参数都是一个Java类型的数据,有基本数据类型或封装类型,但是一般都要声明该类型的全路径名称,例如“java.lang.String”、“java.util.HashMap”或者“com.pjb.mybatis.po.User”,如下:
那么,是否可以像在Java类中一样,在某个地方声明该类的全路径名称,然后在使用是仅使用该类型的别名呢?通过设置MyBatis的全局配置文件中的typeAliases属性,就可以为SQL映射文件中的输入/输出参数设置类型别名,然后在SQL映射配置文件中指定输入/输出参数类型时使用别名,配置如下:
此时在SQL映射配置文件中可以使用别名来指定输入/输出参数的类型:
当然,一般会将JavaBean类型的封装类放置在一个包下面(如本示例中的 com.pjb.mybatis.po 包)一个一个配置别名很繁琐,所以MyBatis提供了批量定义别名的方法,指定包名即可,程序会为该包下的所有包装类加上别名。定义别名的规则就是对应包装类的类名首字母变为小写。配置如下:
别名也可以使用注解来实现,实现方式就是在需要指定别名的类声明头添加“@Alias”注解,其中的参数就是该类对应的别名,代码如下:
MyBatis已经为Java的常见类型默认指定了别名,可以直接使用。这里要注意的是,有一些基本数据类型和包装类数据类型的名称一样(除了包装类中首字母大写的类),故在基本数据类型的前面加上下划线“_”作为区分。
MyBatis中常见类型别名:
别名 | 映射的类型 |
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | java.lang.String |
byte | java.lang.Byte |
long | java.lang.Long |
short | java.lang.Short |
int | java.lang.Integer |
integer | java.lang.Integer |
double | java.lang.Double |
float | java.lang.Float |
boolean | java.lang.Boolean |
date | java.util.Date |
decimal | java.math.BigDecimal |
bigdecimal | java.math.BigDecimal |
object | java.lang.Object |
map | java.util.Map |
hashmap | java.util.HashMap |
list | java.util.List |
arraylist | java.util.ArrayList |
collection | java.util.Collection |
iterator | java.util.Iterator |
所以,当需要为映射参数配置别名时,就可以使用typeAliases属性。在日常开发中经常使用实体类型的别名来简化配置文件,以便提高开发效率。
5、typeHandlers(类型处理器)
在MyBatis的SQL映射配置文件中,为SQL配置的输入参数最终要从Java类型转换成数据库能识别的类型,而从SQL的查询结果集中获取的数据,也要从数据库的数据类型转换为对应的Java类型。在MyBatis中,使用类型处理器(TypeHandler)将从数据库获取的值以合适的方式转换为Java类型,或者将Java类型的参数转换为数据库对应的类型。在MyBatis中有许多自带的类型处理器,但有时候也会满足不了开发的需求,这时候就需要配置自己的类型处理器了,typeHandlers标签就是用来声明自己的类型处理器的。
5.1 创建typeHandlers类型处理器
使用typeHandlers标签配置一个自己的类型处理器,一般需要三个步骤:编写类型处理器类,在MyBatis全局配置文件中配置该类型处理器,在SQL映射配置文件中使用。
下面编写一个将JDBC的TIMESTAMP类型与Java的String类型相互转换的类型转换器配置示例。首先编写类型处理器类,一般要实现 org.apache.ibatis.type.TypeHandler 接口,接口的泛型指定要转换的Java参数类型(若不指定则默认为Object类)。实现TypeHandler接口主要改写一下4个方法:
public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException;
public String getResult(ResultSet rs, String columnName) throws SQLException;
public String getResult(ResultSet rs, int columnIndex) throws SQLException;
public String getResult(CallableStatement cs, int columnIndex) throws SQLException;
其中,setParameter方法是在为SQL配置传入参数时(新增、删除、修改以及条件查询)执行的操作,可以在将参数传入数据库之前在该方法中对数据类型做处理。另外三个 getResult 方法则在数据库返回结果时,将结果信息转换为相应的Java类型。它们之间的区别是,前两个 getResult 方法提供给普通的Select方法使用(一个根据字段名,一个根据字段下标来获取数据),最后一个 getResult 方法供存储过程使用(根据字段下标获取数据)。
【示例】编写一个将JDBC的TIMESTAMP类型与Java的String类型相互转换的类型转换器。
创建名称为DateStrTypeHandler的类型转换器,并实现TypeHandler接口。
package com.pjb.mybatis.handler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import java.sql.*;
import java.text.SimpleDateFormat;
/**
* 字符串日期类型转换器
* @author pan_junbiao
**/
public class DateStrTypeHandler implements TypeHandler<String>
{
//设置日期转换格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException
{
Timestamp timestamp = Timestamp.valueOf(parameter);
ps.setTimestamp(i,timestamp);
}
@Override
public String getResult(ResultSet rs, String columnName) throws SQLException
{
Timestamp sqlTimestamp = rs.getTimestamp(columnName);
return sqlTimestamp != null ? sdf.format(sqlTimestamp) : null;
}
@Override
public String getResult(ResultSet rs, int columnIndex) throws SQLException
{
Timestamp sqlTimestamp = rs.getTimestamp(columnIndex);
return sqlTimestamp != null ? sdf.format(sqlTimestamp) : null;
}
@Override
public String getResult(CallableStatement cs, int columnIndex) throws SQLException
{
Timestamp sqlTimestamp = cs.getTimestamp(columnIndex);
return sqlTimestamp != null ? sdf.format(sqlTimestamp) : null;
}
}
5.2 使用typeHandlers类型处理器
5.2.1 使用方式(一):全局配置
在MyBatis配置文件(SqlMapConfig.xml)中,配置类型转换器信息。
<!-- 类型转换器 -->
<typeHandlers>
<!-- 字符串日期类型转换器 -->
<typeHandler handler="com.pjb.mybatis.handler.DateStrTypeHandler" javaType="java.lang.String" jdbcType="TIMESTAMP"/>
</typeHandlers>
这样全局配置后,该类型转换器将作用于整个项目。
5.2.2 使用方式(二):单独使用
首先将上面在MyBatis配置文件(SqlMapConfig.xml)中,配置的类型转换器删除。
(1)Select方法使用类型转换器。
在SQL映射配置文件(UserMapper.xml)中,创建User类对应的resultMap对象,然后在该对象的子节点中使用typeHandler属性,设置类型转换器,最后将该resultMap对象引用到Select方法的返回结果集中。
(2)Insert方法使用类型转换器。
其实,在编写自己的类型处理器时,可以不实现TypeHandler接口,转而继承另一个类(org.apache.ibatis.type.BaseTypeHandler),它是MyBatis的一个标准基础类型处理器类。BaseTypeHandler本身已经实现了TypeHandler接口,并继承了TypeReference抽象类,在它内部简单地实现了TypeHandler接口中定义的4个方法的部分功能。继承BaseTypeHandler类之后,可以通过改写setNonNullParameter、getNullableResult方法(三个)来实现类型处理器:
public abstract void setNonNullParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException;
public abstract T getNullableResult(ResultSet var1, String var2) throws SQLException;
public abstract T getNullableResult(ResultSet var1, int var2) throws SQLException;
public abstract T getNullableResult(CallableStatement var1, int var2) throws SQLException;
因为BaseTypeHandler类继承TypeReference抽象类,所以它本身也是一个抽象类(abstract),类中的4个方法也为抽象方法。
5.3 默认类型处理器
MyBatis已经提供了一些默认的类型处理器,这些默认的类型处理器都继承BaseTypeHandler类。
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 |
6、objectFactory(对象工厂)
SQL映射配置文件中的SQL语句所得到的查询结果,会动态映射到resultType或者其他处理结果集的参数配置对应的Java类型,其中就有JavaBean等封装类。而objectFactory(对象工厂)就是用来创建实体对象的类。
在MyBatis中,默认的objectFactory要做的就是实例化查询结果对应的目标类,有两种方式可以将查询结果的值映射到对应的目标类,一种是通过目标类的默认构造方法,另外一种就是通过目标类的有参构造方法。
有时候在New一个新对象时(构造方法或者有参构造方法),在得到对象之前需要处理一些逻辑,或者在执行该类的有参构造方法时,在传入参数之前,要对参数进行一些处理,这是就可以创建自己的objectFactory来加载该类型的对象。
如果想改写默认的对象工厂,可以继承DefaultObjectFactory来创建自己的对象工厂,从而改写相关的4个方法,如:
package com.pjb.mybatis.objectFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import java.util.List;
import java.util.Properties;
/**
* 自定义objectFactory(对象工厂)
* @author pan_junbiao
**/
public class MyObjectFactory extends DefaultObjectFactory
{
//处理默认构造方法
public Object create(Class type) {
return super.create(type);
}
//处理有参构造方法
public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) {
return super.create(type, constructorArgTypes, constructorArgs);
}
//处理参数
public void setProperties(Properties properties) {
super.setProperties(properties);
}
//判断集合类型参数
public <T> boolean isCollection(Class<T> type) {
return Collection.class.isAssignableFrom(type);
}
}
编写好自己的对象工厂之后,在MyBatis的全局配置文件SqlMapConfig.xml中添加以下配置,这样才能使对象工厂生效:
<!-- 5.对象工厂 -->
<objectFactory type="com.pjb.mybatis.objectFactory.MyObjectFactory">
<!-- 对象工厂注入参数 -->
<property name="someProperty" value="100"/>
</objectFactory>
其中的property参数,会在加载全局配置文件SqlMapConfig.xml时通过setProperties方法被初始化到MyObjectFactory中,作为该类的全局参数使用。
【示例】编写一个实例化订单Order类的对象工厂OrderObjectFactory,它的功能就是在执行订单Order类的构造方法之前,执行Order类的init方法来计算订单的总金额。
(1)创建Order类
package com.pjb.mybatis.po;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 订单持久化类
* @author pan_junbiao
**/
public class Order implements Serializable
{
private int id; //订单ID
private String productName; //商品名称
private int number; //购买数量
private Double price; //商品单价
private Double totalAmout; //总金额
//无参构造方法
public Order() { }
//有参构造方法
public Order(int id,String productName,int number,Double price)
{
super();
this.id = id;
this.productName = productName;
this.number = number;
this.price = price;
}
public void init()
{
//计算商品的总金额
this.totalAmout = this.number * this.price;
}
//getter与setter方法省略...
}
(2)定义OrderObjectFactory对象工厂类,继承DefaultObjectFactory类,并重新create方法,在该方法中检查如果加装的是Order类型,就加载init方法:
package com.pjb.mybatis.objectFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import java.util.List;
import com.pjb.mybatis.po.Order;
/**
* 订单ObjectFactory对象工厂
* @author pan_junbiao
**/
public class OrderObjectFactory extends DefaultObjectFactory
{
@Override
public <T> T create(Class<T> type) {
return super.create(type);
}
//DefaultObjectFactory的create(Class type)方法也会调用此方法
//所以,只需要在此方法中添加逻辑即可
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs)
{
T ret = super.create(type,constructorArgTypes,constructorArgs);
//判断加载类的类型,然后执行init方法
if(Order.class.isAssignableFrom(type))
{
Order entity = (Order)ret;
entity.init();
}
return ret;
}
}
(3)最后在SqlMapConfig.xml全局配置文件中配置该自定义对象工厂即可:
<!-- 对象工厂 -->
<objectFactory type="com.pjb.mybatis.objectFactory.OrderObjectFactory"/>
(4)编写测试方法
package com.pjb.mybatis.test;
import com.pjb.mybatis.datasource.DataConnection;
import com.pjb.mybatis.objectFactory.OrderObjectFactory;
import com.pjb.mybatis.po.Order;
import org.apache.ibatis.session.SqlSession;
import java.util.ArrayList;
import java.util.List;
/**
* ObjectFactory(对象工厂)测试类
* @author pan_junbiao
**/
public class ObjectFactoryTest
{
public static void main(String[] args)
{
DataConnection dataConnection = new DataConnection();
SqlSession sqlSession = dataConnection.getSqlSession();
OrderObjectFactory orderObjectFactory = new OrderObjectFactory();
//设置参数类型List
List constructorArgTypes = new ArrayList();
constructorArgTypes.add(int.class);
constructorArgTypes.add(String.class);
constructorArgTypes.add(int.class);
constructorArgTypes.add(Double.class);
//设置参数值List
List constructorArgs = new ArrayList();
constructorArgs.add(1);
constructorArgs.add("华为手机");
constructorArgs.add(8);
constructorArgs.add(1100);
Order order = (Order)orderObjectFactory.create(Order.class,constructorArgTypes,constructorArgs);
System.out.println("订单编号:" + order.getId());
System.out.println("商品名称:" + order.getProductName());
System.out.println("购买数量:" + order.getNumber());
System.out.println("商品单价:" + order.getPrice());
System.out.println("总金额:" + order.getTotalAmout());
sqlSession.close();
}
}
7、plugins(插件)
在某种情况下,需要在执行程序的过程中对某一点进行拦截,并在拦截后做出一系列处理,此时就需要使用一种“拦截器”。在MyBatis中,对某种方法进行拦截调用的机制,被称为 plugin 插件。使用 plugin 可以很好地对方法地调用进行监控,而且还可以修改或重写方法的行为逻辑。在 MyBatis 中允许使用 plugin 来拦截的方法如下:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
其中,Executor是MyBatis对外提供的一个操作接口类,其中包含了query 查询、update 修改、commit 提交、rollback 回滚等核心方法。ParameterHandler、ResultSetHandler 及 StatementHandler 分别是处理参数、结果集、预编译状态的接口,里面的一些方法也可以使用plugin进行拦截。值得一提的是,plugin可以操作MyBatis的框架核心方法。在修改plugin时可能会影响框架的稳定性,所以在编写plugin时要十分谨慎。
实现一个plugin很简单,只需要继承Interceptor接口,并且指定需要拦截的方法的签名信息即可。
如下是一个基础拦截器的实现:
package com.pjb.mybatis.plugins;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.util.Properties;
import java.util.concurrent.Executor;
/**
* Query拦截器
* @author pan_junbiao
**/
@Intercepts({
@Signature(
type = Executor.class,
method="query",
args={MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class}
)
})
public class QueryPlugin implements Interceptor
{
@Override
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
这里,在插件的类头部添加了“@Intercepts”拦截器注解,此注解声明此类是一个插件类。其中可以声明多个 @Signature 签名信息注解,type为拦截的方法所属的接口类型,method为拦截的方法名称,args是需要的参数信息。其中intercept方法是一个对目标方法进行拦截的抽象方法,而plugin方法的作用是将拦截器插入目标对象。setProperties方法的作用是将全局配置文件中的参数注入插件类中。这里的示例插件中对Executor的query方法进行了拦截调用。
编写了插件类后,还要找在MyBatis 全局配置文件中配置该插件即可,这样插件就会起到拦截作用,示例代码如下:
<!-- 插件 -->
<plugins>
<plugin interceptor="com.pjb.mybatis.plugins.QueryPlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
此时就可以拦截Executor的query方法了,也即是默认执行的查询方法,可以在重写的插件类的intercept方法中添加拦截逻辑。
插件使用的场景有:日志记录、权限控制、缓存控制等。
注意:使用plugin拦截和覆盖MyBatis的核心方法时,一定要小心谨慎,否则可能会影响MyBatis的核心功能。
8、environments(环境配置)
在MyBatis全局配置文件中,environments是放置有关数据库连接数据的配置标签,所有与外部数据库进行交互的数据都配置在该标签中,在environments标签中可以配置多个数据库连接环境,以便SQL语句可以适用于多个数据库环境。
在environments中可以配置一个个单独的environment,它们代表多个数据库环境的配置信息。每个environment都包含事务管理器(transactionManager)和数据源(dataSource)信息。如下是一个完整的environments配置:
<!-- environments数据库环境配置 -->
<!-- 和Spring整合后environments配置将被废除 -->
<environments default="development">
<environment id="development">
<!-- 使用JDBC事务管理 -->
<transactionManager type="JDBC"/>
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
事务管理器(transactionManager)有两种类型:分别是JDBC和MANAGET。配置JDBC代表直接使用JDBC的提交和回滚设置。配置MANAGED则不提交和回滚连接,而是由容器来管理事务的生命周期。默认情况下,MANAGED会关闭连接,但是可以动态指定closeConnection参数,当设置为“false”时,在MANAGED类型下就不会自动关闭连接。配置如下:
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>
在项目开发中将Spring MVC与MyBatis进行整合时,由于Spring的框架机制,其自带的管理器会覆盖MyBatis的配置,此时要单独设置事务管理器。
关于数据源(dataSource),在MyBatis中有三种内建的数据源类型(也就是 type=”[UNPOOLED | POOLED | JNDI]”)。其中,UNPOOLED设置每次请求时打开和关闭连接,而POOLED可以设置一个管理数据库连接的资源池,用来合理控制数据库的连接和关闭次数,利用“池”的概念将JDBC连接对象组织起来。而JNDI则配置连接外部数据源(如服务器提供的数据源)的信息。
在数据源(dataSource)中配置以JDBC标准连接数据库所需要的各项参数信息,根据dataSource的不同,可以设置如下信息:
属性名称 | 作用 | 数据源类型 |
driver | JDBC驱动名称,可以使用“.”为驱动添加其他属性(如:driver.encoding=UTF8) | UNPOOLED | POOLED |
url | 数据库的连接地址 | UNPOOLED | POOLED |
username | 连接数据库的用户名 | UNPOOLED | POOLED |
password | 连接数据库的密码 | UNPOOLED | POOLED |
defaultTransactionIsolationLevel | 默认的连接事务隔离级别 | UNPOOLED | POOLED |
poolMaximumActiveConnections | 数据库最大活动连接数 | UNPOOLED | POOLED |
poolMaximumIdleConnections | 数据库最大空闲连接数 | UNPOOLED | POOLED |
poolMaximumCheckoutTime | 连接的最大失效时间,默认值:20000 毫秒(即 20 秒) | UNPOOLED | POOLED |
poolTimeToWait | 对数据库进行连通检测(ping)时,如果数据库的连接等待时间过长,它会给连接池打印状态日志并重新尝试获取一个连接,默认值:20000 毫秒(即 20 秒) | UNPOOLED | POOLED |
poolPingQuery | 用来检测数据库是否可以连通查询,默认是“NO PING QUERY SET” | UNPOOLED | POOLED |
poolPingEnabled | 是否开启数据库连通检测 | UNPOOLED | POOLED |
poolPingConnectionsNotUsedFor | 配置poolPingQuery的使用频率,默认是0 | UNPOOLED | POOLED |
initial_context | 用来设置在initialContext中寻找上下文。此属性为可选,若不设置,data_source配置将会直接在initialContext中寻找 | JNDI |
data_source | 引用外部数据源信息的具体路径 | JNDI |
env.xxx | 通过前缀“env.”将后面的属性直接传给上下文(如:driver.encoding=UTF8) | JNDI |
当然,也可以自己设置数据源,通过实行 DataSourceFactory 接口来实现(也可以引入其他第三方数据源)。
前面提到MyBaits支持配置多个数据库连接环境,那么在多个数据库中执行SQL语句时,某些规则是不一样的,如果要兼容各个数据库厂商的SQL语言规则,则还需要配置“databaseIdProvider”参数。如果希望SQL支持多个数据库厂商的规则,可以在MyBatis全局文件中添加以下配置:
<databaseIdProvider type="DB_VENDOR"/>
可以通过实现DatabaseIdProvider接口,并在MyBaits全局配置文件中注册,来创建自己的DatabaseIdProvider。
9、mappers(映射器)
MyBatis是基于SQL映射配置的框架,SQL语句都写在Mapper配置文件中,那么当构建SqlSession类之后,是需要读取Mapper 配置文件中的SQL配置。而mappers标签就是用来配置需要加载的SQL映射配置文件路径的。
mappers标签下有许多mapper子标签,每一个mapper子标签中配置的都是一个独立的Mapper映射配置文件的路径。有以下几种配置方式:
第一种,使用相对路径进行配置:
<mappers>
<mapper resource="org/mybatis/mappers/UserMapper.xml"/>
<mapper resource="org/mybatis/mappers/ProductMapper.xml"/>
<mapper resource="org/mybatis/mappers/ManagerMapper.xml"/>
</mappers>
第二种,使用绝对路径进行配置:
<mappers>
<mapper url="file:///var/mappers/UserMapper.xml"/>
<mapper url="file:///var/mappers/ProductMapper.xml"/>
<mapper url="file:///var/mappers/ManagerMapper.xml"/>
</mappers>
第三种,使用接口信息进行配置:
<mappers>
<mapper class="org.mybatis.mappers.UserMapper"/>
<mapper class="org.mybatis.mappers.ProductMapper"/>
<mapper class="org.mybatis.mappers.ManagerMapper"/>
</mappers>
第四种,使用接口所在包进行配置:
<mappers>
<package name="org.mybatis.mappers"/>
</mappers>
配置了mappers信息后,MyBatis就知道去哪里加载Mapper映射文件。在MyBatis中,mappers配置是MyBatis全局配置文件中比较重要的配置。