MyBatis如何执行复杂查询

多对一查询(association)

建表SQL

#创建教室表
create table classroom
(
  id int not null AUTO_INCREMENT,
  classname VARCHAR(40) not null,
  PRIMARY KEY (id)
)
#创建学生表
create table student
(
  id int not null AUTO_INCREMENT,
  name VARCHAR(40) not null,
  classid int not null,
  PRIMARY KEY (id),
  FOREIGN key (classid) REFERENCES classroom(id)
)
#创建一些数据
insert into classroom VALUES (1,'101班');
insert into classroom VALUES (2,'102班');
insert into student VALUES(1,'Amy',1);
insert into student VALUES(2,'Bob',1);
insert into student VALUES(3,'javayz',1);

Java实体类

Student实体类

@Getter
@Setter
@ToString
public class Student {
    private int sid;
    private String stuName;
    private ClassRoom classRoom; // 学生所属的教室
}

ClassRoom实体类

@Getter
@Setter
@ToString
public class ClassRoom {
    private int cid;
    private String className;
}

Mapper接口和配置文件

StudentMapper接口类

@Mapper
public interface StudentMapper {
    List<Student> selectAllStudent(); // 查询所有学生
}

StudentMapper.xml配置类

column对应表中列名时,直接使用select *查询时,会自动进行映射:

<?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.ego.mapper.StudentMapper">

    <!-- 定义 resultMap,将查询结果映射到 Java 对象 -->
    <resultMap id="studentAndClassRoom2" type="com.ego.pojo.Student">
        <!-- 映射学生的 id 列到 Student 对象的 sid 属性 -->
        <id column="id" property="sid"/>
        <!-- 映射学生的 name 列到 Student 对象的 stuName 属性 -->
        <result column="name" property="stuName"/>
        <!-- 映射班级信息到 Student 对象的 classRoom 属性 -->
        <association property="classRoom" javaType="com.ego.pojo.ClassRoom">
            <!-- 映射班级的 id 列到 ClassRoom 对象的 cid 属性 -->
            <id column="id" property="cid"/>
            <!-- 映射班级的 classname 列到 ClassRoom 对象的 className 属性 -->
            <result column="classname" property="className"/>
        </association>
    </resultMap>

    <!-- 定义 SQL 查询 -->
    <select id="selectAllStudent" resultMap="studentAndClassRoom2">
        <!-- 从 student 表和 classroom 表中选择所有列 -->
        select *
        from student s, classroom c
        where s.classid = c.id;
    </select>

</mapper>

记住,association表示java对象,collection 表示集合。

column对应select查询的别名时,使用select *可能会报错,此时需要把select查询的别名和java对象的属性名一一对应:

<?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.ego.mapper.StudentMapper">

    <!-- 定义 resultMap,将查询结果映射到 Java 对象 -->
    <resultMap id="studentAndClassRoom" type="com.ego.pojo.Student">
        <id column="sid" property="sid"/> <!-- 注意这里的 column 应与 SQL 查询中的别名一致 -->
        <result column="stuName" property="stuName"/>

        <association property="classRoom" javaType="com.ego.pojo.ClassRoom">
            <id column="cid" property="cid"/><!-- 注意这里的 column 应与 SQL 查询中的别名一致 -->
            <result column="className" property="className"/>
        </association>
    </resultMap>

    <!-- 定义 SQL 查询 -->
    <select id="selectAllStudent" resultMap="studentAndClassRoom">
        <!-- 使用别名来简化列名,并避免与其他表的列名冲突 -->
        select s.id sid, s.name stuName, c.id cid, c.classname className
        from student s, classroom c
        where s.classid = c.id;
    </select>

</mapper>

执行查询:

studentMapper.selectAllStudent().forEach(System.out::println);

两种.xml的写法的查询结果都是相同的:

Student(sid=1, stuName=Amy, classRoom=ClassRoom(cid=1, className=101班))
Student(sid=2, stuName=Bob, classRoom=ClassRoom(cid=1, className=101班))
Student(sid=3, stuName=javayz, classRoom=ClassRoom(cid=1, className=101班))

