使用Mybatis封装返回多层级数据

473 阅读3分钟

前言

实际开发中,我们从数据库查询返回得到的数据可能需要二次封装成前端想要处理的数据,而不只是简单的返回 pojo/entity 类。

例如,有如下一张简单的表:分类表

字段含义
id分类 id
name分类名称
parent_id父类 id,第一级分类 parent_id 为 0

如表格所示,这张表用来存储大学专业分类数据、或者商品分类、图书分类等,这些分类往往是多级结构的。

例如,我们可能需要这样的数据展示:

一级分类二级分类
经济学经济学类
财政学类
金融学类
法学法学类
政治学类
社会学类
理学数学类
物理学类
化学类
天文学类
工学力学类
机械类

我们可以在查询到数据后,直接在 pojo 类上进行封装成前端所需要的数据结构返回,但是我们可以利用 MyBatis 帮我们完成封装过程,提高开发效率。

有两种方法,一种是直接封装,另一种是使用分段查询,具体使用请看下文。

代码实现

我们只需使用 MyBatis 提供的 resultMap 标签指明查询数据的数据结构即可。

关于 resultMap 标签,它的属性有:

  • id:唯一标识自己,与 select 标签中的 resultMap 属性绑定
  • type: 指明数据封装使用的

它的子标签有:

  • id :表示数据库中的主键
  • result:基本数据类型所对应的列
  • collection:映射对象集合
  • association:映射单个对象
  • discriminator:条件分支,根据结果决定映射

详细可以查看说明:XML 映射器_MyBatis中文网

这里使用上文讲述的大学专业分类展示

数据准备

分类表结构category 以及数据

CREATE TABLE `category` (
  `id` int(11) NOT NULL COMMENT 'id 主键',
  `parent_id` int(11) DEFAULT NULL COMMENT '上级 Id, 一级分类的 parent_id 为 0',
  `name` varchar(20) DEFAULT NULL COMMENT '分类名称',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


INSERT INTO `category` VALUES (1, 0, '经济学');
INSERT INTO `category` VALUES (2, 0, '法学');
INSERT INTO `category` VALUES (3, 0, '理学');
INSERT INTO `category` VALUES (4, 0, '工学');
INSERT INTO `category` VALUES (11, 1, '经济学类');
INSERT INTO `category` VALUES (12, 1, '财政学类');
INSERT INTO `category` VALUES (13, 1, '金融学类');
INSERT INTO `category` VALUES (21, 2, '法学类');
INSERT INTO `category` VALUES (22, 2, '政治学类');
INSERT INTO `category` VALUES (23, 2, '社会学类');
INSERT INTO `category` VALUES (31, 3, '数学类');
INSERT INTO `category` VALUES (32, 3, '物理学类');
INSERT INTO `category` VALUES (33, 3, '化学类');
INSERT INTO `category` VALUES (34, 3, '天文学类');
INSERT INTO `category` VALUES (41, 4, '力学类');
INSERT INTO `category` VALUES (42, 4, '机械类');

代码展示

为了演示效果,这里并未编写对应的数据库实体类 pojo——Category

项目结构为:

image-20220726163218420.png

mapper 文件:

<mapper namespace="com.example.leveldemo.mapper.CategoryMapper">
   
    <!-- 使用 resultMap 指示数据结构 -->
    <resultMap id="category" type="com.example.leveldemo.dto.OneCategoryLevel">
        <result column="one_id" property="id"/>
        <result column="one_category" property="name"/>
        <collection property="children" ofType="com.example.leveldemo.dto.TwoCategoryLevel">
            <result column="two_id" property="id"/>
            <result column="two_category" property="name"/>
        </collection>
    </resultMap>
    
    <select id="selectCategories" resultMap="category">
        SELECT
            t1.id AS one_id,
            t1.`name` AS one_category,
            t2.id AS two_id,
            t2.`name` AS two_category
        FROM
            `category` AS t1,
            `category` AS t2
        WHERE
            t1.id = t2.parent_id;
    </select>
</mapper>

DTO 类:

@Data
public class OneCategoryLevel {
    private Integer id;
    private String name;
    private List<TwoCategoryLevel> children;
}

@Data
public class TwoCategoryLevel {
    private Integer id;
    private String name;
}

其他代码:

//================== Controller 
@RestController
public class CategoryController {

    @Autowired
    private CategoryService categoryService;

    @GetMapping("/categories")
    public List<OneCategoryLevel> getCategories() {
        return categoryService.getCategories();
    }

}
//================== Service
@Service
public class CategoryServiceImpl implements CategoryService {

    @Autowired
    private CategoryMapper categoryMapper;

    @Override
    public List<OneCategoryLevel> getCategories() {
        return categoryMapper.selectCategories();
    }
}
//================== Mapper
@Mapper
public interface CategoryMapper {
    List<OneCategoryLevel> selectCategories();
}

测试

调用请求接口,得到返回数据,可以看到 MyBatis 已经帮我们封装好了:

[
    {
        "id":1,
        "name":"经济学",
        "children":[
            {
                "id":11,
                "name":"经济学类"
            },
            {
                "id":12,
                "name":"财政学类"
            },
            {
                "id":13,
                "name":"金融学类"
            }
        ]
    },
    {
        "id":2,
        "name":"法学",
        "children":[
            {
                "id":21,
                "name":"法学类"
            },
            {
                "id":22,
                "name":"政治学类"
            },
            {
                "id":23,
                "name":"社会学类"
            }
        ]
    },
    {
        "id":3,
        "name":"理学",
        "children":[
            {
                "id":31,
                "name":"数学类"
            },
            {
                "id":32,
                "name":"物理学类"
            },
            {
                "id":33,
                "name":"化学类"
            },
            {
                "id":34,
                "name":"天文学类"
            }
        ]
    },
    {
        "id":4,
        "name":"工学",
        "children":[
            {
                "id":41,
                "name":"力学类"
            },
            {
                "id":42,
                "name":"机械类"
            }
        ]
    }
]

分布查询实现:

 <mapper namespace="com.example.leveldemo.mapper.CategoryMapper">
    
     <!-- 使用 resultMap 指示数据结构 -->
     <resultMap id="category" type="com.example.leveldemo.dto.OneCategoryLevel">
         <result column="id" property="id"/>
         <result column="name" property="name"/>
         <!-- column 为要传入查询的参数 select 为查询语句的 ID  -->
         <!-- fetchType 可以配置懒加载和立即加载 分别取值为:lazy / eager -->
         <!-- autoMapping 可以自动配置字段 -->
         <collection property="children" select="selectTwoCategories" column="id" >
             <result column="two_id" property="id"/>
             <result column="two_category" property="name"/>
         </collection>
     </resultMap>
     
     <select id="selectCategories" resultMap="category">
         SELECT
             t1.id,
             t1.`name
         FROM
             `category` AS t1
     </select>
     <select id="selectTwoCategories" resultType="com.example.leveldemo.dto.TwoCategoryLevel">
         SELECT
             t2.id,
             t2.`name
         FROM
             `category` AS t2
         WHERE
             t2.parent_id = #{id}
     </select>
 </mapper>

总结

当需要对查询结果进行封装时,即查询结果的数据结构较为复杂时,我们可以使用 resultMap 标签对返回结果的数据结构进行映射,例如需要查询的结果数据结构是多层级的时候。

参考:blog.csdn.net/qq_44706176…