MyBatis关联查询

刺骨的言语ヽ痛彻心扉 2023-10-14 12:40 131阅读 0赞

文章目录

  • 前言
  • 多对一关联 association
  • 一对多关联 collection
  • resultMap元素 处理结果集映射
  • 三表连接查询案例
    • 在数据库中的对应关系:
    • po 类:
    • Mapper接口:
    • service接口:
    • service实现类:
    • controller类:
    • 测试:(JSON格式)
    • 前端:

前言

提示:这里可以添加本文要记录的大概内容:
关联查询是指在一个查询中同时获取多个表中的数据,将它们结合在一起进行展示。

关联表需要两个及以上的表

数据库代码:

  1. DROP TABLE IF EXISTS student;
  2. DROP TABLE IF EXISTS teacher;
  3. CREATE TABLE teacher
  4. (
  5. tid int PRIMARY KEY auto_increment,
  6. tname VARCHAR(30)
  7. );
  8. CREATE TABLE student
  9. (
  10. sid int PRIMARY KEY auto_increment,
  11. sname VARCHAR(30),
  12. age int,
  13. tid int,
  14. foreign key (tid) references teacher(tid)
  15. );

生成实体类代码:

Student :

  1. public class Student {
  2. private Integer sid;
  3. private String sname;
  4. private Integer age;
  5. // 方便以后的扩展 po类
  6. private Teacher teacher;
  7. // 自动生成 Getter、Setter、toString()、有参无参方法
  8. }

Teacher :

  1. public class Teacher {
  2. private Integer tid;
  3. private String tname;
  4. // 自动生成 Getter、Setter、toString()、有参无参方法
  5. }

提示:以下是本篇文章正文内容,下面案例可供参考

  1. 主键列标记是id
  2. 普通列标记是result
  3. 关联查询的映射标记是association

多对一关联 association

<association>标签用于描述一个一对一的关联关系,它可以嵌套在<select>语句中,查询指定的关联表数据。

多个学生关联一个老师

在 main/java 文件夹中创建学生和老师的接口以及同名的映射文件

其中映射文件中的namespace是接口文件中的限定名

查询所有的学生数据,同时关联出对应的老师

所以在学生接口文件中:

  1. public interface StudentMapper {
  2. List<Student> queryAllStudents();
  3. }

在学生的映射文件中:SQL语句用的SQL99写法

  1. <mapper namespace="com.mybatis.mapper.StudentMapper">
  2. <!-- 结果的映射 -->
  3. <select id="queryAllStudents" resultMap="st">
  4. select sid,sname,age,t.tid,tname
  5. from student s join teacher t
  6. on s.tid = t.tid
  7. </select>
  8. </mapper>

SQL语句写完后要在Navicat中单独运行一下:防止有错
在这里插入图片描述
因为 t.tid,tname 和 teacher 数据不一致,所有需要在映射文件中加一个 resultMap 标记
请添加图片描述
需要把不一致的那两列单独映射到 teacher 属性里

所以最终学生的映射文件中的代码:

  1. <mapper namespace="com.mybatis.mapper.StudentMapper">
  2. <!-- st:随便起名 -->
  3. <resultMap type="Student" id="st">
  4. <!-- 主键列用id标记 -->
  5. <id column="sid" property="sid"/>
  6. <!-- 普通列用result标记 -->
  7. <result column="sname" property="sname"/>
  8. <result column="age" property="age"/>
  9. <!-- 关联查询的映射标记是association -->
  10. <association property="teacher" javaType="Teacher">
  11. <id column="tid" property="tid"/>
  12. <result column="tname" property="tname"/>
  13. </association>
  14. </resultMap>
  15. <!-- 结果的映射与上面的id值对应 -->
  16. <select id="queryAllStudents" resultMap="st">
  17. select sid,sname,age,t.tid,tname
  18. from student s join teacher t
  19. on s.tid = t.tid
  20. </select>
  21. </mapper>

测试类代码:

  1. @Test
  2. public void queryStudent() {
  3. StudentMapper mapper = session.getMapper(StudentMapper.class);
  4. List<Student> list = mapper.queryAllStudents();
  5. // foreach提示
  6. for (Student student : list) {
  7. System.out.println(student);
  8. }
  9. }

在这里插入图片描述

一对多关联 collection

<collection>标签用于描述一个一对多的关联关系,它可以嵌套在<select>语句中,查询关联表数据集合。

