多对一查询(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班))
总结:
resultMap
中的column
和property
:column
表示数据库表中的列名。property
表示 Java 对象的属性名。它们应该是一一对应的关系。- 使用
select *
进行查询:当column
表示数据库表中的列名时,可以使用select *
进行查询。MyBatis 会自动将查询结果映射到 Java 对象的属性上。 - 使用别名进行查询:当
column
表示数据库表中select
语句查询的别名时,不要使用select *
。需要把别名和 Java 对象的属性名对应起来。 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
从建表语句中可以得知,该表可以构成下面的常见关系。
- 自关联:
- 表中
parent_id
字段指向同一表的id
字段。每一行记录的parent_id
表示其父分类的id
,从而形成了一个树形结构。
- 表中
- 多级联查:
- 层级关系: 通过
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);
插入表中的数据遵循如下规则:
- 第一级别:level = 1,parent_id = 0
- 第二级别:level = 2,parent_id = 第一级别的id
- 第三级别: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----
多余的部分应该隐藏,如上面的页面展示效果一样。