2.1 一份完整的配置文件
<?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="application.properties"><!--属性-->
<property name="username" value="db_user"/>
<property name="password" value="verysecurepwd"/>
</properties>
<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="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25000"/>
<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>
<typeAliases><!--类型命名-->
<typeAlias alias="Tutor" type="com.mybatis3.domain.Tutor"/>
<package name="com.mybatis3.domain"/>
</typeAliases>
<typeHandlers><!--类型处理器-->
<typeHandler handler="com.mybatis3.typehandlers.PhoneTypeHandler"/>
<package name="com.mybatis3.typehandlers"/>
</typeHandlers>
<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="JNDI">
<property name="data_source" value="java:comp/jdbc/MyBatisDemoDS"/>
</dataSource>
</environment>
</environments>
<mappers><!--映射器-->
<mapper resource="com/mybatis3/mappers/StudentMapper.xml"/>
<mapper url="file:///var/mappers/StudentMapper.xml"/>
<mapper class="com.mybatis3.mappers.TutorMapper"/>
</mappers>
</configuration>
2.2properties
这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通
过 properties 元素的子元素来传递。例如:
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/></properties>
其中的属性就可以在整个配置文件中使用来替换需要动态配置的属性值。比如:
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/></dataSource>
这个例子中的 username 和 password 将会由 properties 元素中设置的相应值来替换。
driver 和 url 属性将会由 config.properties 文件中对应的值来替换。这样就为配置提供了诸多灵活选择。
属性也可以被传递到 SqlSessionFactoryBuilder.build()方法中。例如:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);
// ... or ...
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, props);
如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:
在 properties 元素体内指定的属性首先被读取。
然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性
指定的路径读取属性文件,并覆盖已读取的同名属性。
最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。
因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,
最低优先级的是 properties 属性中指定的属性。
2.3 设置
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。下表描述了设
置中各项的意图、默认值等。
2.4typeAliases 别名
因为我遇到的类的全限定名过长,所以我们希望用一个简短的名称去指代它,这个名称
可以 MyBatis 上下文中使用,
别名在 MyBatis 里分为系统定义别名和自定义别名两类,
别名不分大小写
一个 typeAliases 的实例是在解析配置文件时生成的,然后长期保存在 Configuration 对
象中,当我们使用它时,再把它拿出来,没有必要再次实例化
2.4.1 系统别名
MyBatis 系统定义了一些经常使用的类型别名,例如,数值、字符串、日期、集合等,我
们可以在 MyBatis 中直接使用它们,数组类型只要加[] 如 date[]
我们通过 MyBatis 的源码 org.apache.ibatis.type.TypeAliasRegistry 可以看出其自定义注册
的信息
已经为许多常见的 Java 类型内建了相应的类型别名。它们都是大小写不敏感的,需要
注意的是由基本类型名称重复导致的特殊处理。
2.4.2 自定义别名
当系统定义的别名不够用时,MyBatis 也允许自定义别名
类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用
来减少类完全限定名的冗余。例如:
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
</typeAliases>
当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="domain.blog"/></typeAliases>
每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小
写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,
则别名为其注解值。看下面的例子:
@Alias("author")
public class Author {
...}
2.5typeHandlers 类型处理器
MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,或者从结果集(ResultSet)
中取出一个值时,都会用注册了的 typeHandler 进行处理。
TypeHandler 和别名一样,分为 MyBatis 系统定义和用户自定义两种
typeHandler 的作用就是将参数从 javaType 转化为 jdbcType 或者从数据库取出结果时把
jdbcType 转化为 javaType
2.5.1 系统定义的 TypeHandler
在源码 org.apache.ibatis.type.TypeHandlerRegistry 中查看注册的系统 typeHandler
2.5.2 自定义 TypeHandler
你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。 具体
做 法 为 : 实 现 org.apache.ibatis.type.TypeHandler 接 口 , 或 继 承 一 个 很 便 利 的
类 org.apache.ibatis.type.BaseTypeHandler, 然后可以选择性地将它映射到一个 JDBC 类型。
比如:
// ExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType j
dbcType) throws SQLException {
ps.setString(i, parameter);
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getString(columnName);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getString(columnIndex);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLExceptio
n {
return cs.getString(columnIndex);
}}
public class PhoneNumber
{
private String countryCode;
private String stateCode;
private String number;
public PhoneNumber()
{
}
public PhoneNumber(String countryCode, String stateCode, String
number)
{
this.countryCode = countryCode;
this.stateCode = stateCode;
this.number = number;
}
public PhoneNumber(String string)
{
if(string != null)
{
String[] parts = string.split("-");
if(parts.length > 0) this.countryCode = parts[0];
if(parts.length > 1) this.stateCode = parts[1];
if(parts.length > 2) this.number = parts[2];
}
}
public String getAsString()
{
return countryCode + "-" + stateCode + "-" + number;
}
// Setters and getters
}
public class Student
{
private Integer id;
private String name;
private String email;
private PhoneNumber phone;
// Setters and getters
}
Xml Code:
<insert id="insertStudent" parameterType="Student">
insert into students(name,email,phone)
values(#{name},#{email},#{phone})
</insert>
这里,phone 参数需要传递给#{phone};而 phone 对象是 PhoneNumber 类型。然而,MyBatis 并不知道该怎样来处理这个类型的对象。
为了让 MyBatis 明白怎样处理这个自定义的 Java 对象类型,如 PhoneNumber,我们可以创建一个自定义的类型处理器,如下所示:
- MyBatis 提供了抽象类 BaseTypeHandler ,我们可以继承此类创建自定义类型处理器。
packagecom.mybatis3.typehandlers;
importjava.sql.CallableStatement;
importjava.sql.PreparedStatement;
importjava.sql.ResultSet;
importjava.sql.SQLException;
importorg.apache.ibatis.type.BaseTypeHandler;
importorg.apache.ibatis.type.JdbcType;
importcom.mybatis3.domain.PhoneNumber;
public class PhoneTypeHandler extends BaseTypeHandler<PhoneNumber>
{
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
PhoneNumber parameter, JdbcType jdbcType) throws
SQLException
{
ps.setString(i, parameter.getAsString());
}
@Override
public PhoneNumber getNullableResult(ResultSet rs, String
columnName)
throws SQLException
{
return new PhoneNumber(rs.getString(columnName));
}
@Override
public PhoneNumber getNullableResult(ResultSet rs, int
columnIndex)
throws SQLException
{
return new PhoneNumber(rs.getString(columnIndex));
}
@Override
public PhoneNumber getNullableResult(CallableStatement cs, int
columnIndex)
throws SQLException
{
return new PhoneNumber(cs.getString(columnIndex));
}
}
- 我们使用 ps.setString()和 rs.getString()方法是因为 phone 列是 VARCHAR 类型。
- 一旦我们实现了自定义的类型处理器,我们需要在 mybatis-config.xml 中注册它:
XML Code:
<?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="application.properties" />
<typeHandlers>
<typeHandler handler="com.mybatis3.typehandlers. PhoneTypeHandler" />
</typeHandlers>
</configuration>
注册 PhoneTypeHandler 后, MyBatis 就能够将 Phone 类型的对象值存储到 VARCHAR 类型
的列上。
2.6 枚举类型 TypeHandler
在 MyBatis 中枚举类型的 TypeHandler 则有自己特殊规则,MyBatis 内部提供了两个转化枚举
类型的 typeHandler 给我们使用
org.apache.ibatis.type.EnumTypeHandler
org.apache.ibatis.type.EnumOrdinalTypeHandler
其中 EnumTypeHandler 是使用枚举字符串名称作为参数传递的,EnumOrdinalTypeHandler 是使用整数下标作为参数传递的,如果枚举和数据库字典项保持一致,我们使用它们就可以。
然而这两个枚举类型应用却不是那么广泛,更多的时候我们希望使用自定义的 typeHandler处理它们,所以在这里我也会谈及自定义的 typeHandler 实现枚举映射。
注意:EnumTypeHandler 对应的是一个字符串,对应 varchar 型的字段项,EnumTypeHandler是通过 Enum.name 方法将其转化为字符串,通过 Enum.valueOf 方法将字符串转化为枚举的
示例:数据表中有一个字段 sex 为 int 类型
1. package com.pojo;
2. public enum Sex
3. {
4. MALE(1,"男"),FEMALE(2,"女");;
5. private int id;
6. private String name;
7. private Sex(int id,String name)
8. {
9. this.id=id;
10. this.name=name;
11. }
12. public int getId() {
13. return id;
14. }
15. public void setId(int id) {
16. this.id = id;
17. }
18. public String getName() {
19. return name;
20. }
21. public void setName(String name) {
22. this.name = name;
23. }
24. public static Sex getSex(int id)
25. {
26. if(id==1)
27. return MALE;
28. else if(id==2)
29. return FEMALE;
30. else
31. return null;
32. }
33. }
Mybatis-config.xml
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
javaType=”com.pojo.Sex”/>
<package name="com.mybatis3.typehandlers"/>
</typeHandlers>
Mapper.xml
<mapper namespace="com.mapper.StudentMapper">
<resultMap type="com.pojo.Students" id="stuMap">
<id column="stu_id" property="stu_id" javaType="long" jdbcType="BIGINT"/>
<result column="sex" property="sex"
typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
......................
</resultMap>
<insert id="insertStu" parameterType="com.pojo.Students">
insert into students(name,email,sex)
values(#{name},#{email},
#{sex,typeHandler=org.apache.ibatis.type.EnumOrdinaTypeHandler})
</insert>
</mapper>
2.7ObjectFactory
当 MyBatis 构建一个结果返回时,都会使用(ObjectFactory)去构建 POJO, 默认的对象工
厂(org.apache.ibatis.reflection.factory.DefaultObjectFactory)需要做的仅仅是实例化目标类,
要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。 如果想
覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。比如:
// ExampleObjectFactory.java
public class ExampleObjectFactory extends DefaultObjectFactory {
public Object create(Class type) {
return super.create(type);
}
public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructor
Args) {
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-config.xml -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
<property name="someProperty" value="100"/>
</objectFactory>
ObjectFactory 接口很简单,它包含两个创建用的方法,一个是处理默认构造方法的,另外一个是处理带参数的构造方法的。 最后,setProperties 方法可以被用来配置 ObjectFactory,在初始化你的 ObjectFactory 实例后, objectFactory 元素体中定义的属性会被传递给setProperties 方法。
2.8 插件
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 的发行包中的源代码。 假设你想做的不仅仅是监控方法的调用,那么你应该很好的了解正在重写的方法的行为。 因为如果在试图修改或重写已有方法的行为的时候,你很可能在破坏MyBatis 的核心模块。 这些都是更低层的类和方法,所以使用插件的时候要特别当心。通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定了想要拦截的方法签名即可。
// ExamplePlugin.java
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}}
<!-- mybatis-config.xml -->
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin></plugins>
2.9 配置环境(environments)
Environments 可以注册多个数据源(DataSource),每个数据源分为两大部分:一个是
数据源的配置,另外一个数据库事务(transactionManager)的配置。
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之
中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的
配置;或者共享相同 Schema 的多个生产数据库, 想使用相同的 SQL 映射。许多类
似的用例。
不过要记住:尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一。
所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单:每个数据库对应一个 SqlSessionFactory 实例,为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可。可以接受环境配置的两个方法签名是:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);SqlSes
sionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment,properties);
如果忽略了环境参数,那么默认环境将会被加载,如下所示:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);SqlSessionFactory f
actory = new SqlSessionFactoryBuilder().build(reader,properties);
环境元素定义了如何配置环境。
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</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>
Environments 中的属性 default 配置默认的数据源
Environment 元素是配置一数据源的开始,属性 id 设置数据源的标志,以便 mybatis 上
下文使用
transactionManager 配置数据库事务,
JDBC,采用 JDBC 方式管理事务
MANAGED,采用容器方式管理事务,在 JNDI 数据源中常用
自定义,由使用者定义数据库事务管理方法,适用于特殊应用
Property 配置数据源各属性,如 autoCommit=false
dataSource 标签配置数据源连接信息,type 属性是提供我们对数据库连接方式的配置,
type 方式如下:
UNPOOLED, 非 连 接 池 数 据 库 , 使 用 MyBatis 提 供 的
org.apache.ibatis.datasource.unpooled.UnpooledDataSource 实现
POOLED,连接池数据库,org.apache.ibatis.datasource.pooled.PooldDataSource 实现
JNDI,JNDI 数据源,org.apache.ibatis.datasource.jndiDataSourceFactory 来获取数据源
自定义,有时我们需要使用其它数据源,如果自定义数据源,必须实现
org.apache.ibatis.datasource.DataSourceFactory 接口,比如我们可能要用 DBCP 数据
源
注意:数据库事务 MyBatis 是交由 SqlSession 去控制的,我们可以通过 SqlSession 提交(commit)或者回滚(rollback)
2.9.1 自定义数据源
1. import java.sql.SQLFeatureNotSupportedException;
2. import java.util.Properties;
3. import java.util.logging.Logger;
4. import javax.sql.DataSource;
5. import org.apache.commons.dbcp.BasicDataSource;
6. import org.apache.commons.dbcp.BasicDataSourceFactory;
7. import org.apache.ibatis.datasource.DataSourceFactory;
8. public class DBCPDataSourceFactoryextends BasicDataSource implements DataSourceFactory
9. {
10. private Properties props=null;
11. @Override
12. public Logger getParentLogger() throws SQLFeatureNotSupportedException {
13.
14. return null;
15. }
16. @Override
17. public DataSource getDataSource() {
18. DataSource ds=null;
19. try {
20. ds=BasicDataSourceFactory.createDataSource(props);
21. } catch (Exception e) {
22. e.printStackTrace();
23. }
24. return ds;
25. }
26. @Override
27. public void setProperties(Properties arg0) {
28. this.props=arg0;
29.
30. }
31. }
<datasource type=”xxx.xxx.DBCPDataSourceFactory”/>
2.9.2JNDI 数据源
META-INF/context.xml:
<Context>
<Resource auth="Container"
description="DB Connection"
driverClass="com.mysql.jdbc.Driver"
maxPoolSize="4"
minPoolSize="2"
acquireIncrement="1"
name="jdbc/test"
user="root"
password="mysql"
factory="org.apache.naming.factory.BeanFactory"
type="com.mchange.v2.c3p0.ComboPooledDataSource"
jdbcUrl="jdbc:mysql://localhost:3306/mybatis" />
</Context>
MyBatis-config.xml
...
<dataSource type="JNDI">
<property name="initial_context" value="java:comp/env"/>
<property name="data_source" value="jdbc/test"/>
</dataSource>
...
2.10databaseIdProvider
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中
的 databaseId 属 性 。 MyBatis 会 加 载 不 带 databaseId 属 性 和 带 有 匹 配 当 前 数 据
库 databaseId 属性的所有语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语
句,则后者会被舍弃。 为支持多厂商特性只要像下面这样在 mybatis-config.xml 文件中加
入 databaseIdProvider 即可:
这里的 DB_VENDOR 会通过 DatabaseMetaData#getDatabaseProductName() 返回的字符串进
行设置。 由于通常情况下这个字符串都非常长而且相同产品的不同版本会返回不同的值,
所以最好通过设置属性别名来使其变短,如下:
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" /></databaseIdProvider>
在有 properties 时,DB_VENDOR databaseIdProvider 的将被设置为第一个能匹配数据库产品
名称的属性键对应的值,如果没有匹配的属性将会设置为 “null”。 在这个例子中,如
果 getDatabaseProductName() 返回“Oracle (DataDirect)”,databaseId 将被设置为“oracle”。
你可以通过实现接口 org.apache.ibatis.mapping.DatabaseIdProvider 并在 mybatis-config.xml
中注册来构建自己的 DatabaseIdProvider:
public interface DatabaseIdProvider {
void setProperties(Properties p);
String getDatabaseId(DataSource dataSource) throws SQLException;}
2.11 映射器(mappers)
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要定义 SQL 映射语句了。但是
首先我们需要告诉 MyBatis 到哪里去找到这些语句。Java 在自动查找这方面没有提供一个
很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。你可以使用相对于类
路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。例如:
<!-- Using classpath relative resources -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- Using url fully qualified paths -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- Using mapper interface classes -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- Register all interfaces in a package as mappers -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
这些配置会告诉了 MyBatis 去哪里找映射文件,剩下的细节就应该是每个 SQL 映射文件
了,也就是接下来我们要讨论的。