一个老师关联多少个学生

查询老师数据时,同时关联出老师对应的学生

这里要更老师的 po 类,加上:

  1. // 数组和集合能体现多个数据,这里用集合,因为不确定有多少学生
  2. private List<Student> students;
  3. // 生成Getter、Setter

所以在老师接口文件中:首先在接口中定义一个方法(查询所有老师)

  1. List<Teacher> queryAllTeachers();

在老师的映射文件中:
首先加上 namespace,映射文件中的namespace是接口文件中的限定名

  1. <select id="queryAllTeachers">
  2. select t.tid,tname,sid,sname,age
  3. from teacher t join student s
  4. on t.tid = s.tid
  5. </select>

SQL语句写完后要在Navicat中单独运行一下:防止有错
在这里插入图片描述
接下来配置映射:

  1. <!-- 结果映射 -->
  2. <resultMap type="Teacher" id="teacher">
  3. <id column="tid" property="tid"/>
  4. <result column="tname" property="tname"/>
  5. <!-- 一对多映射使用collection标记 --><!-- 属性名property是老师po类的students -->
  6. <collection property="students" ofType="Student">
  7. <id column="sid" property="sid"/>
  8. <result column="sname" property="sname"/>
  9. <result column="age" property="age"/>
  10. </collection>
  11. </resultMap>

最后要在 select 标签内加一个 resultMap,因为结果是自己单独配置的

所以最终老师的映射文件中的代码:

  1. <mapper namespace="com.mybatis.mapper.TeacherMapper">
  2. <!-- 结果映射 -->
  3. <resultMap type="Teacher" id="teacher">
  4. <id column="tid" property="tid"/>
  5. <result column="tname" property="tname"/>
  6. <!-- 一对多映射使用collection标记 --><!-- 属性名property是老师po类的students -->
  7. <collection property="students" ofType="Student">
  8. <id column="sid" property="sid"/>
  9. <result column="sname" property="sname"/>
  10. <result column="age" property="age"/>
  11. </collection>
  12. </resultMap>
  13. <select id="queryAllTeachers" resultMap="teacher">
  14. select t.tid,tname,sid,sname,age
  15. from teacher t join student s
  16. on t.tid = s.tid
  17. </select>
  18. </mapper>

接下来写测试类

  1. @Test
  2. public void queryTeacher() {
  3. TeacherMapper mapper = session.getMapper(TeacherMapper.class);
  4. List<Teacher> list = mapper.queryAllTeachers();
  5. // foreach循环
  6. for (Teacher teacher : list) {
  7. // 只输出老师数据
  8. System.out.println(teacher);
  9. // 得到学生
  10. List<Student> sl = teacher.getStudents();
  11. // 遍历sl学生集合,看看老师关联的学生数据
  12. for (Student student : sl) {
  13. System.out.println(student);
  14. }
  15. }
  16. }

在这里插入图片描述


resultMap元素 处理结果集映射

当数据表中的列和需要返回的对象的属性不完全一致, MyBatis是不会自动赋值的。此时,就可以使用<resultMap>元素进行处理。

<resultMap>元素的子元素<id>用于表示哪个列是主键,而<result>元素用于表示POJO和数据表中普通列的映射关系。

  1. <resultMap id="custResultMap" type="Customer">
  2. <id property="id" column="id" />
  3. <result property="custId" column="cust_id"/>
  4. <result property="name" column="name"/>
  5. </resultMap>

三表连接查询案例

效果:

用户登录选择体检套餐,男用户选择男士套餐,女用户选择女士套餐
在这里插入图片描述

在数据库中的对应关系:

在这里插入图片描述

po 类:

po类中要写三个po,因为有三个表

套餐明细表:

  1. import lombok.AllArgsConstructor;
  2. import lombok.Builder;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. //自动生成Getter、Setter和toString()方法
  6. @Data
  7. //自动生成无参构造函数
  8. @NoArgsConstructor
  9. //自动生成所有参数构造函数
  10. @AllArgsConstructor
  11. //自动生成Builder模式代码
  12. @Builder
  13. public class Setmealdetailed {
  14. private Long sdId;
  15. private Long smId;
  16. private Long ciId;
  17. //Setmealdetailed表checkitem表与一对一关系
  18. private Checkitem checkitem;
  19. }

