mybatis-plus多表联查怎么实现?注解方式查询学生所选课程
作者:会飞的种花家
前言
查阅了mybatis-plus官方文档,发现对多表联查的说明只言片语,也没有例子,只能自己琢磨琢磨如何利用mybatis-plus进行多表联查了。
使用xml的方式过于冗余,所以在查看了注解的使用后,果断选择利用注解实现。
注解学习
- @Select注解:其实就是在注解后加上相应的sql语句,写法与xml中的一致。
- @Results注解:有个id属性,可以理解为定义了一个方法,可以通过id引用,引用方式:ResultMap(“”)。@Results可以理解为一个结果集的映射,里面可以添加多个@Result。
- @Result注解:与xml中的《result/》差不多。主要解释的是many=@Many(),该属性指定的这个类的属性对应的结果是多个数据,即包装在list当中。还有的就是one=@One(),顾名思义,这就是单个对象。
实践出真知,不多说了,直接通过实例来学习吧!
需要解决问题
需要查询这个学生所选的所有课程,查询结果为:
学生id,学生name,课程Course对象的list集合
和查询这个教师所教的所有课程,查询结果为:
教师id,教师name,课程Course对象的list集合
上述问题实际上是一致的,可通过解决其一然后通过C+V解决
下面我们就学生来解决。
实际呢,学生类Student只有两个属性,那就是
- id,name(数据库与其一致)
- 而Course类也只有两个属性,也是
- id,name(数据库与其一致)
我们需要通过二者的关系表查询出我们想要的Course对象的list集合
这个关系表为Takes,也为Takes类。
其属性有
id,studentId,courseId(数据库与其一致)
问题描述完毕,下面我们开始实践。
数据库部分
学生表
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int NOT NULL,
`name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `name` (`name`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES ('18120107', '学生九');
INSERT INTO `student` VALUES ('18120108', '学生十号');
INSERT INTO `student` VALUES ('18120101', '张三');
INSERT INTO `student` VALUES ('18120102', '李四');
INSERT INTO `student` VALUES ('18120103', '王五');
INSERT INTO `student` VALUES ('18120105', '田七');
INSERT INTO `student` VALUES ('18120104', '赵六');
INSERT INTO `student` VALUES ('18120106', '陈八');课程表
-- ----------------------------
-- Table structure for course
-- ----------------------------
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
`id` int NOT NULL,
`name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of course
-- ----------------------------
INSERT INTO `course` VALUES ('87508', 'JavaWeb开发');
INSERT INTO `course` VALUES ('87502', '操作系统');
INSERT INTO `course` VALUES ('87505', '数据库原理');
INSERT INTO `course` VALUES ('87501', '数据结构');
INSERT INTO `course` VALUES ('87506', '算法设计与分析');
INSERT INTO `course` VALUES ('87504', '计算机组成');
INSERT INTO `course` VALUES ('87503', '计算机网络');
INSERT INTO `course` VALUES ('87509', '软件协同设计');
INSERT INTO `course` VALUES ('87507', '软件工程');选课表
(这里的course_name属性其实是多余的,暂且留着)
-- ----------------------------
-- Table structure for takes
-- ----------------------------
DROP TABLE IF EXISTS `takes`;
CREATE TABLE `takes` (
`id` int NOT NULL AUTO_INCREMENT,
`student_id` int NOT NULL,
`course_id` int NOT NULL,
`course_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_takes_student_id` (`student_id`),
KEY `fk_takes_course_id` (`course_id`),
KEY `fk_takes_course_name` (`course_name`),
CONSTRAINT `fk_takes_course_id` FOREIGN KEY (`course_id`) REFERENCES `course` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk_takes_course_name` FOREIGN KEY (`course_name`) REFERENCES `course` (`name`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk_takes_student_id` FOREIGN KEY (`student_id`) REFERENCES `student` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- ----------------------------
-- Records of takes
-- ----------------------------
INSERT INTO `takes` VALUES ('1', '18120101', '87501', '数据结构');
INSERT INTO `takes` VALUES ('2', '18120101', '87502', '操作系统');
INSERT INTO `takes` VALUES ('3', '18120101', '87503', '计算机网络');
INSERT INTO `takes` VALUES ('4', '18120102', '87501', '数据结构');
INSERT INTO `takes` VALUES ('5', '18120102', '87502', '操作系统');
INSERT INTO `takes` VALUES ('6', '18120102', '87503', '计算机网络');
INSERT INTO `takes` VALUES ('7', '18120103', '87509', '软件协同设计');
INSERT INTO `takes` VALUES ('8', '18120103', '87508', 'JavaWeb开发');
INSERT INTO `takes` VALUES ('9', '18120103', '87507', '软件工程');
INSERT INTO `takes` VALUES ('10', '18120104', '87503', '计算机网络');
INSERT INTO `takes` VALUES ('11', '18120104', '87506', '算法设计与分析');
INSERT INTO `takes` VALUES ('12', '18120104', '87507', '软件工程');
INSERT INTO `takes` VALUES ('13', '18120105', '87502', '操作系统');
INSERT INTO `takes` VALUES ('14', '18120105', '87504', '计算机组成');
INSERT INTO `takes` VALUES ('15', '18120105', '87505', '数据库原理');
INSERT INTO `takes` VALUES ('16', '18120106', '87501', '数据结构');
INSERT INTO `takes` VALUES ('17', '18120106', '87503', '计算机网络');
INSERT INTO `takes` VALUES ('18', '18120106', '87505', '数据库原理');
INSERT INTO `takes` VALUES ('19', '18120107', '87509', '软件协同设计');
INSERT INTO `takes` VALUES ('20', '18120107', '87506', '算法设计与分析');
INSERT INTO `takes` VALUES ('21', '18120107', '87504', '计算机组成');
INSERT INTO `takes` VALUES ('22', '18120108', '87506', '算法设计与分析');
INSERT INTO `takes` VALUES ('23', '18120108', '87507', '软件工程');
INSERT INTO `takes` VALUES ('24', '18120108', '87508', 'JavaWeb开发');实体类
用了lombok插件自动生成相关方法,getter,setter,构造器等
学生类
这里解释一下,List《Course》上方这**个@TableField(exist = false)**注解表示该属性不为数据库中的字段(其实是为了图方便直接写到了实体类里面,实际开发应该多写一个Vo类继承自Student而后再写这一个属性)
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.NONE)
private Integer id;
private String name;
@TableField(exist = false)
private List<Course> courses;
}
课程类
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class Course implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.NONE)
private Integer id;
private String name;
}
选课类
还是一样,这个courseName可以忽略,实际是不需要的
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class Takes implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private int studentId;
private int courseId;
private String courseName;
}
重点来了!
Mapper层
CourseMapper
这里明显能看明白,就是查出这个学生的所有课程,返回一个课程列表
public interface CourseMapper extends BaseMapper<Course> {
@Select("select * from course where id in(select course_id from takes where student_id=#{stuId})")
List<Course> queryCoursesByStuId(Integer stuId);
}
StudentMapper
全文就这里是最关键的了
首先,外部查询出这个学生对象,在@Results注解内,表示返回的实际结果集,
前面两个@Result,就是学生id和name了,这里不用说
主要是第三个@Result,通过javaType=List.class,many=@Many来指定这个Result的属性(通过CourseMapper中的方法来查询这个list集合)注意:要写方法名的全名称,包名+类名+方法名
@Select("select * from student where id=#{id}")
@Results({
@Result(column="id",property="id",id=true),
@Result(column = "name",property = "name"),
@Result(column="id",property="courses",javaType= List.class,
many=@Many(
select="com.usst.mapper.CourseMapper.queryCoursesByStuId"
)
)
})
Student queryStuById(@Param("id") Integer id);
其实写到这里,Service层不言而喻就出来了,不过还是再写一下吧
Service层
StudentService
public interface StudentService extends IService<Student> {
Student queryStuById(@Param("id") int id);
}
StudentServiceImpl
@Service
public class StudentServiceImpl extends ServiceImpl<StudentMapper, Student> implements StudentService {
@Autowired
private StudentMapper studentMapper;
@Override
public Student queryStuById(int id) {
return this.studentMapper.queryStuById(id);
}
}
结果测试
@Autowired
private StudentService studentService;
@Test
void test(){
System.out.println(studentService.queryStuById(18120101).toString());
}
测试结果截图:

总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
