MyBatis-Plus -JacksonTypeHandler 原理详解

文章目录

  • 前言
      • [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)

当执行 INSERTUPDATE 时:

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 JSONVARCHAR(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 的核心价值在于:

  1. 桥接作用:无缝连接 Java 对象和数据库 JSON 字段
  2. 自动转换:序列化和反序列化完全自动化
  3. 灵活性:支持任意复杂的 JSON 结构存储

对于 extraFields 这种需要动态存储不同类型字段的场景,使用 Map<String, Object> + JacksonTypeHandler 是非常优雅的解决方案。