检查项表:

  1. import lombok.AllArgsConstructor;
  2. import lombok.Builder;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. @Data
  6. @NoArgsConstructor
  7. @AllArgsConstructor
  8. @Builder
  9. public class Checkitem {
  10. private Integer ciId;
  11. private String ciName;
  12. private String ciContent;
  13. private String meaning;
  14. private String remarks;
  15. }

套餐表:

  1. import lombok.AllArgsConstructor;
  2. import lombok.Builder;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import java.util.List;
  6. @Data
  7. @NoArgsConstructor
  8. @AllArgsConstructor
  9. @Builder
  10. public class Setmeal {
  11. private Integer smId;
  12. private String name;
  13. private Integer type;
  14. private Double price;
  15. //中间表Setmealdetailed,多个数据用集合
  16. private List<Setmealdetailed> Setmealdetailed;
  17. }

Mapper接口:

  1. @Mapper
  2. public interface SetmealMapper {
  3. List<Setmeal> querySetmeals(int type);
  4. }

Mapper接口对应的映射文件:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4. <!-- SetmealMapper接口中的限定名 -->
  5. <mapper namespace="com.neuedu.tijian.mapper.SetmealMapper">
  6. <!-- 定义结果映射,type属性指定结果集对应的Java类型,id属性指定结果集的标识符 -->
  7. <resultMap type="Setmeal" id="setmeal">
  8. <!-- 定义主键映射,column属性指定数据库中的列名,property属性指定Java对象中的属性名 -->
  9. <id column="smId" property="smId"/>
  10. <!-- 定义普通映射,column属性指定数据库中的列名,property属性指定Java对象中的属性名 -->
  11. <result column="name" property="name"/>
  12. <result column="type" property="type"/>
  13. <result column="price" property="price"/>
  14. <collection property="Setmealdetailed" ofType="Setmealdetailed" javaType="List"> <!-- property——→po数据 -->
  15. <id column="sdId" property="sdId"/>
  16. <result column="smId" property="smId"/>
  17. <result column="ciId" property="ciId"/>
  18. <association property="checkitem" javaType="Checkitem"> <!-- po数据 -->
  19. <id column="ciId" property="ciId"/>
  20. <result column="ciName" property="ciName"/>
  21. <result column="ciContent" property="ciContent"/>
  22. <result column="meaning" property="meaning"/>
  23. <result column="remarks" property="remarks"/>
  24. </association>
  25. </collection>
  26. <!-- 结束结果映射 -->
  27. </resultMap>
  28. <select id="querySetmeals" resultMap="setmeal">
  29. select *
  30. from
  31. setmealdetailed sd
  32. join checkitem c
  33. on c.ciId=sd.ciId
  34. join setmeal sm
  35. on sd.smId=sm.smId
  36. where sm.type=#{
  37. type}
  38. </select>
  39. </mapper>

映射文件中的SQL语句:

这是一个查询语句,用于查询体检套餐。

  1. select *:查询所有列。
  2. from setmealdetailed sd join checkitem c on c.ciId=sd.ciId join setmeal sm on sd.smId=sm.smId:从setmealdetailed、checkitem和setmeal三个表中进行关联查询。通过sd和c表以ciId列为关联条件,关联出体检项目的详细信息;通过sd和sm表以smId列为关联条件,关联出体检套餐的信息。
  3. where sm.type=#{type}:根据体检套餐的类型进行条件查询,其中#{type}是一个占位符,表示需要从Java对象中动态获取type属性的值。

service接口:

SetmealService接口定义了查询体检套餐的方法querySetmeals,该方法接受一个type参数用于查询指定类型的体检套餐。

  1. public interface SetmealService {
  2. List<Setmeal> querySetmeals(int type);
  3. }

service实现类:

SetmealServiceImpl实现了SetmealService接口,通过自动装配SetmealMapper接口实现数据库查询,并将结果返回给调用方。

  1. @Service
  2. public class SetmealServiceImpl implements SetmealService {
  3. @Autowired
  4. private SetmealMapper mapper;
  5. @Override
  6. public List<Setmeal> querySetmeals(int type) {
  7. // TODO 自动生成的方法存根
  8. return mapper.querySetmeals(type);
  9. }
  10. }

controller类:

