1. 程式人生 > 其它 >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

(二)根據測試資料庫建立實體類

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

(四)結果展示


三、一對多查詢

我們根據上面搭建的環境,給出一個查詢需求:查詢所有老師的資訊及他們的學生

(一)資料持久化層

  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;

    @Service
    public 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;

@Controller
public 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
);

(二)根據資料庫表建立實體類

  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
    @ToString
    public 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
    @ToString
    public 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;

    @Repository
    public 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;

    @Repository
    public 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;

    @Service
    public 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;

    @Service
    public 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;

    @Controller
    public 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;

    @Controller
    public 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 註解

  • 當需要級聯的物件的屬性名和自身的屬性名相同時,需要起別名,防止衝突覆蓋