MyBatis-Plus 实现 MySQL JSON 类型字段的映射

序言

MySQL 支持 JSON 类型的字段。相比字符串类型的字段, JSON 类型有如下好处:

  • 自动检查 JSON 语法是否正确
  • 底层支持快速存取 JSON 中的元素。不需要将整个 字符串 全部读取出来,再解析成 JSON 对象

在逻辑上,JSON 与 POJO 无异。Spring MVC 已经实现了 Controller 层两者的自动转化,这体现在请求参数和返回值上。那么,Repository 层如何实现两者的自动转换呢?

本文展示了通过使用 MyBatis-Plus,无侵入式地实现持久层 JSON 与 POJO 的自动映射(ORM, Object Relational Mapping)

案例演示

数据库表

1
2
3
4
5
6
CREATE TABLE `person` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`log` json NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;

依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

entity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Data
@TableName(value = "person", autoResultMap = true)
public class Person {

private Long id;

private String name;

@TableField(typeHandler = JacksonTypeHandler.class)
private PersonLog log;

@Data
public static class PersonLog {
List<String> hobbies;
Integer age;
String address;
}
}

这里的PersonLog是重点讨论的对象,我们要实现它与 JSON 的映射。

Person类上需要标记两个注解,两者通常一起使用:

TableName#autoResultMap:负责在查询时候把 JSON 转换为 POJO

TableField#typeHandler:负责在插入时将 POJO 转换为 JSON。由于 spring-web-starter 已经导入了 jackson 依赖,直接使用 JacksonTypeHandler 即可。

mapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.demo.mapper.PersonMapper">

<resultMap id="BaseResultMap" type="com.example.demo.entity.Person">
<id column="id" property="id" jdbcType="BIGINT"/>
<result column="name" property="name" jdbcType="VARCHAR"/>
<result column="log" property="log" jdbcType="LONGVARCHAR" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
</resultMap>

<sql id="Base_Column_List">
id, name, log
</sql>

</mapper>

mapper 文件中需要指出 log 字段的 typeHandler 的全类名

测试

1
2
3
@Mapper
public interface PersonMapper extends BaseMapper<Person> {
}

省略了 Service 层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
public class PersonController {
@Autowired
private PersonMapper personMapper;

@PostMapping("/json")
public Integer testJSON2BeanMap(@RequestBody Person person) {
return personMapper.insert(person);
}

@GetMapping("/json/{id}")
public Person getPersonById(@PathVariable("id") Long id) {
return personMapper.selectById(id);
}
}

测试两个方法,没报错就说明成功,插入时可以再查看一下数据库。

序言中提到的无侵入式ORM,是指我们不需要在代码中手动地执行 JSON 与 PersonLog 的转换。

总结

本文展示了通过使用 MyBatis-Plus无侵入式实现持久层 JSON 与 POJO 的自动映射。

我对这一问题的研究源自实习时业务的要求。当我查询网上的资料时,发现帖子的表述都不太完整、直观,于是我记录下了解决方案,写成了这篇文章。

参考文章

The JSON Data Type

mybatis-plus处理mysql中json格式方法