表结构准备
准备一张任务表 task
1 | create table task ( |
再准备一张任务详情表 task_detail
1 | create table task_detail ( |
往任务表内写入一些模拟数据
1 | INSERT INTO task (id, task_name, task_status, task_create_time, task_update_time) VALUES (1, '任务001', 1, '2024-01-08 15:04:10', '2024-01-08 15:04:10'); |
往任务详情表内写入一些模拟数据
1 | INSERT INTO task_detail (id, task_id, task_info) VALUES (1, 1, '{"taskId": 1, "taskInfo": "子任务1", "taskName": "任务001-01"}'); |
任务表中的一个任务可能会对应多个任务详情,创建对应的 POJO 类
1 |
|
任务详情中存在一个类型比较特殊字段,这里就直接用静态内部类来表示了
1 |
|
结果集嵌套
collection 指定列嵌套
Mapper 接口
1 | List<TaskVO> selectTaskList(); |
Mapper xml
1 | <resultMap id="BaseResultMap" type="com.venom.model.vo.TaskVO"> |
sql 日志:
1 | ==> Preparing: select t1.id, t1.task_name, t1.task_status,t1.task_create_time, t1.task_update_time, t2.id d_id, t2.task_id, t2.task_info from task t1 left join task_detail t2 on t1.id = t2.task_id |
查询结果:
1 | [ |
这里需要注意的是,task 与 task_detail 中都存在同名列 id,所以 task_detail 中的 id 需要使用别名
collection 指定 ResultMap 嵌套
除此之外,还有一种情况是,直接嵌套另外已存在的 ResutlMap
Mapper 接口与上相同
xml 如下:
1 | <resultMap id="BaseResultMap" type="com.venom.model.vo.TaskVO"> |
resultMap
指定嵌套的 resultMap,columnPrefix
添加别名前缀
查询结果与上相同
association 嵌套
这里顺带提以下 association 嵌套,不同于 collection 嵌套的一对多,association 嵌套针对于一对一,主要用于映射单个对象,collection 可以理解为映射多个对象的集合
先准备一张与 task 一对一关联的 task_name_detail 表
1 | create table task_name_detail( |
插入几条数据:
1 | INSERT INTO db1.task_name_detail (task_name,description) VALUES ('任务001','第一条任务'); |
创建对应 POJO
1 |
|
引入到 TaskVO 中:
1 |
|
新增 Mapper 接口:
1 | List<TaskVO> selectTaskList3(); |
新增对应的 xml 查询:
1 | <resultMap id="BaseResultMap3" type="com.venom.model.vo.TaskVO"> |
与
collection
标签不同的是,association
标签没有ofType
属性,并且必须指定javaType
属性
子查询嵌套
与上面的结果集嵌套类似,也是用于关系的映射,写起来较结果集嵌套也会更加简单,不过 存在性能损耗,会导致 1+N 问题,所以通常不建议使用
1 | <resultMap id="BaseResultMap2" type="com.venom.model.vo.TaskVO"> |
主查询为 selectTaskList2
子查询为 selectTaskDetailByTaskId
,主查询没查询一条结果就会将指定的参数传递给子查询,子查询再查出与之对应的记录,所以查询的次数会是 1 条主查询 + N 条主查询记录对应的子查询
JSON 类型的映射
前面在创建示例表结构和数据的时候就能发现,里面有特殊的 JSON 字段,JSON 字段在增删改查的过程中的对象关系映射是比较特殊的。原理先不分析,先弄清楚使用方式
先创建一个基础的 JSON 对象处理抽象类:
1 | public abstract class AbstractJsonTypeHandler<T> extends BaseTypeHandler<T> { |
如果项目中使用了 Mybatis-plus 的话,可以不用创建这个抽象类,直接使用 Mybatis-plus 提供的
com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler
然后针对具体的 JSON 对象创建对应的处理类,继承上面的抽象类,实现其中抽象的序列化与反序列化方法即可:
1 | public class TaskInfoTypeHandler extends AbstractJsonTypeHandler<TaskDetailVO.TaskInfo> { |
这里使用的是 Jackson,也可以使用 Fastjson,后面再附上一个 Jackson 的工具类:
1 |
|
然后就是 Mybatis 的 xml 中对 JSON 对象指定我们创建的处理类:
1 | <resultMap id="TaskDetailResultMap" type="com.venom.model.vo.TaskDetailVO"> |
特别提示:这里有个容易踩坑的地方,在
resultMap
中指定typeHandler
属性时需要加引号,但是在注入值的时候指定是不需要引号的!
1 #{taskInfo,typeHandler=com.venom.model.TaskInfoTypeHandler}