MyBatis 的 类型系统(Type System) 是框架处理 Java 类型与数据库类型之间映射的核心模块,它通过 类型处理器(TypeHandler)、类型别名(TypeAlias) 和 类型转换器 等机制,实现了数据库字段与 Java 对象属性的无缝转换。以下是其核心功能、使用场景及实现原理的详解:
一、类型系统的核心组件
组件 | 作用 |
| 处理 Java 类型与 JDBC 类型之间的转换(如 |
| 为 Java 类型定义别名,简化 XML 配置中的类型名称。 |
| 全局注册所有 |
| 创建结果集映射的 Java 对象实例(如 POJO、集合等)。 |
二、类型处理器(TypeHandler)
1. 功能与职责
- 双向转换:
-
- 写入数据库:将 Java 对象属性转换为 JDBC 参数(
PreparedStatement.setXxx
)。 - 读取数据库:将 JDBC 结果集(
ResultSet.getXxx
)转换为 Java 对象属性。
- 写入数据库:将 Java 对象属性转换为 JDBC 参数(
- 支持复杂类型:
-
- 枚举、集合、自定义对象、JSON 字符串等。
2. 内置 TypeHandler
MyBatis 默认注册了常见类型的处理器,例如:
Java 类型 | JDBC 类型 | 对应 TypeHandler |
|
|
|
|
|
|
|
|
|
|
|
|
枚举类 |
|
|
枚举类(按序数存储) |
|
|
3. 自定义 TypeHandler
当默认处理器无法满足需求时(如处理 JSON 字段),可自定义 TypeHandler
。
示例:将 Java 对象序列化为 JSON 字符串存入数据库
// 1. 实现 TypeHandler 接口
@MappedTypes(User.class) // 指定处理的 Java 类型
@MappedJdbcTypes(JdbcType.VARCHAR) // 指定对应的 JDBC 类型
public class JsonTypeHandler implements TypeHandler<User> {private final ObjectMapper objectMapper = new ObjectMapper();// 写入数据库时,将 User 对象转为 JSON 字符串@Overridepublic void setParameter(PreparedStatement ps, int i, User parameter, JdbcType jdbcType) throws SQLException {try {String json = objectMapper.writeValueAsString(parameter);ps.setString(i, json);} catch (JsonProcessingException e) {throw new SQLException("JSON 序列化失败", e);}}// 从数据库读取时,将 JSON 字符串转为 User 对象@Overridepublic User getResult(ResultSet rs, String columnName) throws SQLException {String json = rs.getString(columnName);return parseJson(json);}// 其他重载方法(如 getResult(ResultSet rs, int columnIndex))// ...private User parseJson(String json) {try {return objectMapper.readValue(json, User.class);} catch (JsonProcessingException e) {throw new RuntimeException("JSON 解析失败", e);}}
}
注册自定义 TypeHandler:
<!-- mybatis-config.xml -->
<typeHandlers><typeHandler handler="com.example.JsonTypeHandler"/>
</typeHandlers>
在 Mapper 中使用:
<resultMap id="userResultMap" type="User"><result column="json_data" property="data" typeHandler="com.example.JsonTypeHandler"/>
</resultMap>
三、类型别名(TypeAlias)
1. 功能
- 简化配置:为长类名定义短别名,避免 XML 中重复书写全限定类名。
- 提升可读性:例如将
java.util.List
别名为list
。
2. 使用方式
方式一:XML 配置
<!-- mybatis-config.xml -->
<typeAliases><typeAlias type="com.example.User" alias="User"/><package name="com.example.dto"/> <!-- 自动扫描包下所有类,别名为首字母小写的类名 -->
</typeAliases>
方式二:注解配置
@Alias("User") // 在类上添加注解
public class User { ... }
在 Mapper 中使用别名:
<select id="getUser" resultType="User"> <!-- 直接使用别名 -->SELECT * FROM users WHERE id = #{id}
</select>
四、类型处理器注册表(TypeHandlerRegistry)
1. 职责
- 全局管理 TypeHandler:维护
Java类型 ↔ JDBC类型 ↔ TypeHandler
的映射关系。 - 自动发现机制:通过
<typeHandlers>
配置或扫描包路径注册处理器。
2. 优先级规则
当多个 TypeHandler 可处理同一类型时,按以下顺序选择:
- 显式指定
typeHandler
属性的处理器。 - 注解
@MappedTypes
和@MappedJdbcTypes
精确匹配的处理器。 - 默认注册的处理器(如
StringTypeHandler
)。
五、常见应用场景
1. 处理枚举类型
- 按名称存储(默认):使用
EnumTypeHandler
,将枚举的name()
存入数据库。 - 按序数存储:使用
EnumOrdinalTypeHandler
,将枚举的ordinal()
存入数据库。 - 自定义存储逻辑:实现
TypeHandler
接口,例如将枚举转换为特定代码值。
2. 处理复杂类型
- JSON 字段:如上述
JsonTypeHandler
示例。 - 加密字段:自定义处理器,在写入时加密、读取时解密敏感数据(如手机号、身份证号)。
3. 处理集合类型
- 默认支持:MyBatis 内置
ListTypeHandler
、MapTypeHandler
,但通常直接通过resultMap
映射集合属性,无需手动处理。
六、最佳实践
1. 合理使用类型别名
- 统一管理:在
mybatis-config.xml
中集中定义别名,避免分散配置。 - 避免冲突:确保不同包下的类别名唯一,或直接使用全限定类名。
2. 自定义 TypeHandler 的注意事项
- 线程安全:确保
TypeHandler
无状态或使用线程安全的数据结构(如上述ObjectMapper
可复用)。 - 异常处理:捕获并转换异常为
SQLException
,避免框架层面崩溃。
3. 性能优化
- 缓存复杂对象:若频繁解析 JSON 或加密数据,可添加缓存层(如
ConcurrentHashMap
)。 - 避免过度自定义:优先使用 MyBatis 内置处理器,减少不必要的复杂性。
七、总结
MyBatis 的类型系统通过 类型处理器 和 类型别名 等机制,屏蔽了 Java 对象与数据库类型之间的差异,使开发者能够专注于业务逻辑。通过合理使用内置功能并扩展自定义 TypeHandler
,可以高效处理复杂数据类型,提升代码可维护性和灵活性。