1、基本操作
要在MyBatis-Plus中使用MySQL的JSON类型,可以按照以下步骤操作:
数据库表设计
在MySQL表中定义JSON类型的字段,例如:
CREATE TABLE your_table (
id INT PRIMARY KEY AUTO_INCREMENT,
json_data JSON
);
实体类映射
在Java实体类中,使用相应的注解映射JSON字段。
-
添加依赖
确保项目中已添加MyBatis-Plus和Jackson相关的依赖。
-
实体类注解
@Data @TableName(autoResultMap = true) // 开启自动结果映射 public class YourEntity { // ... @TableField(typeHandler = JacksonTypeHandler.class) // 指定JSON类型处理器 private YourJsonObject jsonData; // ... }@TableName(autoResultMap = true):启用自动结果映射,确保MyBatis-Plus能够正确映射JSON字段。@TableField(typeHandler = JacksonTypeHandler.class):指定使用JacksonTypeHandler处理JSON类型与Java对象之间的转换。
CRUD操作
使用MyBatis-Plus提供的CRUD方法,可以直接操作JSON类型字段。
-
插入数据
YourEntity entity = new YourEntity(); entity.setJsonData(yourJsonObject); // 设置JSON对象 yourEntityMapper.insert(entity); -
查询数据
YourEntity entity = yourEntityMapper.selectById(id); YourJsonObject jsonData = entity.getJsonData(); // 获取JSON对象
自定义TypeHandler
如果需要使用自定义的JSON类型处理器,可以实现org.apache.ibatis.type.TypeHandler接口。
@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(Object.class)
public class CustomJsonTypeHandler<T> extends BaseTypeHandler<T> {
private static final ObjectMapper objectMapper = new ObjectMapper();
// ... 实现setNonNullParameter、getNullableResult等方法
}
然后在实体类中使用@TableField(typeHandler = CustomJsonTypeHandler.class)指定自定义的TypeHandler。
注意事项
- MySQL版本:确保MySQL版本在5.7以上,才支持JSON类型。
- Jackson依赖:如果使用
JacksonTypeHandler,需要确保项目中已添加Jackson相关的依赖。
2、MySQL查询JSON字段
在MySQL中可以直接查询JSON字段中的值。MySQL提供了多个函数来操作和查询JSON数据类型,例如JSON_EXTRACT()、->操作符等。
假设你有一个表your_table,其中包含一个JSON类型的字段json_data。如果你想查询这个JSON字段中某个键对应的值,可以使用如下方法:
使用 JSON_EXTRACT()
SELECT JSON_EXTRACT(json_data, '$.key_name') AS key_value FROM your_table;
这里,$.key_name是你要查询的键路径。如果json_data的内容是{"key_name":"value"},那么上述查询将返回"value"。
使用 -> 操作符
MySQL还提供了一个更简洁的语法,即使用->操作符来代替JSON_EXTRACT()函数:
SELECT json_data->'$.key_name' AS key_value FROM your_table;
这个查询与前面提到的JSON_EXTRACT()例子具有相同的效果。
查询条件
你还可以在WHERE子句中使用这些函数或操作符来过滤结果。例如,查找所有key_name值为"specific_value"的记录:
SELECT * FROM your_table WHERE json_data->>'$.key_name' = 'specific_value';
注意这里的->>操作符,它不仅提取了JSON值,还取消了可能存在的引号(适用于字符串值),这使得它可以与普通字符串直接比较。
通过这些方法,你可以直接从MySQL的JSON字段中查询特定的值,并根据需要进行处理。结合MyBatis-Plus使用时,只需在XML映射文件或者注解中编写相应的SQL语句即可实现对JSON字段的复杂查询。
3、Lambda 表达式
在 MyBatis-Plus 中,原生的 Lambda 表达式(QueryWrapper.lambda())无法直接支持 MySQL 的 JSON 字段路径查询,因为 Lambda 表达式是基于 Java 实体类的字段进行编译时检查的,而 JSON 字段中的路径(如 $.name)并不是实体类的直接属性。
但是,你仍然可以通过以下几种方式,在使用 QueryWrapper 的同时,结合 Lambda 风格或增强可读性来实现对 JSON 字段的查询:
✅ 方法一:在 QueryWrapper 中使用 apply 方法(推荐)
虽然不能直接用 Lambda 操作 JSON 路径,但你可以使用 QueryWrapper 的 apply 方法插入原生 SQL 片段,并保持链式调用风格。
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
// 查询 json_data 中 name 字段等于 'Alice' 的记录
QueryWrapper<YourEntity> wrapper = new QueryWrapper<>();
wrapper.apply("json_data->>'$.name' = {0}", "Alice");
List<YourEntity> result = yourEntityMapper.selectList(wrapper);
->>会提取并去掉引号(适用于字符串)。{0}是 MyBatis-Plus 的占位符,防止 SQL 注入。- 这种方式可以很好地融入
QueryWrapper的链式语法,接近“类 Lambda”的体验。
✅ 方法二:结合 eq 或 like 使用 apply
如果你还想使用 eq、like 等语义化方法,可以配合 apply:
QueryWrapper<YourEntity> wrapper = new QueryWrapper<>();
wrapper.apply("json_data->>'$.email' = {0}", "alice@example.com");
或者模糊查询:
wrapper.apply("json_data->>'$.name' LIKE {0}", "%li%");
✅ 方法三:封装成工具方法(提升复用性)
为了更接近“Lambda 风格”,你可以封装一个工具类或扩展方法:
public class JsonQueryWrapper<T> extends QueryWrapper<T> {
public JsonQueryWrapper<T> jsonEq(String jsonField, String jsonPath, Object value) {
return (JsonQueryWrapper<T>) this.apply(jsonField + "->>'$" + jsonPath + "' = {0}", value);
}
public JsonQueryWrapper<T> jsonLike(String jsonField, String jsonPath, String value) {
return (JsonQueryWrapper<T>) this.apply(jsonField + "->>'$" + jsonPath + "' LIKE {0}", "%" + value + "%");
}
}
使用方式:
JsonQueryWrapper<YourEntity> wrapper = new JsonQueryWrapper<>();
wrapper.jsonEq("json_data", ".name", "Alice")
.jsonLike("json_data", ".email", "gmail");
List<YourEntity> result = yourEntityMapper.selectList(wrapper);
这样既保持了链式调用,又提升了代码可读性。
❌ 不能这样做(错误示例)
// 错误!实体类没有 getName() 对应 JSON 内部字段
wrapper.lambda().eq(YourEntity::getName, "Alice"); // 不会映射到 json_data->name
🔔 总结
| 方法 | 是否支持 Lambda | 推荐程度 | 说明 |
|---|---|---|---|
apply() + 原生 JSON 路径 |
❌ | ⭐⭐⭐⭐☆ | 最常用,安全且灵活 |
自定义 QueryWrapper 扩展 |
❌(但风格类似) | ⭐⭐⭐⭐ | 提高可读性和复用性 |
| 实体类映射 JSON 子字段 | ❌ | ⭐ | 当前不支持,不推荐 |
✅ 最佳实践:使用
QueryWrapper.apply()插入json_field->>'$.path' = ?的条件,是目前最稳定、清晰且安全的方式。
🧩 补充:JSON 索引建议
如果 JSON 查询频繁,建议为常用字段创建 虚拟列 + 索引:
ALTER TABLE your_table
ADD COLUMN name_virtual VARCHAR(100)
GENERATED ALWAYS AS (json_data->>'$.name');
CREATE INDEX idx_name_virtual ON your_table(name_virtual);
这样可以大幅提升查询性能。
4、实体类映射类型(String、POJO、Map)
在大多数情况下,将 MySQL 的 JSON 类型字段在 Java 实体类中映射为 String 类型,进行 CRUD 操作是可行的,并且 MyBatis-Plus 可以正常工作,但有一些重要的细节和潜在问题需要注意。
✅ 可以这样做的原因
-
MySQL 的 JSON 字段本质上是文本存储:
- MySQL 的
JSON类型在底层是以优化的二进制格式存储的,但在 SQL 层面,它表现为一个 JSON 格式的字符串。 - 当你
SELECT时,返回的是一个 JSON 字符串(如{"name": "Alice", "age": 30})。
- MySQL 的
-
MyBatis/MyBatis-Plus 的默认行为:
- 如果你没有为该字段指定
typeHandler,MyBatis 会尝试将数据库中的 JSON 值当作字符串处理。 - 因此,将实体类字段定义为
String,MyBatis 会直接把查询结果赋值给这个String字段。
- 如果你没有为该字段指定
-
插入和更新:
- 只要你传入的
String是合法的 JSON 格式,MySQL 能够接受并正确解析它。 - 例如:
entity.setJsonData("{\"name\": \"Bob\"}");插入时不会报错。
- 只要你传入的
⚠️ 潜在问题和注意事项
1. 没有类型安全,容易出错
- 你可以在代码中给
String字段赋任何字符串,比如"not a json",MyBatis 不会检查,但 MySQL 在插入时会抛出异常:Invalid JSON text: "Invalid value." - 缺少编译时或运行时的 JSON 格式校验。
2. 操作不便,需要手动序列化/反序列化
- 每次使用 JSON 内容时,你都需要手动用
ObjectMapper或JSON.toJSONString()进行转换:// 查询后 String jsonData = entity.getJsonData(); YourObject obj = objectMapper.readValue(jsonData, YourObject.class); // 更新前 YourObject obj = new YourObject(); entity.setJsonData(objectMapper.writeValueAsString(obj)); - 容易出错,代码冗余。
3. 无法利用 MyBatis-Plus 的 JacksonTypeHandler 自动映射
- 如果你用
String类型,即使加了@TableField(typeHandler = JacksonTypeHandler.class),它也会尝试把 Java 对象序列化成 JSON 字符串,但目标字段是String,可能导致类型不匹配或异常。
4. JSON 函数查询可能受影响
- 虽然 SQL 层面不影响(你查的是数据库字段),但如果你在应用层对
String字段做逻辑判断,可能因为格式问题(如空格、转义)导致错误。
✅ 推荐做法(最佳实践)
✅ 方案一:使用 POJO 对象 + JacksonTypeHandler
@Data
public class YourEntity {
private Long id;
@TableField(typeHandler = JacksonTypeHandler.class)
private UserInfo jsonData; // UserInfo 是一个 Java 对象
}
@Data
public class UserInfo {
private String name;
private Integer age;
}
- 优点:类型安全、自动序列化/反序列化、代码清晰。
- 要求:确保引入
jackson-databind依赖。
✅ 方案二:使用 Map<String, Object> + JacksonTypeHandler
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<String, Object> jsonData;
- 适合结构不固定的 JSON 数据。
✅ 方案三:继续用 String,但做好校验
如果你坚持用 String,建议:
- 在
setJsonData()方法中加入 JSON 格式校验:public void setJsonData(String jsonData) { if (jsonData != null && !jsonData.trim().isEmpty()) { try { objectMapper.readTree(jsonData); // 验证是否为合法 JSON } catch (IOException e) { throw new IllegalArgumentException("Invalid JSON format", e); } } this.jsonData = jsonData; } - 在插入前确保数据是合法 JSON。
✅ 总结
| 项目 | 说明 |
|---|---|
| 是否影响 CRUD | ❌ 不影响(只要 JSON 格式正确) |
| 是否推荐 | ⚠️ 不推荐长期使用,仅适合临时或简单场景 |
| 推荐替代方案 | 使用 POJO 或 Map + JacksonTypeHandler |
| 关键点 | 确保 String 中的内容是合法 JSON,否则插入会失败 |
💡 结论:为了代码健壮性和可维护性,建议使用对象映射 +
JacksonTypeHandler。

浙公网安备 33010602011771号