共计 4201 个字符,预计需要花费 11 分钟才能阅读完成。
在日常的 Java 开发中,我们经常需要将复杂对象(如 Java Bean、List、Map)序列化为 JSON 字符串存入数据库,或者将文件、图像等数据序列化为二进制(BLOB)存储。这些类型的数据并不能被 MyBatis 默认识别,因此需要借助一个非常关键的机制 ——
TypeHandler
。本文将从原理到实战,全面剖析 TypeHandler 在处理 JSON 与 BLOB 数据中的应用与技巧。
一、什么是 TypeHandler?
定义与本质
TypeHandler
是 MyBatis 提供的一个扩展点,作用是在 Java 类型与 JDBC 类型之间做双向转换,它在以下两个时机被调用:
- 写入数据库时(Java → JDBC):将 Java 对象转换为数据库可识别的类型;
- 读取数据库时(JDBC → Java):将数据库字段值转换为 Java 对象。
换句话说,TypeHandler 就是 MyBatis 中的数据“编解码器”,隐藏了底层 JDBC 与 Java 类型之间的映射细节。
接口结构
MyBatis 定义了一个 org.apache.ibatis.type.TypeHandler<T>
接口,核心方法如下:
public interface TypeHandler<T> {
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
其中:
setParameter
:用于将 Java 对象设置到PreparedStatement
中;getResult
:从ResultSet
或CallableStatement
中读取字段值并转为 Java 对象。
注册方式
MyBatis 提供了三种方式注册 TypeHandler:
全局扫描(推荐)
在配置文件中配置包扫描路径:
mybatis:
type-handlers-package: com.example.handler
Mapper XML 中显式指定
<result column="config_json" property="config"
typeHandler="com.example.handler.JsonTypeHandler"/>
注解中指定
@Result(column = "config_json", property = "config",
typeHandler = JsonTypeHandler.class)
手动注册(适用于泛型/构造函数注册)
configuration.getTypeHandlerRegistry().register(UserConfig.class, new JsonTypeHandler<>(UserConfig.class));
二、MyBatis 默认支持的类型有哪些?
MyBatis 自带了一套常用类型的处理器:
Java 类型 | JDBC 类型 | 默认 TypeHandler |
---|---|---|
String | VARCHAR | StringTypeHandler |
Integer / int | INTEGER | IntegerTypeHandler |
Long / long | BIGINT | LongTypeHandler |
Boolean / boolean | BIT | BooleanTypeHandler |
byte[] | BLOB | ByteArrayTypeHandler |
Date / Timestamp | DATE / TIMESTAMP | DateTypeHandler , DateOnlyTypeHandler 等 |
当你使用 Java Bean、Map、List 等复杂结构时,就需要自定义 TypeHandler 来处理了。
三、场景一:处理 JSON 字段
1. 应用背景
数据库字段 config_json
为 TEXT
/ JSON
类型,存储用户个性化设置:
{
"darkMode": true,
"favorites": ["java", "spring", "mybatis"]
}
Java 对应结构:
public class UserConfig {
private boolean darkMode;
private List<String> favorites;
// getter/setter
}
2. 编写通用 JsonTypeHandler(支持泛型)
@MappedTypes(Object.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class JsonTypeHandler<T> extends BaseTypeHandler<T> {
private static final ObjectMapper objectMapper = new ObjectMapper();
private final Class<T> clazz;
public JsonTypeHandler(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)
throws SQLException {
ps.setString(i, objectMapper.writeValueAsString(parameter));
}
@Override
public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
String json = rs.getString(columnName);
return json == null ? null : objectMapper.readValue(json, clazz);
}
@Override
public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return getNullableResult(rs, rs.getMetaData().getColumnLabel(columnIndex));
}
@Override
public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return getNullableResult(cs.getResultSet(), columnIndex);
}
}
3. 注册与使用方式
Spring Boot 配置注册泛型 Handler:
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration ->
configuration.getTypeHandlerRegistry()
.register(UserConfig.class, new JsonTypeHandler<>(UserConfig.class));
}
四、场景二:处理 BLOB 对象(二进制序列化)
1. 应用背景
希望将 Java 对象(如临时缓存、上传数据)存入数据库 BLOB 字段,自动序列化和反序列化。
2. 编写对象序列化 TypeHandler
public class ObjectToBlobTypeHandler<T extends Serializable> extends BaseTypeHandler<T> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)
throws SQLException {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(parameter);
ps.setBytes(i, bos.toByteArray());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
return deserialize(rs.getBytes(columnName));
}
private T deserialize(byte[] bytes) {
if (bytes == null) return null;
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) {
return (T) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
五、总结与最佳实践
应用场景 | 数据库字段类型 | Java 映射类型 | 是否需要自定义 TypeHandler | 处理方式建议 |
---|---|---|---|---|
JSON 配置 | JSON/TEXT | Java Bean / Map | ✅ 是 | 使用通用 JsonTypeHandler |
文件 / 图片 / 音频 | BLOB | byte[] | ❌ 否(MyBatis 已支持) | 使用 ByteArrayTypeHandler |
对象序列化持久化 | BLOB | Java Bean | ✅ 是 | 使用 ObjectToBlobTypeHandler |
六、结语
TypeHandler
是 MyBatis 中非常强大的一项机制,无论是处理复杂对象的 JSON 映射,还是处理二进制文件或缓存数据的存储,合理使用 TypeHandler 都能让你的数据访问层更具扩展性、解耦性和可维护性。
我是 李卷卷,专注Java相关知识输出。感谢阅读!