总结:

  1. resultMap 中的 columnproperty:column 表示数据库表中的列名。property 表示 Java 对象的属性名。它们应该是一一对应的关系。
  2. 使用 select * 进行查询:当 column 表示数据库表中的列名时,可以使用 select * 进行查询。MyBatis 会自动将查询结果映射到 Java 对象的属性上。
  3. 使用别名进行查询:当 column 表示数据库表中 select 语句查询的别名时,不要使用 select *。需要把别名和 Java 对象的属性名对应起来。
  4. resultMap 的定义:使用 <id> 标签映射主键字段。使用 <result> 标签映射普通字段。在使用 <association> 时,也需要定义关联对象的映射关系。

一对多查询(collection)

修改一下之前的两个实体类,来实现一对多的查询,每个教室里有多个学生:

实体类

Student.java

@Getter
@Setter
@ToString
public class Student {
    private int sid;
    private String stuName;
    private int classId;
}

ClassRoom.java

@Getter
@Setter
@ToString
public class ClassRoom {
    private int cid;
    private String className;
    private List<Student> students; // 一间教室有多个学生
}

Mapper接口和配置文件

这里就用Java对象映射,不用使用别名了。

ClassRoomMapper.java

@Mapper
public interface ClassRoomMapper {

    /**
     * 根据班级 ID 查询所有教室及其对应的学生。
     * 
     * @param classId 指定的班级 ID
     * @return 包含指定班级 ID 的教室及其学生列表
     */
    List<ClassRoom> selectAllClassRoomByClassId(@Param("classId") int classId);
}    

ClassRoomMapper.xml

<?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.ego.mapper.ClassRoomMapper">

    <!--
      定义一个 resultMap,将查询结果映射到 ClassRoom 对象,并包含一个学生列表。
      - id: 映射 ClassRoom 对象的 id 属性。
      - result: 映射 ClassRoom 对象的 classname 属性。
      - collection: 映射 ClassRoom 对象的 students 属性(一个学生列表)。
    -->
    <resultMap id="classRoomAndStudent" type="com.ego.pojo.ClassRoom">
        <id column="id" property="cid"/>
        <result column="classname" property="className"/>
        <collection property="students" ofType="com.ego.pojo.Student">
            <id column="id" property="sid"/>
            <result column="name" property="stuName"/>
            <result column="classid" property="classId"/>
        </collection>
    </resultMap>

    <!--
      查询指定 classId 的所有教室及其对应的学生。
      - parameterType: 输入参数为整型 int。
      - resultMap: 使用 classRoomAndStudent 映射。
    -->
    <select id="selectAllClassRoomByClassId" parameterType="int" resultMap="classRoomAndStudent">
        select *
        from student s, classroom c
        where s.classid = c.id and c.id = #{classId}
    </select>

</mapper>

执行查询:

 classRoomMapper.selectAllClassRoomByClassId(1).forEach(System.out::println);

查询结果:

ClassRoom(cid=1, className=101班, students=[Student(sid=1, stuName=Amy, classId=1)])
ClassRoom(cid=2, className=101班, students=[Student(sid=2, stuName=Bob, classId=1)])
ClassRoom(cid=3, className=101班, students=[Student(sid=3, stuName=javayz, classId=1)])

总结:

对象的关联使用association,集合的关联使用collection

参考:Mybatis从小白到小黑(四)MyBatis实现复杂环境的Sql查询

自关联查询一对多查询

建表SQL

下面时一张有着严格层级关系的商品分类层级表。

