MyBatis多表关联查询

  • Post author:
  • Post category:其他


本文将向大家介绍 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

image-20210810120108788

mybatis多表查询-学生数据表



(二)根据测试数据库创建实体类
  1. 学生类

    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;
    }
    

  2. 老师类

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<>();
}



(三)文件配置
  1. 在 application.yml 配置 mybatis

    mybatis:
      type-aliases-package: com.jarreet.test.pojo
      mapper-locations: classpath:mapper/*.xml
      configuration:
        map-underscore-to-camel-case: true
    

  2. 在 TestApplication 启动类上加上包扫描注解

    @MapperScan("com.jarreet.test.dao")
    



二、多对一查询

我们根据上面搭建的环境,给出一个查询需求:查询所有学生的信息及他们的老师



(一)数据持久化层
  1. 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();
    }
    

  2. StudentMapper.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.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>
        <!--
            查询学生列表
            注意:要给属性取别名,
            因为老师和学生都有id和name属性,
            若不取别名则会冲突覆盖
        -->
        <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>
    



(二)业务逻辑层
  1. StudentService.java

    package com.jarreet.test.service;
    
    import com.jarreet.test.pojo.Student;
    
    import java.util.List;
    
    public interface StudentService {
    
        List<Student> getAllStudents();
    }
    

  2. 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();
    }
}



(四)结果展示

image-20210810094738768




三、一对多查询

我们根据上面搭建的环境,给出一个查询需求:查询所有老师的信息及他们的学生



(一)数据持久化层
  1. 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();
    }
    

  2. TeacherMapper.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.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>
    



(二)业务逻辑层
  1. TeacherService.java

    package com.jarreet.test.service;import com.jarreet.test.pojo.Teacher;import java.util.List;public interface TeacherService {        List<Teacher> getAllTeachers();}
    

  2. 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();    }}



(四)结果展示

image-20210810100308480




四、多对多查询

多对多查询其实就是双向的一对多查询

这里我们给出一个多对多的情境:网上选课,一个账号可以选择多个课程,而一个课程也有可能被多个账号选择

那么我们现在需要一个账号表 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);

image-20210810105036774

image-20210810105101500

image-20210810105120112



(二)根据数据库表创建实体类
  1. 账户类

    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;}
    

  2. 课程类

    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;}
    



(三)数据持久化层
  1. 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);}
    

  2. AccountMapper.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.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>
    

  3. 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);}
    

  4. CourseMapper.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.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>
    



(四)业务逻辑层
  1. AccountService.java

    package com.jarreet.test.service;import com.jarreet.test.pojo.Account;public interface AccountService {    Account findAccountById(Integer id);}
    

  2. 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);    }}
    

  3. CourseService.java

    package com.jarreet.test.service;import com.jarreet.test.pojo.Course;public interface CourseService {        Course findCourseById(Integer id);}
    

  4. 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);    }}
    



(五)访问控制层
  1. 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();    }}
    

  2. 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();    }}
    



(六)结果展示

image-20210810104927283

image-20210810104948422


至此,关于 Mybatis 的多表查询就讲解完毕了,下面我们简单做个总结。

Mybatis的多表查询无非就是做好实体类属性和mapper.xml文件的映射,其中:

  • association 是将结果集封装成⼀个实体类的对象(⼀个⽬标对象),并通过 JavaType 设置数类型;

  • collection 是将结果集封装成⼀个集合对象(多个⽬标对象),并通过 ofType 设置数据类型;

  • 当多对一时,使用 association

  • 当一对多时,使用 collection;

  • 而当多对多时,无非就是进行一个双向的一对多绑定

特别要注意的几个点:

  • 搭建环境时,要在配置文件中配置好 mybatis 信息,否则有可能报错
  • 需要开启包扫描,要么在启动类上加上 @MapperScan 注解,要么在 持久化接口上加上 @Mapper 注解
  • 当需要级联的对象的属性名和自身的属性名相同时,需要起别名,防止冲突覆盖



版权声明:本文为IceTiger_原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。