SetmealController类是一个基于Spring框架的RESTful风格的Web服务控制器,使用@RestController@RequestMapping注解声明了一个”/setmeal“的请求处理器。该类通过自动装配SetmealService接口来实现查询体检套餐的功能,当收到GET请求后,调用querySetmeals方法,并将结果返回给调用方。其中,type参数由请求URL中的查询参数指定。

  1. @RestController
  2. @RequestMapping("/setmeal")
  3. public class SetmealController {
  4. @Autowired
  5. private SetmealService service;
  6. @GetMapping
  7. public List<Setmeal> query(int type){
  8. return service.querySetmeals(type);
  9. }
  10. }

测试:(JSON格式)

在这里插入图片描述

前端:

  1. <template>
  2. <!-- 定义模板 -->
  3. <div class="wrapper">
  4. <!-- 定义页面外层容器 -->
  5. <header>
  6. <!-- 定义头部 -->
  7. <span><</span>
  8. <!-- 定义左侧返回按钮 -->
  9. 请您选择体检套餐
  10. <!-- 显示标题 -->
  11. </header>
  12. <div class="taocan" v-for="(setItem, i) in setItems" :key="i">
  13. <!-- 设置套餐容器,并遍历数据,为每个套餐设置唯一的key值 -->
  14. <div class="top">
  15. <!-- 定义套餐顶部栏 -->
  16. <div class="title">
  17. <!-- 定义套餐标题 -->
  18. <h3>{
  19. {
  20. this.text }}套餐</h3>
  21. <!-- 显示套餐名 -->
  22. <p>{
  23. {
  24. setItem.name }}</p>
  25. <!-- 显示套餐描述 -->
  26. </div>
  27. <div class="xiangqing">
  28. <!-- 定义套餐详情栏 -->
  29. <span>详情</span>
  30. <!-- 显示"详情"按钮 -->
  31. <i class="fa fa-sort-down" @click="showit(i)"></i>
  32. <!-- 显示下拉箭头 -->
  33. </div>
  34. </div>
  35. <div class="xiangmu" v-show="showFlag[i]">
  36. <!-- 定义项目表格,根据showFlag变量判断是否展开表格 -->
  37. <table>
  38. <tr>
  39. <!-- 定义表头 -->
  40. <th>检查类型</th>
  41. <th>检查内容</th>
  42. <th>检查意义</th>
  43. </tr>
  44. <tr class="tr" v-for="(setmealdetailed, j) in setItems[i].setmealdetailed" :key="j">
  45. <!-- 遍历数据,显示检查类型、检查内容、检查意义 -->
  46. <th> {
  47. {
  48. setmealdetailed.checkitem.ciName }} </th>
  49. <th> {
  50. {
  51. setmealdetailed.checkitem.ciContent }} </th>
  52. <th> {
  53. {
  54. setmealdetailed.checkitem.meaning }} </th>
  55. </tr>
  56. </table>
  57. </div>
  58. </div>
  59. </div>
  60. </template>
  61. <!-- 定义javascript代码 -->
  62. <script>
  63. import axios from 'axios';
  64. export default {
  65. data() {
  66. // 数据初始化
  67. return {
  68. user: {
  69. }, // 用户数据
  70. setItems: [], // 套餐数据
  71. text: '女士', // 默认套餐标题中“女士”
  72. showFlag: [] // 用于判断项目表格是否展开的标识数组
  73. }
  74. },
  75. methods: {
  76. // 定义方法
  77. showit(i) {
  78. this.showFlag[i] = !this.showFlag[i]
  79. // 点击详情按钮切换表格的展开状态
  80. }
  81. },
  82. components: {
  83. },
  84. computed: {
  85. },
  86. watch: {
  87. },
  88. mounted() {
  89. // 组件挂载时执行的代码 (获取用户信息)
  90. this.user = JSON.parse(sessionStorage.getItem('login'))
  91. // 获取用户信息
  92. axios.get(`/api/setmeal?type=${
  93. this.user.sex}`)
  94. .then(resp => {
  95. // 获取套餐信息(返回的数据 )
  96. this.setItems = resp.data
  97. // 将后台获取的套餐数据赋值给setItems
  98. this.showFlag = Array(this.setItems.length).fill(false)
  99. // 使用长度为setItems.length,初始值为false的数组,用于判断项目表格是否展开
  100. if (this.user.sex == 1) {
  101. this.text = '男士'
  102. // 如果登录用户性别为男,将套餐标题中的“女士”修改为“男士”
  103. }
  104. })
  105. }
  106. }
  107. </script>