CREATE TABLE `t_goods_category`
(
    `id`              smallint unsigned                               NOT NULL AUTO_INCREMENT COMMENT '商品分类id',
    `name`            varchar(90) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '商品分类名称',
    `mobile_name`     varchar(64) CHARACTER SET utf8 COLLATE utf8_bin          DEFAULT '' COMMENT '手机端显示的商品分类名',
    `parent_id`       smallint unsigned                               NOT NULL DEFAULT '0' COMMENT '父id',
    `parent_id_path`  varchar(128) CHARACTER SET utf8 COLLATE utf8_bin         DEFAULT '' COMMENT '家族图谱',
    `level`           tinyint(1)                                               DEFAULT '0' COMMENT '等级',
    `sort_order`      tinyint unsigned                                NOT NULL DEFAULT '50' COMMENT '顺序排序',
    `is_show`         tinyint unsigned                                NOT NULL DEFAULT '1' COMMENT '是否显示',
    `image`           varchar(512) CHARACTER SET utf8 COLLATE utf8_bin         DEFAULT '' COMMENT '分类图片',
    `is_hot`          tinyint(1)                                               DEFAULT '0' COMMENT '是否推荐为热门分类',
    `cat_group`       tinyint(1)                                               DEFAULT '0' COMMENT '分类分组默认0',
    `commission_rate` tinyint(1)                                               DEFAULT '0' COMMENT '分佣比例',
    PRIMARY KEY (`id`),
    KEY `parent_id` (`parent_id`)
) ENGINE = InnoDB
  AUTO_INCREMENT = 917
  DEFAULT CHARSET = utf8mb3

从建表语句中可以得知,该表可以构成下面的常见关系。

  1. 自关联:
    • 表中 parent_id 字段指向同一表的 id 字段。每一行记录的 parent_id 表示其父分类的 id,从而形成了一个树形结构。
  2. 多级联查:
    • 层级关系: 通过 parent_id 字段,表可以定义分类的父子关系。level 字段可以标记每个分类的层级深度,帮助管理分类的层次。
    • 家族图谱: parent_id_path 字段记录了从根分类到当前分类的路径,可以帮助快速获取分类的完整路径和执行多级查询。

插入数据

在表中插入一些数据:

# 插入第一级商品分类
INSERT INTO `t_goods_category` VALUES (4, '家居、家具、家装、厨具', '家具', 0, '0_4', 1, 50, 1, '/Public/upload/category/2016/04-22/5719c41e70959.jpg', 0, 0, 0);

