MySQL 与 Java 的 JSON 数据处理

MySQL 与 Java 的 JSON 数据处理

系统梳理 JSON 在 MySQL 和 Java 中的增删改查,覆盖 Hutool JSON、Fastjson、Jackson 三种主流工具,全部用独立示例,方便理解。


目录

  1. [MySQL JSON 增删改查](#MySQL JSON 增删改查)
  2. [Hutool JSON 增删改查](#Hutool JSON 增删改查)
  3. [Fastjson 增删改查](#Fastjson 增删改查)
  4. [Jackson 增删改查](#Jackson 增删改查)
  5. [MyBatis TypeHandler](#MyBatis TypeHandler)
  6. 三种工具对比

1. MySQL JSON 增删改查

1.1 建表 + 插入

sql 复制代码
-- 建表:定义JSON字段
CREATE TABLE sys_config (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    config_key VARCHAR(100) NOT NULL,
    config_value JSON NOT NULL COMMENT 'JSON配置',
    gmt_create DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 插入JSON数据
INSERT INTO sys_config (config_key, config_value) VALUES
('sms_config', '{"provider":"aliyun","signName":"小租","templateCode":"SMS_123"}');

-- 用JSON_OBJECT构造
INSERT INTO sys_config (config_key, config_value) VALUES
('pay_config', JSON_OBJECT('appId','xxx','mchId','123'));

-- 嵌套结构
INSERT INTO sys_config (config_key, config_value) VALUES
('auth', JSON_OBJECT(
    'menu', JSON_ARRAY('用户管理','订单管理','财务'),
    'button', JSON_OBJECT('add',true,'delete',false)
));

1.2 读取JSON

sql 复制代码
-- 基本读取(->> 返回文本,去掉引号)
SELECT config_value->>'$.provider' FROM sys_config WHERE config_key='sms_config';
-- 结果:aliyun

-- -> 返回JSON类型(保留引号)
SELECT config_value->'$.provider' FROM sys_config WHERE config_key='sms_config';
-- 结果:"aliyun"

-- 嵌套字段
SELECT config_value->>'$.button.add' FROM sys_config WHERE config_key='auth';
-- 结果:true

-- 数组元素(下标从0开始)
SELECT JSON_EXTRACT(config_value, '$.menu[0]') FROM sys_config WHERE config_key='auth';
-- 结果:"用户管理"

1.3 修改JSON

sql 复制代码
-- JSON_SET:更新已有字段 / 新增不存在的字段
UPDATE sys_config
SET config_value = JSON_SET(config_value, '$.provider', 'tencent')
WHERE config_key = 'sms_config';

-- JSON_INSERT:只新增,不更新已存在的字段
UPDATE sys_config
SET config_value = JSON_INSERT(config_value, '$.newField', 'hello')
WHERE config_key = 'sms_config';

-- JSON_REPLACE:只更新,不新增
UPDATE sys_config
SET config_value = JSON_REPLACE(config_value, '$.provider', 'new_provider')
WHERE config_key = 'sms_config';

-- 数组追加元素
UPDATE sys_config
SET config_value = JSON_ARRAY_APPEND(config_value, '$.menu', '新菜单')
WHERE config_key = 'auth';

1.4 删除JSON字段

sql 复制代码
-- 删除对象字段
UPDATE sys_config
SET config_value = JSON_REMOVE(config_value, '$.email')
WHERE config_key = 'sms_config';

-- 删除数组元素(下标1的元素)
UPDATE sys_config
SET config_value = JSON_REMOVE(config_value, '$.menu[1]')
WHERE config_key = 'auth';

-- 同时删除多个字段
UPDATE sys_config
SET config_value = JSON_REMOVE(config_value, '$.phone', '$.email')
WHERE config_key = 'sms_config';

1.5 条件查询

sql 复制代码
-- 判断数组是否包含某个值
SELECT * FROM sys_config
WHERE JSON_CONTAINS(config_value, '"用户管理"', '$.menu');

-- 数组长度
SELECT * FROM sys_config
WHERE JSON_LENGTH(config_value, '$.menu') > 2;

-- 按JSON字段值过滤
SELECT * FROM sys_config
WHERE config_value->>'$.provider' = 'aliyun';

1.6 函数速查

函数 作用
-> / ->> 读取字段(->返回JSON,->>返回文本)
JSON_EXTRACT 提取值,等价于 ->
JSON_SET 更新/新增字段(都生效)
JSON_INSERT 只新增不更新
JSON_REPLACE 只更新不新增
JSON_REMOVE 删除字段/数组元素
JSON_ARRAY_APPEND 数组末尾追加
JSON_ARRAY_INSERT 数组指定位置插入
JSON_CONTAINS 判断是否包含某个值
JSON_LENGTH 获取数组长度
JSON_OBJECT 构造JSON对象
JSON_ARRAY 构造JSON数组

2. Hutool JSON 增删改查

Hutool 的 cn.hutool.json 包,API简洁,链式调用方便。

2.1 创建

java 复制代码
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;

// 方式1:new JSONObject()
JSONObject obj = new JSONObject();
obj.set("name", "张三");
obj.set("age", 25);

// 方式2:链式调用(fluentPut)
JSONObject obj2 = new JSONObject()
    .fluentPut("name", "张三")
    .fluentPut("age", 25)
    .fluentPut("address", new JSONObject()
        .fluentPut("city", "北京"))
    .fluentPut("hobbies", new JSONArray()
        .fluentPut("编程")
        .fluentPut("跑步"));
// {"name":"张三","age":25,"address":{"city":"北京"},"hobbies":["编程","跑步"]}

// 方式3:从字符串解析
JSONObject obj3 = JSONUtil.parseObj("{\"name\":\"张三\"}");

2.2 读取

java 复制代码
JSONObject obj = JSONUtil.parseObj(
    "{\"name\":\"张三\",\"age\":25,\"address\":{\"city\":\"北京\"}}"
);

// 基本字段
String name = obj.getStr("name");              // "张三"
int age = obj.getInt("age");                   // 25
String phone = obj.getStr("phone", "");        // 不存在返回默认值

// 嵌套字段
String city = obj.getJSONObject("address").getStr("city");  // "北京"

// 判断字段是否存在
boolean has = obj.containsKey("name");         // true

2.3 增/改

java 复制代码
JSONObject obj = JSONUtil.parseObj("{\"name\":\"张三\",\"age\":25}");

// set():存在则更新,不存在则新增
obj.set("age", 26);                           // 更新 → 26
obj.set("phone", "13800138000");               // 新增

// 链式操作
obj.fluentPut("city", "上海").fluentPut("dept", "技术部");
// {"name":"张三","age":26,"phone":"13800138000","city":"上海","dept":"技术部"}

// 修改嵌套字段
JSONObject nested = JSONUtil.parseObj("{\"user\":{\"name\":\"张三\"}}");
nested.getJSONObject("user").set("name", "李四");
// {"user":{"name":"李四"}}

2.4 删

java 复制代码
JSONObject obj = JSONUtil.parseObj(
    "{\"name\":\"张三\",\"age\":25,\"phone\":\"123\"}"
);

// 删除字段
obj.remove("phone");

// 删除多个
obj.remove("age", "name");
// {}

// 删除嵌套字段
JSONObject nested = JSONUtil.parseObj("{\"user\":{\"name\":\"张三\",\"phone\":\"123\"}}");
nested.getJSONObject("user").remove("phone");
// {"user":{"name":"张三"}}

2.5 数组操作

java 复制代码
// 创建数组
JSONArray arr = new JSONArray();
arr.add("用户管理");
arr.add("订单管理");
arr.add("财务");
// ["用户管理","订单管理","财务"]

// 从字符串解析
JSONArray arr2 = JSONUtil.parseArray("[\"A\",\"B\",\"C\"]");

// 读取
String first = arr.getStr(0);                 // "用户管理"

// 追加
arr.add("人事");

// 指定位置插入
arr.add(1, "插入的菜单");

// 删除(按下标)
arr.remove(0);

// 删除(按值)
arr.remove("订单管理");

// 判断是否存在
boolean exists = arr.contains("用户管理");    // true

2.6 对象 ↔ Java互转

java 复制代码
// JSON字符串 → Java对象
User user = JSONUtil.toBean("{\"name\":\"张三\",\"age\":25}", User.class);

// Java对象 → JSON字符串
String json = JSONUtil.toJsonStr(user);

// JSON字符串 → List
List<User> list = JSONUtil.toList("[{\"name\":\"张三\"}]", User.class);

3. Fastjson 增删改查

阿里的 com.alibaba.fastjson 包,性能好,支持JSONPath点号读取。

3.1 创建

java 复制代码
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONArray;

// 方式1:new JSONObject()
JSONObject obj = new JSONObject();
obj.put("name", "张三");
obj.put("age", 25);

// 方式2:链式调用(fluentPut)
JSONObject obj2 = new JSONObject()
    .fluentPut("name", "张三")
    .fluentPut("age", 25)
    .fluentPut("address", new JSONObject()
        .fluentPut("city", "北京"))
    .fluentPut("hobbies", new JSONArray()
        .fluentAdd("编程")
        .fluentAdd("跑步"));

// 方式3:从字符串解析
JSONObject obj3 = JSON.parseObject("{\"name\":\"张三\"}");

3.2 读取

java 复制代码
JSONObject obj = JSON.parseObject(
    "{\"name\":\"张三\",\"age\":25,\"address\":{\"city\":\"北京\"}}"
);

// 基本字段
String name = obj.getString("name");           // "张三"
int age = obj.getIntValue("age");              // 25
String phone = obj.getString("phone");         // 不存在返回null

// 嵌套字段(支持点号,比Hutool方便)
String city = obj.getString("address.city");   // "北京"

// 判断字段是否存在
boolean has = obj.containsKey("name");         // true

3.3 增/改

java 复制代码
JSONObject obj = JSON.parseObject("{\"name\":\"张三\",\"age\":25}");

// put():存在则更新,不存在则新增
obj.put("age", 26);
obj.put("phone", "13800138000");

// 链式操作
obj.fluentPut("city", "上海").fluentPut("dept", "技术部");

// 修改嵌套字段
JSONObject nested = JSON.parseObject("{\"user\":{\"name\":\"张三\"}}");
nested.getJSONObject("user").put("name", "李四");
// {"user":{"name":"李四"}}

3.4 删

java 复制代码
JSONObject obj = JSON.parseObject(
    "{\"name\":\"张三\",\"age\":25,\"phone\":\"123\"}"
);

// 删除字段
obj.remove("phone");

// 删除嵌套字段
JSONObject nested = JSON.parseObject("{\"user\":{\"name\":\"张三\",\"phone\":\"123\"}}");
nested.getJSONObject("user").remove("phone");
// {"user":{"name":"张三"}}

3.5 数组操作

java 复制代码
// 创建
JSONArray arr = new JSONArray();
arr.add("用户管理");
arr.add("订单管理");
arr.add("财务");

// 从字符串解析
JSONArray arr2 = JSON.parseArray("[\"A\",\"B\",\"C\"]");

// 读取
String first = arr.getString(0);              // "用户管理"

// 追加
arr.add("人事");

// 删除
arr.remove(0);                                // 按下标
arr.remove("订单管理");                        // 按值

// 遍历
for (Object item : arr) {
    System.out.println(item);
}

3.6 JSONPath(Fastjson特色)

java 复制代码
JSONObject obj = JSON.parseObject(
    "{\"users\":[{\"name\":\"张三\"},{\"name\":\"李四\"}]}"
);

// 用点号读取嵌套字段
String name = (String) JSONPath.eval(obj, "$.users[0].name");  // "张三"

// 过滤
Object result = JSONPath.eval(obj, "$.users[?(@.name=='张三')]");

3.7 对象 ↔ Java互转

java 复制代码
// JSON字符串 → Java对象
User user = JSON.parseObject("{\"name\":\"张三\"}", User.class);

// Java对象 → JSON字符串
String json = JSON.toJSONString(user);

// JSON字符串 → List
List<User> list = JSON.parseArray("[{\"name\":\"张三\"}]", User.class);

// JSONObject → Java对象
JSONObject obj = new JSONObject();
obj.put("name", "张三");
User user2 = obj.toJavaObject(User.class);

3.8 @JSONField注解

java 复制代码
public class User {
    // 指定字段名
    @JSONField(name = "user_name")
    private String name;

    // 格式化日期
    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;

    // 不序列化
    @JSONField(serialize = false)
    private String password;

    // 指定序列化/反序列化顺序
    @JSONField(ordinal = 1)
    private Long id;
}

4. Jackson 增删改查

Spring Boot 默认集成,spring-boot-starter-web 自带,不需要额外引入。

4.1 创建

java 复制代码
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ArrayNode;

ObjectMapper mapper = new ObjectMapper();

// 创建空节点
ObjectNode node = mapper.createObjectNode();
node.put("name", "张三");
node.put("age", 25);
node.putObject("address")                    // 嵌套对象
    .put("city", "北京");
node.putArray("hobbies")                     // 数组
    .add("编程")
    .add("跑步");

// 从Java对象转换
User user = new User("张三", 25);
ObjectNode node2 = mapper.valueToTree(user);

// 转为字符串
String json = mapper.writeValueAsString(node);

4.2 读取

java 复制代码
import com.fasterxml.jackson.databind.JsonNode;

ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(
    "{\"name\":\"张三\",\"age\":25,\"address\":{\"city\":\"北京\"}}"
);

// 基本字段
String name = root.get("name").asText();              // "张三"
int age = root.get("age").asInt();                     // 25

// 嵌套字段
String city = root.get("address").get("city").asText();  // "北京"

// 安全读取(不存在返回空串,不抛异常)
String phone = root.path("phone").asText("");          // ""

// 判断字段是否存在
boolean has = root.has("name");                        // true

4.3 增/改

java 复制代码
ObjectMapper mapper = new ObjectMapper();
ObjectNode node = (ObjectNode) mapper.readTree(
    "{\"name\":\"张三\",\"age\":25}"
);

// put():更新/新增
node.put("age", 26);
node.put("phone", "13800138000");

// set():更新(传入JsonNode)
node.set("name", mapper.valueToTree("李四"));

// putObject():创建嵌套对象
node.putObject("address").put("city", "上海");

// putArray():创建数组
node.putArray("hobbies").add("编程").add("跑步");

// replace():替换字段
node.replace("name", mapper.valueToTree("王五"));

4.4 删

java 复制代码
ObjectMapper mapper = new ObjectMapper();
ObjectNode node = (ObjectNode) mapper.readTree(
    "{\"name\":\"张三\",\"age\":25,\"phone\":\"123\"}"
);

// 删除字段
node.remove("phone");

// 删除多个
node.remove("age", "name");

// 删除嵌套字段
ObjectNode nested = (ObjectNode) mapper.readTree(
    "{\"user\":{\"name\":\"张三\",\"phone\":\"123\"}}"
);
((ObjectNode) nested.get("user")).remove("phone");
// {"user":{"name":"张三"}}

4.5 数组操作

java 复制代码
ObjectMapper mapper = new ObjectMapper();
ArrayNode arr = mapper.createArrayNode();
arr.add("用户管理");
arr.add("订单管理");
arr.add("财务");

// 读取
String first = arr.get(0).asText();            // "用户管理"

// 追加
arr.add("人事");

// 删除
arr.remove(0);                                 // 按下标

// 遍历
for (JsonNode item : arr) {
    System.out.println(item.asText());
}

4.6 对象 ↔ Java互转

java 复制代码
ObjectMapper mapper = new ObjectMapper();

// JSON字符串 → Java对象
User user = mapper.readValue("{\"name\":\"张三\"}", User.class);

// Java对象 → JSON字符串
String json = mapper.writeValueAsString(user);

// JSON字符串 → Map
Map<String, Object> map = mapper.readValue(json, Map.class);

// JSON字符串 → List
List<User> list = mapper.readValue(
    "[{\"name\":\"张三\"}]",
    mapper.getTypeFactory().constructCollectionType(List.class, User.class)
);

4.7 常用注解

java 复制代码
public class User {
    @JsonProperty("user_name")                 // 指定JSON字段名
    private String name;

    @JsonIgnore                                // 序列化时忽略
    private String password;

    @JsonInclude(JsonInclude.Include.NON_NULL) // null不输出
    private String remark;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")  // 日期格式化
    private Date createTime;

    @JsonAlias({"userName", "user_name"})      // 反序列化接受多个名字
    private String nickName;
}

5. MyBatis TypeHandler

数据库有 JSON 字段,MyBatis 默认不认识,需要自定义 TypeHandler 做转换。

5.1 Hutool版 TypeHandler

java 复制代码
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.*;

public class HutoolJsonTypeHandler extends BaseTypeHandler<JSONObject> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
            JSONObject parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter.toString());
    }

    @Override
    public JSONObject getNullableResult(ResultSet rs, String col) throws SQLException {
        return parseJson(rs.getString(col));
    }

    @Override
    public JSONObject getNullableResult(ResultSet rs, int col) throws SQLException {
        return parseJson(rs.getString(col));
    }

    @Override
    public JSONObject getNullableResult(CallableStatement cs, int col) throws SQLException {
        return parseJson(cs.getString(col));
    }

    private JSONObject parseJson(String json) {
        if (json == null || json.isEmpty()) return null;
        return JSONUtil.parseObj(json);
    }
}

5.2 Fastjson版 TypeHandler

java 复制代码
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSON;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.*;

public class FastjsonTypeHandler extends BaseTypeHandler<JSONObject> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
            JSONObject parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter.toJSONString());
    }

    @Override
    public JSONObject getNullableResult(ResultSet rs, String col) throws SQLException {
        return parseJson(rs.getString(col));
    }

    @Override
    public JSONObject getNullableResult(ResultSet rs, int col) throws SQLException {
        return parseJson(rs.getString(col));
    }

    @Override
    public JSONObject getNullableResult(CallableStatement cs, int col) throws SQLException {
        return parseJson(cs.getString(col));
    }

    private JSONObject parseJson(String json) {
        if (json == null || json.isEmpty()) return null;
        return JSON.parseObject(json);
    }
}

