MyBatis 官方文档 -XML 配置
| 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
|
你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。 具体做法为:实现 org.apache.ibatis.type.TypeHandler
接口, 或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler
, 然后可以选择性地将它映射到一个 JDBC 类型。比如:
// ExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, Jdbc
Type jdbcType) 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 SQLException {
return cs.getString(columnIndex);
}
}
<typeHandlers>
<typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>
使用上述的类型处理器将会覆盖已经存在的处理 Java 的 String 类型属性和 VARCHAR 参数及结果的类型处理器。 要注意 MyBatis 不会通过窥探数据库元信息来决定使用哪种类型,所以你必须在参数和结果映射中指明那是 VARCHAR 类型的字段, 以使其能够绑定到正确的类型处理器上。这是因为 MyBatis 直到语句被执行时才清楚数据类型。
通过类型处理器的泛型,MyBatis 可以得知该类型处理器处理的 Java 类型,不过这种行为可以通过两种方法改变:
在类型处理器的配置元素(typeHandler 元素)上增加一个
javaType
属性(比如:javaType="String"
);在类型处理器的类上(TypeHandler class)增加一个
@MappedTypes
注解来指定与其关联的 Java 类型列表。 如果在javaType
属性中也同时指定,则注解方式将被忽略。
可以通过两种方式来指定被关联的 JDBC 类型:
在类型处理器的配置元素上增加一个
jdbcType
属性(比如:jdbcType="VARCHAR"
);在类型处理器的类上增加一个
@MappedJdbcTypes
注解来指定与其关联的 JDBC 类型列表。 如果在jdbcType
属性中也同时指定,则注解方式将被忽略。
当在 ResultMap
中决定使用哪种类型处理器时,此时 Java 类型是已知的(从结果类型中获得),但是 JDBC 类型是未知的。 因此 Mybatis 使用 javaType=[Java 类型], jdbcType=null
的组合来选择一个类型处理器。 这意味着使用 @MappedJdbcTypes
注解可以_限制_类型处理器的范围,同时除非显式的设置,否则类型处理器在 ResultMap
中将是无效的。 如果希望在 ResultMap
中使用类型处理器,那么设置 @MappedJdbcTypes
注解的 includeNullJdbcType=true
即可。 然而从 Mybatis 3.4.0 开始,如果只有一个注册的类型处理器来处理 Java 类型,那么它将是 ResultMap
使用 Java 类型时的默认值(即使没有 includeNullJdbcType=true
)。
最后,可以让 MyBatis 为你查找类型处理器:
<typeHandlers>
<package name="org.mybatis.example"/>
</typeHandlers>
注意在使用自动发现功能的时候,只能通过注解方式来指定 JDBC 的类型。
你可以创建一个能够处理多个类的泛型类型处理器。为了使用泛型类型处理器, 需要增加一个接受该类的 class 作为参数的构造器,这样在构造一个类型处理器的时候 MyBatis 就会传入一个具体的类。
//GenericTypeHandler.java
public class GenericTypeHandler<E extends MyObject> extends BaseTypeHandler<E> {
private Class<E> type;
public GenericTypeHandler(Class<E> type) {
if (type == null) throw new IllegalArgumentException("Type argument cannot be null");
this.type = type;
}
...
EnumTypeHandler
和 EnumOrdinalTypeHandler
都是泛型类型处理器,我们将会在接下来的部分详细探讨。
处理枚举类型
若想映射枚举类型 Enum
,则需要从 EnumTypeHandler
或者 EnumOrdinalTypeHandler
中选一个来使用。
比如说我们想存储取近似值时用到的舍入模式。默认情况下,MyBatis 会利用 EnumTypeHandler
来把 Enum
值转换成对应的名字。
注意 EnumTypeHandler 在某种意义上来说是比较特别的,其他的处理器只针对某个特定的类,而它不同,它会处理任意继承了 Enum 的类。
不过,我们可能不想存储名字,相反我们的 DBA 会坚持使用整形值代码。那也一样轻而易举: 在配置文件中把 EnumOrdinalTypeHandler
加到 typeHandlers
中即可, 这样每个 RoundingMode
将通过他们的序数值来映射成对应的整形数值。
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="java.math.RoundingMode"/>
</typeHandlers>
但是怎样能将同样的 Enum
既映射成字符串又映射成整形呢?
自动映射器(auto-mapper)会自动地选用 EnumOrdinalTypeHandler
来处理, 所以如果我们想用普通的 EnumTypeHandler
,就必须要显式地为那些 SQL 语句设置要使用的类型处理器。
(下一节才开始介绍映射器文件,如果你是首次阅读该文档,你可能需要先跳过这里,过会再来看。)
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.apache.ibatis.submitted.rounding.Mapper">
<resultMap type="org.apache.ibatis.submitted.rounding.User" id="usermap">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="funkyNumber" property="funkyNumber"/>
<result column="roundingMode" property="roundingMode"/>
</resultMap>
<select id="getUser" resultMap="usermap">
select * from users
</select>
<insert id="insert">
insert into users (id, name, funkyNumber, roundingMode) values (
#{id}, #{name}, #{funkyNumber}, #{roundingMode}
)
</insert>
<resultMap type="org.apache.ibatis.submitted.rounding.User" id="usermap2">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="funkyNumber" property="funkyNumber"/>
<result column="roundingMode" property="roundingMode" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/>
</resultMap>
<select id="getUser2" resultMap="usermap2">
select * from users2
</select>
<insert id="insert2">
insert into users2 (id, name, funkyNumber, roundingMode) values (
#{id}, #{name}, #{funkyNumber}, #{roundingMode, typeHandler=org.apache.ibatis.type.EnumTypeHandler}
)
</insert>
</mapper>
注意,这里的 select 语句强制使用 resultMap
来代替 resultType
。
插件(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 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为如果在试图修改或重写已有方法的行为的时候,你很可能在破坏 MyBatis 的核心模块。 这些都是更低层的类和方法,所以使用插件的时候要特别当心。
通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。
// ExamplePlugin.java
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
private Properties properties = new Properties();
public Object intercept(Invocation invocation) throws Throwable {
// implement pre processing if need
Object returnObject = invocation.proceed();
// implement post processing if need
return returnObject;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor 是负责执行低层映射语句的内部对象。
提示 覆盖配置类
除了用插件来修改 MyBatis 核心行为之外,还可以通过完全覆盖配置类来达到目的。只需继承后覆盖其中的每个方法,再把它传递到 SqlSessionFactoryBuilder.build(myConfig) 方法即可。再次重申,这可能会严重影响 MyBatis 的行为,务请慎之又慎。
数据库厂商标识(databaseIdProvider)
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId
属性。 MyBatis 会加载不带 databaseId
属性和带有匹配当前数据库 databaseId
属性的所有语句。 如果同时找到带有 databaseId
和不带 databaseId
的相同语句,则后者会被舍弃。 为支持多厂商特性只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvider
即可:
<databaseIdProvider type="DB_VENDOR" />
DB_VENDOR 对应的 databaseIdProvider 实现会将 databaseId 设置为 DatabaseMetaData#getDatabaseProductName()
返回的字符串。 由于通常情况下这些字符串都非常长而且相同产品的不同版本会返回不同的值,所以你可能想通过设置属性别名来使其变短,如下:
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
</databaseIdProvider>
在提供了属性别名时,DB_VENDOR 的 databaseIdProvider 实现会将 databaseId 设置为第一个数据库产品名与属性中的名称相匹配的值,如果没有匹配的属性将会设置为 “null”。 在这个例子中,如果 getDatabaseProductName()
返回“Oracle (DataDirect)”,databaseId 将被设置为“oracle”。
你可以通过实现接口 org.apache.ibatis.mapping.DatabaseIdProvider
并在 mybatis-config.xml 中注册来构建自己的 DatabaseIdProvider:
public interface DatabaseIdProvider {
default void setProperties(Properties p) { // Since 3.5.2, change to default method
// NOP
}
String getDatabaseId(DataSource dataSource) throws SQLException;
}
映射器(mappers)
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要定义 SQL 映射语句了。 但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。 Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:///
的 URL),或类名和包名等。例如:
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
评论