# 插入第二级商品分类
INSERT INTO `t_goods_category` VALUES (31, '生活日用', '生活日用', 4, '0_4_31', 2, 50, 1, '/Public/upload/category/2016/04-02/56ffa28b12f4f.jpg', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (32, '家装软饰', '家装软饰', 4, '0_4_32', 2, 50, 1, '/Public/upload/category/2016/04-02/56ffa28b12f4f.jpg', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (33, '宠物生活', '宠物生活', 4, '0_4_33', 2, 50, 1, '/Public/upload/category/2016/04-02/56ffa28b12f4f.jpg', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (34, '厨具', '厨具', 4, '0_4_34', 2, 50, 1, '/Public/upload/category/2016/04-02/56ffa28b12f4f.jpg', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (35, '家装建材', '家装建材', 4, '0_4_35', 2, 50, 1, '/Public/upload/category/2016/04-02/56ffa28b12f4f.jpg', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (36, '家纺', '家纺', 4, '0_4_36', 2, 50, 1, '/Public/upload/category/2016/04-02/56ffa28b12f4f.jpg', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (37, '家具', '家具', 4, '0_4_37', 2, 50, 1, '/Public/upload/category/2016/04-02/56ffa28b12f4f.jpg', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (38, '灯具', '灯具', 4, '0_4_38', 2, 50, 1, '/Public/upload/category/2016/04-02/56ffa28b12f4f.jpg', 0, 0, 0);

# 插入第三级商品分类
INSERT INTO `t_goods_category` VALUES (227, '清洁工具', '清洁工具', 31, '0_4_31_227', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (228, '收纳用品', '收纳用品', 31, '0_4_31_228', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (229, '雨伞雨具', '雨伞雨具', 31, '0_4_31_229', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (230, '浴室用品', '浴室用品', 31, '0_4_31_230', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (231, '缝纫/针织用品', '缝纫/针织用品', 31, '0_4_31_231', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (232, '洗晒/熨烫', '洗晒/熨烫', 31, '0_4_31_232', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (233, '净化除味', '净化除味', 31, '0_4_31_233', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (234, '节庆饰品', '节庆饰品', 32, '0_4_32_234', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (235, '手工/十字绣', '手工/十字绣', 32, '0_4_32_235', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (236, '桌布/罩件', '桌布/罩件', 32, '0_4_32_236', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (237, '钟饰', '钟饰', 32, '0_4_32_237', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (238, '地毯地垫', '地毯地垫', 32, '0_4_32_238', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (239, '装饰摆件', '装饰摆件', 32, '0_4_32_239', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (240, '沙发垫套/椅垫', '沙发垫套/椅垫', 32, '0_4_32_240', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (241, '花瓶花艺', '花瓶花艺', 32, '0_4_32_241', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (242, '帘艺隔断', '帘艺隔断', 32, '0_4_32_242', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (243, '创意家居', '创意家居', 32, '0_4_32_243', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (244, '相框/照片墙', '相框/照片墙', 32, '0_4_32_244', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (245, '保暖防护', '保暖防护', 32, '0_4_32_245', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (246, '装饰字画', '装饰字画', 32, '0_4_32_246', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (247, '香薰蜡烛', '香薰蜡烛', 32, '0_4_32_247', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (248, '墙贴/装饰贴', '墙贴/装饰贴', 32, '0_4_32_248', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (249, '水族用品', '水族用品', 33, '0_4_33_249', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (250, '宠物玩具', '宠物玩具', 33, '0_4_33_250', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (251, '宠物主粮', '宠物主粮', 33, '0_4_33_251', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (252, '宠物牵引', '宠物牵引', 33, '0_4_33_252', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (253, '宠物零食', '宠物零食', 33, '0_4_33_253', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (254, '宠物驱虫', '宠物驱虫', 33, '0_4_33_254', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (255, '猫砂/尿布', '猫砂/尿布', 33, '0_4_33_255', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (256, '洗护美容', '洗护美容', 33, '0_4_33_256', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (257, '家居日用', '家居日用', 33, '0_4_33_257', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (258, '医疗保健', '医疗保健', 33, '0_4_33_258', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (259, '出行装备', '出行装备', 33, '0_4_33_259', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (260, '剪刀菜饭', '剪刀菜饭', 34, '0_4_34_260', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (261, '厨房配件', '厨房配件', 34, '0_4_34_261', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (262, '水具酒具', '水具酒具', 34, '0_4_34_262', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (263, '餐具', '餐具', 34, '0_4_34_263', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (264, '茶具/咖啡具', '茶具/咖啡具', 34, '0_4_34_264', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (265, '烹饪锅具', '烹饪锅具', 34, '0_4_34_265', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (266, '电工电料', '电工电料', 35, '0_4_35_266', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (267, '墙地材料', '墙地材料', 35, '0_4_35_267', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (268, '装饰材料', '装饰材料', 35, '0_4_35_268', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (269, '装修服务', '装修服务', 35, '0_4_35_269', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (270, '沐浴花洒', '沐浴花洒', 35, '0_4_35_270', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (271, '灯饰照明', '灯饰照明', 35, '0_4_35_271', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (272, '开关插座', '开关插座', 35, '0_4_35_272', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (273, '厨房卫浴', '厨房卫浴', 35, '0_4_35_273', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (274, '油漆涂料', '油漆涂料', 35, '0_4_35_274', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (275, '五金工具', '五金工具', 35, '0_4_35_275', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (276, '龙头', '龙头', 35, '0_4_35_276', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (277, '床品套件', '床品套件', 36, '0_4_36_277', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (278, '抱枕靠垫', '抱枕靠垫', 36, '0_4_36_278', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (279, '被子', '被子', 36, '0_4_36_279', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (280, '布艺软饰', '布艺软饰', 36, '0_4_36_280', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (281, '被芯', '被芯', 36, '0_4_36_281', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (282, '窗帘窗纱', '窗帘窗纱', 36, '0_4_36_282', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (283, '床单被罩', '床单被罩', 36, '0_4_36_283', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (284, '蚊帐', '蚊帐', 36, '0_4_36_284', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (285, '床垫床褥', '床垫床褥', 36, '0_4_36_285', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (286, '凉席', '凉席', 36, '0_4_36_286', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (287, '电地毯', '电地毯', 36, '0_4_36_287', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (288, '毯子', '毯子', 36, '0_4_36_288', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (289, '毛巾浴巾', '毛巾浴巾', 36, '0_4_36_289', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (290, '餐厅家具', '餐厅家具', 37, '0_4_37_290', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (291, '电脑椅', '电脑椅', 37, '0_4_37_291', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (292, '书房家具', '书房家具', 37, '0_4_37_292', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (293, '衣柜', '衣柜', 37, '0_4_37_293', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (294, '储物家具', '储物家具', 37, '0_4_37_294', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (295, '茶几', '茶几', 37, '0_4_37_295', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (296, '阳台/户外', '阳台/户外', 37, '0_4_37_296', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (297, '电视柜', '电视柜', 37, '0_4_37_297', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (298, '商业办公', '商业办公', 37, '0_4_37_298', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (299, '餐桌', '餐桌', 37, '0_4_37_299', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (300, '卧室家具', '卧室家具', 37, '0_4_37_300', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (301, '床', '床', 37, '0_4_37_301', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (302, '电脑桌', '电脑桌', 37, '0_4_37_302', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (303, '客厅家具', '客厅家具', 37, '0_4_37_303', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (304, '床垫', '床垫', 37, '0_4_37_304', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (305, '鞋架/衣帽架', '鞋架/衣帽架', 37, '0_4_37_305', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (306, '客厅家具', '客厅家具', 37, '0_4_37_306', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (307, '沙发', '沙发', 37, '0_4_37_307', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (308, '吸顶灯', '吸顶灯', 38, '0_4_38_308', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (309, '吊灯', '吊灯', 38, '0_4_38_309', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (310, '筒灯射灯', '筒灯射灯', 38, '0_4_38_310', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (311, '氛围照明', '氛围照明', 38, '0_4_38_311', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (312, 'LED灯', 'LED灯', 38, '0_4_38_312', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (313, '节能灯', '节能灯', 38, '0_4_38_313', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (314, '落地灯', '落地灯', 38, '0_4_38_314', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (315, '五金电器', '五金电器', 38, '0_4_38_315', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (316, '应急灯/手电', '应急灯/手电', 38, '0_4_38_316', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (317, '台灯', '台灯', 38, '0_4_38_317', 3, 50, 1, '', 0, 0, 0);
INSERT INTO `t_goods_category` VALUES (318, '装饰灯', '装饰灯', 38, '0_4_38_318', 3, 50, 1, '', 0, 0, 0);

插入表中的数据遵循如下规则:

  1. 第一级别:level = 1,parent_id = 0
  2. 第二级别:level = 2,parent_id = 第一级别的id
  3. 第三级别:level = 3,parent_id = 第二级别的id

由此形成了层级关系。

查询输出

比如下面sql自我关联实现的多级联查:

SELECT
	t1.id,
	t1.`name`,
	t1.mobile_name,
	t1.parent_id,
	t2.id,
	t2.`name`,
	t2.mobile_name,
	t2.parent_id,
	t3.id,
	t3.`name`,
	t3.mobile_name,
	t3.parent_id 
FROM
	t_goods_category t1
	JOIN t_goods_category t2 ON t2.parent_id = t1.id
	JOIN t_goods_category t3 ON t3.parent_id = t2.id 
WHERE
	t1.LEVEL = 1 
	AND t1.parent_id = 0 

结果输出:

在这里插入图片描述

那么MyBatis中如何实现这种多级联查呢?接下来请看具体操作。

Java映射类

在当前对象中定义一个子级列表。

GoodsCategory.java

package com.ego.pojo;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.io.Serializable;
import java.util.List;

@Getter
@Setter
@ToString
public class GoodsCategory implements Serializable {




    /**
     * 商品分类id
     */

    private Short id;

    /**
     * 商品分类名称
     */
    private String name;

    /**
     * 手机端显示的商品分类名
     */
    private String mobileName;

    /**
     * 父id
     */
    private Short parentId;

    /**
     * 家族图谱
     */
    private String parentIdPath;

    /**
     * 等级
     */
    private Byte level;

    /**
     * 顺序排序
     */
    private Byte sortOrder;

    /**
     * 是否显示
     */
    private Byte isShow;

    /**
     * 分类图片
     */
    private String image;

    /**
     * 是否推荐为热门分类
     */
    private Byte isHot;

    /**
     * 分类分组默认0
     */
    private Byte catGroup;

    /**
     * 分佣比例
     */
    private Byte commissionRate;



    /**
     * 子级列表
     */
    List<GoodsCategory> children;
    
}

上面的javaBean中进行了一对多的自我关联,当前对象中包含了自我对象的List集合 List<GoodsCategory> children;

Mapper接口

定义一个查询方法,该方法通过传入当前对象的parentId和level查询对象集合。

GoodsCategoryMapper.java

public interface GoodsCategoryMapper {
    //    根据 parentId和level查询商品分类
    List<GoodsCategory> selectCategoryVoByParentIdAndLevel(@Param("parentId") Short parentId, @Param("level") Byte level);
}

多级联查

如何实现多级联查呢?即如何通过当前对象的 parentId 和 level 联查出它自我关联的子级列表children呢??

使用ResultMap

在 MyBatis 中,ResultMap 是一个重要的概念,用于将数据库查询结果映射到 Java 对象。ResultMap 提供了一种灵活的方式来映射复杂的查询结果,特别是当涉及到复杂的对象关系时。

通过mybatis配置,与接口GoodsCategoryMapper对应的xml文件如下:

GoodsCategoryMapper.xml
<?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.ego.mapper.GoodsCategoryMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.ego.pojo.GoodsCategory">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="mobile_name" property="mobileName"/>
        <result column="parent_id" property="parentId"/>
        <result column="parent_id_path" property="parentIdPath"/>
        <result column="level" property="level"/>
        <result column="sort_order" property="sortOrder"/>
        <result column="is_show" property="isShow"/>
        <result column="image" property="image"/>
        <result column="is_hot" property="isHot"/>
        <result column="cat_group" property="catGroup"/>
        <result column="commission_rate" property="commissionRate"/>
    </resultMap>

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id
        , name, mobile_name, parent_id, parent_id_path, level, sort_order, is_show, image, is_hot, cat_group, commission_rate
    </sql>


    <!--
  ResultMap 定义了如何将 SQL 查询的结果映射到 Java 对象的属性。
  这里的 resultMap 用于将 t_goods_category 表的数据映射到 GoodsCategory 对象中,并处理其子集合。
-->
    <resultMap id="GoodsCategoryAndChildren" type="com.ego.pojo.GoodsCategory">
        <!-- 映射主键字段 -->
        <id column="id" property="id"/>

        <!-- 映射普通字段 -->
        <result column="name" property="name"/>
        <result column="mobile_name" property="mobileName"/>
        <result column="parent_id" property="parentId"/>
        <result column="parent_id_path" property="parentIdPath"/>
        <result column="level" property="level"/>
        <result column="sort_order" property="sortOrder"/>
        <result column="is_show" property="isShow"/>
        <result column="image" property="image"/>
        <result column="is_hot" property="isHot"/>
        <result column="cat_group" property="catGroup"/>
        <result column="commission_rate" property="commissionRate"/>

        <!-- 映射子集合 -->
        <!-- property="children" 指定 GoodsCategory 对象中的 children 属性,用于存储子类别的集合 -->
        <!-- ofType="com.ego.pojo.GoodsCategory" 子集合中对象的类型为 GoodsCategory -->
        <!-- select="selectCategoryVoByParentIdAndLevel" 使用的查询语句 ID -->
        <!--  column="id" 根据当前类别的 id 列来获取子类别 这里的id传递给子对象的parentId -->
        <collection property="children" ofType="com.ego.pojo.GoodsCategory"
                    select="selectCategoryVoByParentIdAndLevel" column="id"/>
    </resultMap>

    <!--
      查询语句,用于从数据库中获取 GoodsCategory 对象及其子类别。
      这个查询会通过 resultMap 自动填充 GoodsCategory 对象及其 children 属性。
    -->
    <select id="selectCategoryVoByParentIdAndLevel" resultMap="GoodsCategoryAndChildren">
        <!-- 查询 t_goods_category 表中的字段 -->
        select id,
        name,
        mobile_name,
        parent_id,
        parent_id_path,
        level,
        sort_order,
        is_show,
        image,
        is_hot,
        cat_group,
        commission_rate
        from t_goods_category
        where parent_id = #{parentId}  <!-- 根据传入的 parentId 筛选记录 ,这里的parentId和父对象的id进行绑定 -->
            and level = #{level}         <!-- 根据传入的 level 筛选记录 -->
    </select>


</mapper>

  • 这个 resultMap 除了映射 GoodsCategory 的基本属性外,还定义了一个 children 属性,该属性用于存储子类别集合。

  • collection 元素指定了如何查询子类别:它使用 selectCategoryVoByParentIdAndLevel 查询,并将父类别的 id 作为条件来查找子类别,如图。

    在这里插入图片描述

执行查询

MyTest.java

 @Test
    void contextLoads() {

        // 查询顶级分类
        List<GoodsCategory> topLevelCategories = goodsCategoryMapper.selectCategoryVoByParentIdAndLevel((short) 0, (byte) 1);

        // 查询二级和三级分类
        for (GoodsCategory topLevel : topLevelCategories) {

            // 查询二级分类
            List<GoodsCategory> secondLevelCategories = goodsCategoryMapper.selectCategoryVoByParentIdAndLevel(topLevel.getId(), (byte) 2);

            // 查询三级分类
            for (GoodsCategory secondLevel : secondLevelCategories) {
                List<GoodsCategory> thirdLevelCategories = goodsCategoryMapper.selectCategoryVoByParentIdAndLevel(secondLevel.getId(), (byte) 3);
                if (thirdLevelCategories != null && thirdLevelCategories.size() > 0) {
                    secondLevel.setChildren(thirdLevelCategories); // 设置二级分类的子分类
                }
            }

            if (secondLevelCategories != null && secondLevelCategories.size() > 0) {
                topLevel.setChildren(secondLevelCategories); // 设置顶级分类的子分类
            }
        }

        topLevelCategories.forEach(System.out::println);

    }

输出结果如下:

在这里插入图片描述

这样子看起来很别扭,我们使用页面展示之后,效果如图所示。

在这里插入图片描述

上面的页面展示了三级联查的效果。

如果直接使用sql三级联查,一级列表和二级列表有太多重复的字段了,而且我们要的是一种层级的关系,处理起来比较麻烦,直接采用MyBatis的ResultMap配置,实现起来就很简单。

如果使用sql三级联查:

SELECT
	t1.id,
	t1.`name`,
	t1.mobile_name,
	t1.parent_id,
	t2.id,
	t2.`name`,
	t2.mobile_name,
	t2.parent_id,
	t3.id,
	t3.`name`,
	t3.mobile_name,
	t3.parent_id 
FROM
	t_goods_category t1
	JOIN t_goods_category t2 ON t2.parent_id = t1.id
	JOIN t_goods_category t3 ON t3.parent_id = t2.id 
WHERE
	t1.LEVEL = 1 
	AND t1.parent_id = 0 

效果如下:

在这里插入图片描述

可以看到,一级和二级有太多重复值。我们需要的是下面的这种层级关系:

------1-----
		-----2-----
				----3a----
				----3b---
				----3c----

多余的部分应该隐藏,如上面的页面展示效果一样。

参考:
Mybatis【17】-- Mybatis自关联查询一对多查询

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值