5.3 在实体类中使用

java 复制代码
public class SysConfig {
    private Long id;
    private String configKey;

    // Hutool版
    @TableField(typeHandler = HutoolJsonTypeHandler.class)
    private JSONObject configValue;

    // 或 Fastjson版
    // @TableField(typeHandler = FastjsonTypeHandler.class)
    // private JSONObject configValue;

    private Date gmtCreate;
}

5.4 在Mapper中使用

java 复制代码
@Mapper
public interface SysConfigMapper {

    // 查询(自动将JSON字符串转为JSONObject)
    @Select("SELECT * FROM sys_config WHERE id = #{id}")
    SysConfig selectById(@Param("id") Long id);

    // 插入(自动将JSONObject转为JSON字符串)
    @Insert("INSERT INTO sys_config (config_key, config_value) " +
            "VALUES (#{configKey}, #{configValue, typeHandler=HutoolJsonTypeHandler})")
    int insert(SysConfig config);

    // 更新
    @Update("UPDATE sys_config SET config_value = " +
            "#{configValue, typeHandler=HutoolJsonTypeHandler} WHERE id = #{id}")
    int update(SysConfig config);

    // 查询JSON内部字段(不需要TypeHandler)
    @Select("SELECT config_value->>'$.provider' FROM sys_config WHERE config_key = #{key}")
    String getConfigProvider(@Param("key") String key);
}

