本文将向大家介绍 MyBatis 中的多表关联查询
主要通过两个示例来说明在 MyBatis 中如何实现 一对多,多对一 和 多对多 查询
第一个示例:老师和学生关联
老师和学生是一对多的关系,一名老师管教多名学生
学生和老师是多对一的关系,多名学生被一名老师管教
那么如何查询所有学生以及管教他们的老师呢?
又如何查询所有老师和他所管教的学生呢?
第二个示例:账户和课程关联
在网上选课系统中,账户和课程是多对多关系,
一个账户可以选择多门课程,而一门课程也可以被多个账户选择
那么如何查询账户以及此账户所选择的课程呢?
又如何查询课程以及选择这门课程的账户呢?
下面我们就来一步一步解决这两个问题吧
CREATE TABLE `t_teacher` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`name` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
CREATE TABLE `t_student` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`name` varchar(30) DEFAULT NULL,
`tid` int(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
学生类
package com.jarreet.test.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Student {
private Integer id;
private String name;
private Integer tid;
// 学生关联一个老师
private Teacher teacher;
}
老师类
package com.jarreet.test.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.util.ArrayList;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Teacher {
private Integer id;
private String name;
// 老师关联一群学生
private List<Student> studentList = new ArrayList<>();
}
在 application.yml 配置 mybatis
mybatis:
type-aliases-package: com.jarreet.test.pojo
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
在 TestApplication 启动类上加上包扫描注解
@MapperScan("com.jarreet.test.dao")
我们根据上面搭建的环境,给出一个查询需求:查询所有学生的信息及他们的老师
StudentDao.java
package com.jarreet.test.dao;
import com.jarreet.test.pojo.Student;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface StudentDao {
public List<Student> getAllStudents();
}
StudentMapper.xml
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jarreet.test.dao.StudentDao">
<resultMap id="studentAndTeacher" type="com.jarreet.test.pojo.Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="stid"/>
<association property="teacher" javaType="com.jarreet.test.pojo.Teacher">
<id property="id" column="tid"/>
<result property="name" column="tname"/>
association>
resultMap>
<select id="getAllStudents" resultMap="studentAndTeacher">
select
s.id sid,
s.name sname,
s.tid stid,
t.id tid,
t.name tname
from
t_student s, t_teacher t
where
s.tid = t.id
select>
mapper>
StudentService.java
package com.jarreet.test.service;
import com.jarreet.test.pojo.Student;
import java.util.List;
public interface StudentService {
List<Student> getAllStudents();
}
StudentServiceImpl.java
package com.jarreet.test.service.impl;
import com.jarreet.test.dao.StudentDao;
import com.jarreet.test.pojo.Student;
import com.jarreet.test.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentDao studentDao;
@Override
public List<Student> getAllStudents() {
return studentDao.getAllStudents();
}
}
StudentController.java
package com.jarreet.test.controller;
import com.jarreet.test.pojo.Student;
import com.jarreet.test.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
public class StudentController {
@Autowired
private StudentService studentService;
@RequestMapping("/student")
@ResponseBody
public String studentList() {
List<Student> allStudents = studentService.getAllStudents();
return allStudents.toString();
}
}
我们根据上面搭建的环境,给出一个查询需求:查询所有老师的信息及他们的学生
TeacherDao.java
package com.jarreet.test.dao;
import com.jarreet.test.pojo.Teacher;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface TeacherDao {
public List<Teacher> getAllTeachers();
}
TeacherMapper.xml
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jarreet.test.dao.TeacherDao">
<resultMap id="teacherAndStudent" type="com.jarreet.test.pojo.Teacher">
<id property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="studentList" ofType="com.jarreet.test.pojo.Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="stid"/>
collection>
resultMap>
<select id="getAllTeachers" resultMap="teacherAndStudent">
select
t.id tid,
t.name tname,
s.id sid,
s.name sname,
s.tid stid
from
t_teacher t, t_student s
where
t.id = s.tid
select>
mapper>
TeacherService.java
package com.jarreet.test.service;import com.jarreet.test.pojo.Teacher;import java.util.List;public interface TeacherService { List<Teacher> getAllTeachers();}
TeacherServiceImpl.java
package com.jarreet.test.service.impl;import com.jarreet.test.dao.TeacherDao;import com.jarreet.test.pojo.Teacher;import com.jarreet.test.service.TeacherService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Servicepublic class TeacherServiceImpl implements TeacherService { @Autowired private TeacherDao teacherDao; @Override public List<Teacher> getAllTeachers() { return teacherDao.getAllTeachers(); }}
TeacherController.java
package com.jarreet.test.controller;import com.jarreet.test.pojo.Teacher;import com.jarreet.test.service.TeacherService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import java.util.List;@Controllerpublic class TeacherController { @Autowired private TeacherService teacherService; @RequestMapping("/teacher") @ResponseBody public String teacherList() { List<Teacher> allTeachers = teacherService.getAllTeachers(); return allTeachers.toString(); }}
多对多查询其实就是双向的一对多查询
这里我们给出一个多对多的情境:网上选课,一个账号可以选择多个课程,而一个课程也有可能被多个账号选择
那么我们现在需要一个账号表 t_account ,一个课程表 t_course;但是这两张表就够了吗?
当然不够,开头说了,多对多查询是双向的一对多,那么我们就需要一张中间表 account_course 来实现
CREATE TABLE `t_account`(`id` INT(11) PRIMARY KEY AUTO_INCREMENT,`name` VARCHAR(11) DEFAULT NULL);CREATE TABLE `t_course`(`id` INT(11) PRIMARY KEY AUTO_INCREMENT,`name` VARCHAR(11) DEFAULT NULL);CREATE TABLE `account_course`(`id` INT(11) PRIMARY KEY AUTO_INCREMENT,`aid` INT(11) DEFAULT NULL,`cid` INT(11) DEFAULT NULL);
账户类
package com.jarreet.test.pojo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import lombok.ToString;import java.util.List;@Data@NoArgsConstructor@AllArgsConstructor@ToStringpublic class Account { private Integer id; private String name; private List<Course> courseList;}
课程类
package com.jarreet.test.pojo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import lombok.ToString;import java.util.List;@Data@NoArgsConstructor@AllArgsConstructor@ToStringpublic class Course { private Integer id; private String name; private List<Account> accountList;}
AccountDao.java
package com.jarreet.test.dao;import com.jarreet.test.pojo.Account;import org.springframework.stereotype.Repository;@Repositorypublic interface AccountDao { public Account findById(Integer id);}
AccountMapper.xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.jarreet.test.dao.AccountDao"> <resultMap id="accountMap" type="com.jarreet.test.pojo.Account"> <id property="id" column="aid"/> <result property="name" column="aname"/> <collection property="courseList" ofType="com.jarreet.test.pojo.Course"> <id property="id" column="cid"/> <result property="name" column="cname"/> collection> resultMap> <select id="findById" resultMap="accountMap"> select a.id aid, a.name aname, c.id cid, c.name cname from t_account a, t_course c, account_course ac where a.id = #{id} and a.id = ac.aid and c.id = ac.cid select>mapper>
CourseDao.java
package com.jarreet.test.dao;import com.jarreet.test.pojo.Course;import org.springframework.stereotype.Repository;@Repositorypublic interface CouseDao { public Course findById(Integer id);}
CourseMapper.xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.jarreet.test.dao.CouseDao"> <resultMap id="courseMap" type="com.jarreet.test.pojo.Course"> <id property="id" column="cid"/> <result property="name" column="cname"/> <collection property="accountList" ofType="com.jarreet.test.pojo.Account"> <id property="id" column="aid"/> <result property="name" column="aname"/> collection> resultMap> <select id="findById" resultMap="courseMap"> select c.id cid, c.name cname, a.id aid, a.name aname from t_account a, t_course c, account_course ac where c.id = #{id} and a.id = ac.aid and c.id = ac.cid select>mapper>
AccountService.java
package com.jarreet.test.service;import com.jarreet.test.pojo.Account;public interface AccountService { Account findAccountById(Integer id);}
AccountServiceImpl.java
package com.jarreet.test.service.impl;import com.jarreet.test.dao.AccountDao;import com.jarreet.test.pojo.Account;import com.jarreet.test.service.AccountService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; @Override public Account findAccountById(Integer id) { return accountDao.findById(id); }}
CourseService.java
package com.jarreet.test.service;import com.jarreet.test.pojo.Course;public interface CourseService { Course findCourseById(Integer id);}
CourseServiceImpl.java
package com.jarreet.test.service.impl;import com.jarreet.test.dao.CourseDao;import com.jarreet.test.pojo.Course;import com.jarreet.test.service.CourseService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class CourseServiceImpl implements CourseService { @Autowired private CourseDao courseDao; @Override public Course findCourseById(Integer id) { return courseDao.findById(id); }}
AccountController.java
package com.jarreet.test.controller;import com.jarreet.test.pojo.Account;import com.jarreet.test.service.AccountService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;@Controllerpublic class AccountController { @Autowired private AccountService accountService; @RequestMapping("/account/{id}") @ResponseBody public String findAccountById(@PathVariable Integer id) { Account account = accountService.findAccountById(id); return account.toString(); }}
CourseController.java
package com.jarreet.test.controller;import com.jarreet.test.pojo.Course;import com.jarreet.test.service.CourseService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;@Controllerpublic class CourseController { @Autowired private CourseService courseService; @RequestMapping("/course/{id}") @ResponseBody public String findCourseById(@PathVariable Integer id) { Course course = courseService.findCourseById(id); return course.toString(); }}
至此,关于 Mybatis 的多表查询就讲解完毕了,下面我们简单做个总结。
Mybatis的多表查询无非就是做好实体类属性和mapper.xml文件的映射,其中:
association 是将结果集封装成⼀个实体类的对象(⼀个⽬标对象),并通过 JavaType 设置数类型;
collection 是将结果集封装成⼀个集合对象(多个⽬标对象),并通过 ofType 设置数据类型;
当多对一时,使用 association
当一对多时,使用 collection;
而当多对多时,无非就是进行一个双向的一对多绑定
特别要注意的几个点:
- 搭建环境时,要在配置文件中配置好 mybatis 信息,否则有可能报错
- 需要开启包扫描,要么在启动类上加上 @MapperScan 注解,要么在 持久化接口上加上 @Mapper 注解
- 当需要级联的对象的属性名和自身的属性名相同时,需要起别名,防止冲突覆盖