css样式:

  1. <style scoped>
  2. .wrapper {
  3. /* 定义样式,设置首页外层容器的属性 */
  4. width: 100vw;
  5. min-height: 100%;
  6. height: auto;
  7. background-color: #f7f7f7; /* 设置背景颜色为#f7f7f7 */
  8. }
  9. header {
  10. /* 定义样式,设置头部的属性 */
  11. width: 100vw;
  12. height: 12vw;
  13. flex: 0 0 12vw;
  14. background-color: #fff; /* 设置背景颜色为#fff */
  15. display: flex; /* 将header设置成flex布局 */
  16. align-items: center; /* 设置垂直居中 */
  17. font-size: 6vw; /* 设置字体大小为6vw */
  18. color: 4vw; /* 设置字体颜色为#4vw */
  19. }
  20. header span {
  21. /* 定义样式,设置头部标题的属性 */
  22. margin-left: 4vw;
  23. margin-right: 18vw;
  24. }
  25. .taocan {
  26. /* 定义样式,设置套餐容器的属性 */
  27. width: 94vw;
  28. display: flex; /* 将套餐容器设置成flex布局 */
  29. flex-direction: column; /* 设置flex布局方向为垂直 */
  30. }
  31. .top {
  32. /* 定义样式,设置套餐顶部栏的属性 */
  33. width: 94vw;
  34. height: 15vh;
  35. background-color: #fff; /* 设置背景颜色为#fff */
  36. margin-left: 3vw;
  37. margin-top: 4vw;
  38. display: flex; /* 将套餐顶部栏设置成flex布局 */
  39. align-items: center; /* 设置垂直居中 */
  40. }
  41. .title {
  42. /* 定义样式,设置套餐标题的属性 */
  43. width: 40vw;
  44. height: 15vh;
  45. font-size: 4vw; /* 设置字体大小为4vw */
  46. display: flex; /* 将套餐标题设置成flex布局 */
  47. flex-direction: column; /* 设置flex布局方向为垂直 */
  48. justify-content: center; /* 设置垂直居中 */
  49. margin-left: 10vw;
  50. }
  51. .title p {
  52. /* 定义样式,设置套餐标题中的段落的属性 */
  53. color: #575656; /* 设置字体颜色为#575656 */
  54. }
  55. .xiangqing {
  56. /* 定义样式,设置套餐详情栏的属性 */
  57. width: 15vw;
  58. height: 10vw;
  59. font-size: 4vw; /* 设置字体大小为4vw */
  60. display: flex; /* 将套餐详情栏设置成flex布局 */
  61. align-items: center; /* 设置垂直居中 */
  62. justify-content: space-around; /* 设置子元素之间平均分配空间 */
  63. margin-left: 20vw;
  64. border-left: 0.3px solid #cdcdcd; /* 设置左侧边框为0.3px宽度、#cdcdcd颜色 */
  65. color: #575656;
  66. }
  67. .xiangmu {
  68. /* 定义样式,设置项目表格的属性 */
  69. width: 94vw;
  70. margin-left: 3vw;
  71. }
  72. table {
  73. /* 定义样式,设置表格的属性 */
  74. border-collapse: collapse; /* 设置表格边框合并 */
  75. }
  76. .xiangmu th {
  77. /* 定义样式,设置表头的属性 */
  78. font-size: 3.5vw;
  79. width: 94vw;
  80. border: 0.2vw solid #bcbcbc; /* 设置表格边框宽度为0.2vw、颜色为#bcbcbc */
  81. }
  82. .tr {
  83. /* 定义样式,设置表格行的属性 */
  84. background-color: #fff; /* 设置背景颜色为#fff */
  85. }
  86. </style>

发表评论

表情:
评论列表 (有 0 条评论,131人围观)

还没有评论,来说两句吧...

相关阅读

    相关 MyBatis关联查询

      前言 我们进行数据库查询时往往需要的不止一张表的数据,需要将多张表的数据一起查询出来,大家学习过数据库的连接查询,那么在MyBatis中如何将有关系的多张表数据进行关联

    相关 Mybatis关联查询

    Mybatis关联查询 演示的数据表为: ![Center][] 部门和员工属于一对多关系。接下来演示的是根据员工编号查询员工信息并关联查询所在部门信息,查询所有部