6. 三种工具对比

6.1 选型建议

场景 推荐
Spring Boot项目(默认集成) Jackson
非Spring项目,追求API简洁 Hutool JSON
需要JSONPath点号读取 Fastjson
e签宝等第三方SDK Fastjson(@JSONField注解)

6.2 API对比

操作 Hutool JSON Fastjson Jackson
字符串→对象 JSONUtil.parseObj(str) JSON.parseObject(str) mapper.readTree(str)
字符串→Java JSONUtil.toBean(str, Cls.class) JSON.parseObject(str, Cls.class) mapper.readValue(str, Cls.class)
读取字段 obj.getStr("key") obj.getString("key") node.get("key").asText()
嵌套字段 obj.getJSONObject("a").getStr("b") obj.getString("a.b") node.get("a").get("b").asText()
默认值 obj.getStr("key", "") obj.getString("key", "") node.path("key").asText("")
修改字段 obj.set("key", val) obj.put("key", val) node.put("key", val)
删除字段 obj.remove("key") obj.remove("key") node.remove("key")
对象→字符串 JSONUtil.toJsonStr(obj) JSON.toJSONString(obj) mapper.writeValueAsString(obj)

6.3 特色差异

特性 Hutool JSON Fastjson Jackson
嵌套字段点号读取 需链式 getJSONObject().getStr() 直接 getString("a.b") 需链式 get().get().asText()
JSONPath 不支持 支持 JSONPath.eval() 不支持(需额外jar)
注解 @JSONField @JsonProperty/@JsonIgnore
流式处理大文件 不支持 不支持 支持 Streaming API
Spring默认
相关推荐
咖啡八杯2 小时前
实体基类设计:BaseEntity 公共字段抽取与 TreeEntity 树形继承
java·架构·代码规范
她的男孩2 小时前
AI 写完 100 万行代码没人 Review,我做了一件事:把 AI 写的代码管起来了
java·人工智能·程序员
Gl�ria2 小时前
Linux 查看日志常用命令
java·linux·servlet
大模型丫丫2 小时前
LangGraph + MCP(Model Context Protocol)完整讲解
java·开发语言·数据库
lemon_sjdk2 小时前
JavaFX 源码深度剖析:揭开 StringBinding 的神秘面纱
java·javafx·源码解析·绑定框架
砚底藏山河3 小时前
拉数管道设计:从一次性脚本到可重启的数据流水线(魔码量化实战 #01)
java·数据库·python·金融·maven
饕餮争锋3 小时前
PATH路径列表介绍
java·linux·服务器
SimonKing3 小时前
一文终结 Java 路径加载争议:斜杠 什么时候该加,什么时候不该加
java·后端·程序员
Rain的Java大神之路3 小时前
手写一个Spring容器
java·后端·mysql·spring·面试