在上篇文章中粗浅地讨论了MyBatis的组成和它们大致的用法,这里的任务是详细讨论MyBatis的配置。
先来看一下MyBatis配置XML文件的层次结构。注意,这些层次是不能够颠倒顺序的,如果颠倒顺序,MyBatis在解析XML文件的时候就会出现异常。先来了解一下MyBatis配置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>
<!--设置属性-->
<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解析配置文件异常。
下面将接每个属性进行解释。
2.1 Properties属性
Properties属性用于配置连接数据库的信息,在JDBC的学习中可以知道在开发过程中为了方便程序维护及扩展,通常将数据库连接信息单独写在配置文件中,Properties属性为开发者提供了2种方式,用于配置数据库连接信息。
2.1.1 Properties子元素
通过Properties子元素可以配置数据库连接信息,由于项目打包时会将配置文件拷贝到classes目录下,当需要更换数据库或者项目维护时就需要重写修改该文件,并重写打包,因此并不推荐使用这种方式。
<properties>
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
在上面的代码中已经配置好了数据库连接信息,这些信息一般会在datasource标签中使用,使用方式如下:
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
2.2.2 Properties配置文件
更多的时候会使用Properties配置文件来配置属性,以方便程序的扩展和维护,配置文件中内容如下:
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标签中进行配置:
<properties resource="data.properties"></properties>
当Mybatis运行时会自动寻找该文件,并解析文件中配置的数据。使用时同上一小节的使用方式一致。
在上述的两种配置方式中使用resource/url属性中指定的配置文件的优先级要高于properties元素中指定的属性。
从Mybatis 3.4.2开始,可以为占位符指定一个默认值。
<dataSource type="POOLED">
<!-- 如果属性 'username' 没有被配置,'username' 属性的值将为 'ut_user' -->
<property name="username" value="${username:ut_user}"/>
</dataSource>
这个特性默认是关闭的。要启用这个特性,需要添加一个特定的属性来开启这个特性。例如:
<properties resource="org/mybatis/example/config.properties">
<!-- 启用默认值特性 -->
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
</properties>
2.2 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, 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元素的示例如下:
<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的常用配置,会在后续的内容中讲解,此处做了解即可。
2.3 typeAliases别名属性
别名是一个指代的名称,因为在开发中在很多地方需要配置类的全限定名,例如在使用Mybatis做查询时需要告诉Mybatis将查询结果映射成哪个实体类对象。但是通常类的全限定名比较长,需要有一个简短的名称来替代这全限定名。别名在Mybatis中分为系统定义别名和自定义别名两类。需要注意的是Mybatis中别名是不区分大小写的。一个typeAliases的实例是在解析配置文件时生成的,然后长期保存在Configuration对象中。
2.3.1 系统定义别名
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中。
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);
}
}
2.3.2 自定义别名
系统定义的别名通常是不够用的,因为不同的应用有不同的需要,Mybatis也允许自定义别名,配置方式如下,例如第一章中的查询,就可以使用别名来替代全限定名。配置方式如下:
<typeAliases>
<typeAlias alias="student" type="cn.bytecollege.entity.Student"/>
</typeAliases>
为了方便开发,通常会为所有的实体类定义别名,如果实体类较少的情况下使用上述方式配置别名不会有任何问题,但是当实体类较多时上述的配置方式就显得很烦琐,因此Mybatis又提供了一种配置方式,可以直接指定一个包名,Mybatis会为配置包下所有类自动定义别名,别名为类名首字母小写。配置方式如下:
<typeAliases>
<package name="cn.bytecollege.entity"/>
</typeAliases>
如果通过上述方式配置了别名,会为cn.bytecollege.entity包下的所有的类定义别名,例如cn.bytecollege.entity.Student类的别名就是student。
除此以外,Mybatis还提供了注解的方式配置别名,配置方式如下:
@Alias("student")
public class Student {
...
}
虽然Mybatis提供了以上3种方式配置类的别名,但是切记不要混用,否则很容易导致映射文件解析异常。
2.4 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 |
.4.1 自定义typeHandlers
通常而言,Mybatis系统提供使用的TypeHandler已经能给应对绝大部分使用场景,但是并不能保证不会出现不够用的情况,如果出现这种情况,开发者就可以自定义typeHandler进行转换,自定义typeHandler通常需要继承TypeHandler接口,同时Mybatis也提供了BaseTypeHandler继承了TypeHandler接口,也就是说,要实现自定义typeHandler,只需要集成BaseTypeHandler抽象类即可。下面通过示例来学习如何自定义typeHandlers。
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类型的转换器,为了简单起见,方法内只是进行了简单标记,并调用了父类的同名方法。代码注释处方法含义如下:
- 根据占位符的索引,为占位符赋值
- 根据数据库字段名称从结果集中获取值
- 根据字段索引从结果集中获取数据
- 根据字段索引从存储过程结果中获取值
在定义完typeHandler后该typeHandler并不能正常工作,还需要进行配置,配置有两步:
- 在自定义typeHandler类上添加注解@MappedJdbcTypes,注解内的值是该typeHandler可以转换的数据库数据类型
- 在全局配置文件中进行配置,告诉Mybatis自定义的typeHandler可以将数据库的数据类型转换成Java的何种类型。
<typeHandlers>
<typeHandler handler="cn.bytecollege.typeHandler.MyStringHandler" jdbcType="VARCHAR"></typeHandler>
</typeHandlers>
2.5 ObjectFactory对象工厂属性
在上篇文章的示例中可以看出,当要做查询操作时,只需要在标签上配置resultType即可,Mybatis会自动帮开发者创建对象,并将查询出的数据映射到对象中,每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。
默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认无参构造方法,要么通过存在的参数映射来调用带有参数的构造方法。 如果想覆盖对象工厂的默认行为,可以通过创建自己的对象工厂来实现。
通常,如果要创建自己的对象工厂,需要继承ObjectFactory接口,ObjectFactory 接口很简单,它包含两个创建实例化的方法,一个是处理默认无参构造方法的,另外一个是处理带参数的构造方法的,Mybatis为该接口提供了一个实现类DefaultObjectFactory,也就是说创建对象工厂时也可以继承DefaultObjectFactory类。
package cn.bytecollege.factory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.log4j.Logger;
import java.util.List;
public class ExampleObjectFactory extends DefaultObjectFactory {
private Logger logger = Logger.getLogger(ExampleObjectFactory.class);
public ExampleObjectFactory() {
super();
}
@Override
public <T> T create(Class<T> type) {
logger.info("public <T> T create(Class<T> type)");
return super.create(type);
}
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
logger.info("public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs)");
return super.create(type, constructorArgTypes, constructorArgs);
}
@Override
protected Class<?> resolveInterface(Class<?> type) {
logger.info("protected Class<?> resolveInterface(Class<?> type)");
return super.resolveInterface(type);
}
@Override
public <T> boolean isCollection(Class<T> type) {
logger.info("public <T> boolean isCollection(Class<T> type) {");
return super.isCollection(type);
}
}
当创建完对象工厂后,还需要在Mybatis配置文件中进行配置:
<!-- mybatis-config.xml --><objectFactory type="org.mybatis.example.ExampleObjectFactory"> <property name="someProperty" value="100"/></objectFactory>
2.6 plugins插件属性
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。
2.7 environment环境属性
配置环境可以注册多个数据源,例如,日常开发中通常会有开发库、测试库、生产库、并且需要在这几个库中切换,此时配置多个数据源就非常有必要。每个数据源大概分为:数据库连接信息的配置和数据库事务的配置。 一份完整的配置示例如下:
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
<environment id="production">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${prod.driverClassName}"/>
<property name="url" value="${prod.url}"/>
<property name="username" value="${prod.username}"/>
<property name="password" value="${prod.password}"/>
</dataSource>
</environment>
</environments>
注意一些关键点:
- 默认使用的环境 ID(比如:default="development")。
- 每个 environment 元素定义的环境 ID(比如:id="development")。
- 事务管理器的配置(比如:type="JDBC")。
- 数据源的配置(比如:type="POOLED")。
默认环境和环境 ID 顾名思义。 环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。
2.7.1 事务配置
在 MyBatis 中有3种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
- JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
- MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。
- 自定义:开发者自定义数据库事务的管理办法
import cn.bytecollege.entity.Student;
import cn.bytecollege.mapper.StudentMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class TransactionDemo {
public static void main(String[] args) {
SqlSession session = null;
Student student = new Student();
student.setStudentName("郭靖");
student.setStudentAge(18);
student.setStudentGender("男");
try{
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
session = sqlSessionFactory.openSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
mapper.addStudent(student);
//<1>
session.commit();
}catch (Exception e){
//<2>
session.rollback();
e.printStackTrace();
}
}
}
从上例代码中可以看出当执行完数据库操作后,调用session对象的commit()方法提交事务,如果发生异常时则调用rollback()方法进行事务回滚。
2.7.2 数据源
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。Mybatis提供了3种数据源类型,下面就这种类型进行简单介绍:
- UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。
- POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。这种方式是开发中常用的方式。
- JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。
开发者只需要把数据源的属性type定义为:UNPOOLEN、POOLED、JNDI即可。 如果需要使用自定义数据源,需要实现org.apache.ibatis.datasource.DatatSourceFactory接口,例如要是用Druid数据源,就需要自定义数据源。
package cn.bytecollege.datasource;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.apache.ibatis.datasource.DataSourceFactory;
import javax.sql.DataSource;
import java.util.Properties;
public class MyDruidDataSource implements DataSourceFactory {
private Properties properties = null;
@Override
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public DataSource getDataSource() {
DruidDataSource druidDataSource = null;
try {
druidDataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
return druidDataSource;
}
}
数据源定义完毕后还需要在全局配置文件中进行配置,配置完成后就可以使用自定义数据源了,配置如下:
<!--1-->
<dataSource type="cn.bytecollege.datasource.MyDruidDataSource">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
在上述配置中注释1处,只需要将type修改为自定义数据源的全限定名即可。
2.8 databaseIdProvider数据库厂商标识属性
在相同数据库厂商的环境下,数据库厂商标识没有任何意义,在实际开发中使用的较少。如果使用了不同厂商的数据库,Mybatis可以根据不同的数据库厂商执行不同的语句。这种支持是基于配置文件中的databaseId属性。Mybatis会加载带有匹配当前数据库databaseId属性的语句。
2.9 mappers映射器属性
映射器是Mybatis最核心的组件之一,在本小节内主要讨论如何在全局配置文件中引入映射器,映射器中的参数类型、动态SQL、缓存等内容将会在第3章及第4章中详细讲解。 Mybatis提供了4种映射器的引入方式
2.9.1 resource引入
resource导入是使用mapper标签的resource属性引入映射器。这种方式也是使用最多的方式。配置如下:
<mappers>
<mapper resource="mapper/StudentMapper.xml"/>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
如果是使用Maven构建的项目,为了方便管理映射器,通常在resource目录下新建mapper目录,然后将映射器放入该目录中,resource只需配置相对路径即可完成映射。
2.9.2 class引入
class引入则是使用mapper标签的class属性引入映射器,这种方式通常需要将映射器放在Mapper接口相同的目录中(Eclipse项目),如果是Maven项目,则需要在resource目录中新建和包名相同的路径,然后将映射器放入。配置如下:
<mappers>
<mapper class="cn.bytecollege.mapper.StudentMapper"/>
<mapper class="cn.bytecollege.mapper.UserMapper"/>
</mappers>
2.9.3 url引入
url引用方式在开发中使用的较少,因为这种方式是使用映射器的绝对路径进行引入,引入方式如下:
<mappers>
<mapper url="file:///E:\project\idea\mybaits\chapter02\src\main\resources\cn\bytecollege\mapper\StudentMapper.xml"></mapper>
</mappers>
需要注意的是这种引入方式需要在文件的路径前添加“file:///”,否则会引发异常。
2.9.4 package引入
package引入方式也是开发中比较常用的一种引入方式,此种方式是使用package标签配置映射器所在的路径。这种方式只需要配置包名,Mybatis即可扫描到该路径下的映射器。需要注意的是这种方式也需要在resource目录下建立和Mapper接口相同的路径名,并将映射器放入才能使用。配置方式如下:
<mappers>
<package name="cn.bytecollege.mapper"/>
</mappers>
总结
在本文内系统学习了Mybatis的全局配置文件,在整个Mybatis框架内,全局配置文件可以说是该框架的核心之一,该文件中常用配置有数据库与对象字段映射,缓存,以及映射器(即分离后的SQL语句)。因此掌握全局文件的配置也就相当于掌握了一半Mybatis框架。