文章目录
- 前言
-
-
- [1. 什么是 JacksonTypeHandler](#1. 什么是 JacksonTypeHandler)
- [2. 核心作用](#2. 核心作用)
- [3. 工作原理剖析](#3. 工作原理剖析)
-
- [3.1 整体架构](#3.1 整体架构)
- [3.2 序列化过程(Java → JSON)](#3.2 序列化过程(Java → JSON))
- [3.3 反序列化过程(JSON → Java)](#3.3 反序列化过程(JSON → Java))
- [4. 核心代码实现](#4. 核心代码实现)
- [5. 支持的 Java 类型](#5. 支持的 Java 类型)
- [6. 数据库字段类型](#6. 数据库字段类型)
- [7. 配置方式](#7. 配置方式)
-
- 方式一:字段级别(最常用)
- [方式二:全局配置(MyBatis-Plus 配置文件)](#方式二:全局配置(MyBatis-Plus 配置文件))
- [方式三:注解 + 泛型](#方式三:注解 + 泛型)
- [8. 实际应用示例](#8. 实际应用示例)
- [9. 优势与注意事项](#9. 优势与注意事项)
- 总结
-
前言
1. 什么是 JacksonTypeHandler
JacksonTypeHandler 是 MyBatis-Plus 提供的一个类型处理器 ,专门用于处理 JSON 类型字段与 Java 对象之间的转换。
java
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<String, Object> extraFields;
2. 核心作用
| 方向 | 转换过程 | 场景 |
|---|---|---|
| 序列化 | Java Map<String, Object> → JSON String |
插入/更新时,将对象转为数据库可存储的 JSON |
| 反序列化 | JSON String → Java Map<String, Object> |
查询时,将数据库的 JSON 转为 Java 对象 |
3. 工作原理剖析
3.1 整体架构
js
┌─────────────────────────────────────────────────────────────────────────┐
│ MyBatis 执行流程 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 写入流程 (Insert/Update): │
│ ┌──────────────┐ ┌─────────────────────┐ ┌──────────────┐ │
│ │ Java Object │───▶│ JacksonTypeHandler │───▶│ JSON String │ │
│ │ (Map<String, │ │ .setParameter() │ │ (存入数据库) │ │
│ │ Object>) │ │ (序列化) │ │ │ │
│ └──────────────┘ └─────────────────────┘ └──────────────┘ │
│ │
│ 读取流程 (Select): │
│ ┌──────────────┐ ┌─────────────────────┐ ┌──────────────┐ │
│ │ JSON String │───▶│ JacksonTypeHandler │───▶│ Java Object │ │
│ │ (从数据库取)│ │ .getResult() │ │ (Map<String, │ │
│ │ │ │ (反序列化) │ │ Object>) │ │
│ └──────────────┘ └─────────────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
3.2 序列化过程(Java → JSON)
当执行 INSERT 或 UPDATE 时:
java
// 1. 应用层准备数据
Map<String, Object> extraFields = new HashMap<>();
extraFields.put("projectName", "车间扩建工程");
extraFields.put("startDate", "2024-06-01");
extraFields.put("projectScale", "20万元以上");
// 2. MyBatis 调用 TypeHandler
// JacksonTypeHandler.setParameter() 内部:
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(extraFields);
// 结果: "{\"projectName\":\"车间扩建工程\",\"startDate\":\"2024-06-01\",\"projectScale\":\"20万元以上\"}"
// 3. 写入数据库字段(通常是 VARCHAR 或 JSON 类型)
3.3 反序列化过程(JSON → Java)
当执行 SELECT 时:
java
// 1. 从数据库读取 JSON 字符串
String jsonString = "{\"projectName\":\"车间扩建工程\",\"startDate\":\"2024-06-01\"}";
// 2. MyBatis 调用 TypeHandler
// JacksonTypeHandler.getResult() 内部:
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> extraFields = mapper.readValue(jsonString,
new TypeReference<Map<String, Object>>() {});
// 结果: {projectName="车间扩建工程", startDate="2024-06-01"}
// 3. 返回给应用层使用
4. 核心代码实现
MyBatis-Plus 的 JacksonTypeHandler 核心逻辑:
java
public class JacksonTypeHandler<T> extends BaseTypeHandler<T> {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private final Class<T> type;
@Override
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType)
throws SQLException {
if (parameter == null) {
ps.setNull(i, jdbcType.TYPE_CODE);
return;
}
// 序列化:Java对象 → JSON字符串
String json = OBJECT_MAPPER.writeValueAsString(parameter);
ps.setString(i, json);
}
@Override
public T getResult(ResultSet rs, String columnName) throws SQLException {
String json = rs.getString(columnName);
if (json == null) {
return null;
}
// 反序列化:JSON字符串 → Java对象
return OBJECT_MAPPER.readValue(json, type);
}
// ... 其他 getResult 重载方法
}
5. 支持的 Java 类型
JacksonTypeHandler 不仅支持 Map<String, Object>,还支持多种类型:
| Java 类型 | 说明 | 示例 |
|---|---|---|
Map<String, Object> |
通用键值对 | {"key": "value"} |
List<T> |
列表 | ["a", "b", "c"] |
自定义对象 |
POJO | {"name": "张三", "age": 25} |
任意复杂类型 |
嵌套结构 | {"data": {"list": [1, 2, 3]}} |
6. 数据库字段类型
数据库中对应字段通常使用以下类型:
| 数据库 | 推荐字段类型 |
|---|---|
| MySQL | JSON 或 VARCHAR(2000) |
| PostgreSQL | jsonb |
| SQL Server | NVARCHAR(MAX) |
7. 配置方式
方式一:字段级别(最常用)
java
public class SrmProjectInitiationDetailDO {
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<String, Object> extraFields;
}
方式二:全局配置(MyBatis-Plus 配置文件)
yaml
mybatis-plus:
configuration:
type-handlers-package: com.baomidou.mybatisplus.extension.handlers
global-config:
db-config:
type-handler-cache: true
方式三:注解 + 泛型
java
// 如果需要指定具体类型,可以这样写
@TableField(typeHandler = JacksonTypeHandler.class)
private List<ExtraField> extraFields; // ExtraField 是自定义类
8. 实际应用示例
场景:存储动态表单数据
java
// 实体类
public class FormData {
private Long id;
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<String, Object> formData; // 存储动态表单字段
}
// 使用
FormData form = new FormData();
Map<String, Object> data = new HashMap<>();
data.put("username", "zhangsan");
data.put("age", 25);
data.put("address", Map.of("city", "Beijing", "street", "Main St"));
form.setFormData(data);
formMapper.insert(form);
// 数据库存储: {"username":"zhangsan","age":25,"address":{"city":"Beijing","street":"Main St"}}
// 查询
FormData result = formMapper.selectById(1L);
String username = (String) result.getFormData().get("username");
Integer age = (Integer) result.getFormData().get("age");
9. 优势与注意事项
优势
- ✅ 自动转换:无需手动序列化/反序列化
- ✅ 类型安全:编译期类型检查
- ✅ 灵活性:支持任意 JSON 结构
- ✅ 性能优秀:基于 Jackson 高性能序列化框架
注意事项
- ⚠️ 类型转换 :读取时需手动进行类型转换(如
(String) map.get("key")) - ⚠️ 字段变更:JSON 结构变更不会影响数据库表结构
- ⚠️ 查询限制:复杂查询可能需要数据库原生 JSON 函数支持
总结
JacksonTypeHandler 的核心价值在于:
- 桥接作用:无缝连接 Java 对象和数据库 JSON 字段
- 自动转换:序列化和反序列化完全自动化
- 灵活性:支持任意复杂的 JSON 结构存储
对于 extraFields 这种需要动态存储不同类型字段的场景,使用 Map<String, Object> + JacksonTypeHandler 是非常优雅的解决方案。