MyBatis关联查询
文章目录
- 前言
- 多对一关联 association
- 一对多关联 collection
- resultMap元素 处理结果集映射
- 三表连接查询案例
- 在数据库中的对应关系:
- po 类:
- Mapper接口:
- service接口:
- service实现类:
- controller类:
- 测试:(JSON格式)
- 前端:
前言
提示:这里可以添加本文要记录的大概内容:
关联查询是指在一个查询中同时获取多个表中的数据,将它们结合在一起进行展示。
关联表需要两个及以上的表
数据库代码:
DROP TABLE IF EXISTS student;
DROP TABLE IF EXISTS teacher;
CREATE TABLE teacher
(
tid int PRIMARY KEY auto_increment,
tname VARCHAR(30)
);
CREATE TABLE student
(
sid int PRIMARY KEY auto_increment,
sname VARCHAR(30),
age int,
tid int,
foreign key (tid) references teacher(tid)
);
生成实体类代码:
Student :
public class Student {
private Integer sid;
private String sname;
private Integer age;
// 方便以后的扩展 po类
private Teacher teacher;
// 自动生成 Getter、Setter、toString()、有参无参方法
}
Teacher :
public class Teacher {
private Integer tid;
private String tname;
// 自动生成 Getter、Setter、toString()、有参无参方法
}
提示:以下是本篇文章正文内容,下面案例可供参考
- 主键列标记是
id
- 普通列标记是
result
- 关联查询的映射标记是
association
多对一关联 association
<association>
标签用于描述一个一对一的关联关系,它可以嵌套在<select>
语句中,查询指定的关联表数据。
多个学生关联一个老师
在 main/java 文件夹中创建学生和老师的接口以及同名的映射文件
其中映射文件中的namespace
是接口文件中的限定名
查询所有的学生数据,同时关联出对应的老师
所以在学生接口文件中:
public interface StudentMapper {
List<Student> queryAllStudents();
}
在学生的映射文件中:SQL语句用的SQL99写法
<mapper namespace="com.mybatis.mapper.StudentMapper">
<!-- 结果的映射 -->
<select id="queryAllStudents" resultMap="st">
select sid,sname,age,t.tid,tname
from student s join teacher t
on s.tid = t.tid
</select>
</mapper>
SQL语句写完后要在Navicat中单独运行一下:防止有错
因为 t.tid,tname 和 teacher 数据不一致,所有需要在映射文件中加一个 resultMap
标记
需要把不一致的那两列单独映射到 teacher 属性里
所以最终学生的映射文件中的代码:
<mapper namespace="com.mybatis.mapper.StudentMapper">
<!-- st:随便起名 -->
<resultMap type="Student" id="st">
<!-- 主键列用id标记 -->
<id column="sid" property="sid"/>
<!-- 普通列用result标记 -->
<result column="sname" property="sname"/>
<result column="age" property="age"/>
<!-- 关联查询的映射标记是association -->
<association property="teacher" javaType="Teacher">
<id column="tid" property="tid"/>
<result column="tname" property="tname"/>
</association>
</resultMap>
<!-- 结果的映射与上面的id值对应 -->
<select id="queryAllStudents" resultMap="st">
select sid,sname,age,t.tid,tname
from student s join teacher t
on s.tid = t.tid
</select>
</mapper>
测试类代码:
@Test
public void queryStudent() {
StudentMapper mapper = session.getMapper(StudentMapper.class);
List<Student> list = mapper.queryAllStudents();
// foreach提示
for (Student student : list) {
System.out.println(student);
}
}
一对多关联 collection
<collection>
标签用于描述一个一对多的关联关系,它可以嵌套在<select>
语句中,查询关联表数据集合。
一个老师关联多少个学生
查询老师数据时,同时关联出老师对应的学生
这里要更老师的 po 类,加上:
// 数组和集合能体现多个数据,这里用集合,因为不确定有多少学生
private List<Student> students;
// 生成Getter、Setter
所以在老师接口文件中:首先在接口中定义一个方法(查询所有老师)
List<Teacher> queryAllTeachers();
在老师的映射文件中:
首先加上 namespace,映射文件中的namespace
是接口文件中的限定名
<select id="queryAllTeachers">
select t.tid,tname,sid,sname,age
from teacher t join student s
on t.tid = s.tid
</select>
SQL语句写完后要在Navicat中单独运行一下:防止有错
接下来配置映射:
<!-- 结果映射 -->
<resultMap type="Teacher" id="teacher">
<id column="tid" property="tid"/>
<result column="tname" property="tname"/>
<!-- 一对多映射使用collection标记 --><!-- 属性名property是老师po类的students -->
<collection property="students" ofType="Student">
<id column="sid" property="sid"/>
<result column="sname" property="sname"/>
<result column="age" property="age"/>
</collection>
</resultMap>
最后要在 select 标签内加一个 resultMap
,因为结果是自己单独配置的
所以最终老师的映射文件中的代码:
<mapper namespace="com.mybatis.mapper.TeacherMapper">
<!-- 结果映射 -->
<resultMap type="Teacher" id="teacher">
<id column="tid" property="tid"/>
<result column="tname" property="tname"/>
<!-- 一对多映射使用collection标记 --><!-- 属性名property是老师po类的students -->
<collection property="students" ofType="Student">
<id column="sid" property="sid"/>
<result column="sname" property="sname"/>
<result column="age" property="age"/>
</collection>
</resultMap>
<select id="queryAllTeachers" resultMap="teacher">
select t.tid,tname,sid,sname,age
from teacher t join student s
on t.tid = s.tid
</select>
</mapper>
接下来写测试类
@Test
public void queryTeacher() {
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
List<Teacher> list = mapper.queryAllTeachers();
// foreach循环
for (Teacher teacher : list) {
// 只输出老师数据
System.out.println(teacher);
// 得到学生
List<Student> sl = teacher.getStudents();
// 遍历sl学生集合,看看老师关联的学生数据
for (Student student : sl) {
System.out.println(student);
}
}
}
resultMap元素 处理结果集映射
当数据表中的列和需要返回的对象的属性不完全一致, MyBatis是不会自动赋值的。此时,就可以使用<resultMap>
元素进行处理。
<resultMap>
元素的子元素<id>
用于表示哪个列是主键,而<result>
元素用于表示POJO和数据表中普通列的映射关系。
<resultMap id="custResultMap" type="Customer">
<id property="id" column="id" />
<result property="custId" column="cust_id"/>
<result property="name" column="name"/>
</resultMap>
三表连接查询案例
效果:
用户登录选择体检套餐,男用户选择男士套餐,女用户选择女士套餐
在数据库中的对应关系:
po 类:
po类中要写三个po,因为有三个表
套餐明细表:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
//自动生成Getter、Setter和toString()方法
@Data
//自动生成无参构造函数
@NoArgsConstructor
//自动生成所有参数构造函数
@AllArgsConstructor
//自动生成Builder模式代码
@Builder
public class Setmealdetailed {
private Long sdId;
private Long smId;
private Long ciId;
//Setmealdetailed表checkitem表与一对一关系
private Checkitem checkitem;
}
检查项表:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Checkitem {
private Integer ciId;
private String ciName;
private String ciContent;
private String meaning;
private String remarks;
}
套餐表:
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Setmeal {
private Integer smId;
private String name;
private Integer type;
private Double price;
//中间表Setmealdetailed,多个数据用集合
private List<Setmealdetailed> Setmealdetailed;
}
Mapper接口:
@Mapper
public interface SetmealMapper {
List<Setmeal> querySetmeals(int type);
}
Mapper接口对应的映射文件:
<?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">
<!-- SetmealMapper接口中的限定名 -->
<mapper namespace="com.neuedu.tijian.mapper.SetmealMapper">
<!-- 定义结果映射,type属性指定结果集对应的Java类型,id属性指定结果集的标识符 -->
<resultMap type="Setmeal" id="setmeal">
<!-- 定义主键映射,column属性指定数据库中的列名,property属性指定Java对象中的属性名 -->
<id column="smId" property="smId"/>
<!-- 定义普通映射,column属性指定数据库中的列名,property属性指定Java对象中的属性名 -->
<result column="name" property="name"/>
<result column="type" property="type"/>
<result column="price" property="price"/>
<collection property="Setmealdetailed" ofType="Setmealdetailed" javaType="List"> <!-- property——→po数据 -->
<id column="sdId" property="sdId"/>
<result column="smId" property="smId"/>
<result column="ciId" property="ciId"/>
<association property="checkitem" javaType="Checkitem"> <!-- po数据 -->
<id column="ciId" property="ciId"/>
<result column="ciName" property="ciName"/>
<result column="ciContent" property="ciContent"/>
<result column="meaning" property="meaning"/>
<result column="remarks" property="remarks"/>
</association>
</collection>
<!-- 结束结果映射 -->
</resultMap>
<select id="querySetmeals" resultMap="setmeal">
select *
from
setmealdetailed sd
join checkitem c
on c.ciId=sd.ciId
join setmeal sm
on sd.smId=sm.smId
where sm.type=#{
type}
</select>
</mapper>
映射文件中的SQL语句:
这是一个查询语句,用于查询体检套餐。
select *
:查询所有列。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列为关联条件,关联出体检套餐的信息。where sm.type=#{type}
:根据体检套餐的类型进行条件查询,其中#{type}
是一个占位符,表示需要从Java对象中动态获取type属性的值。
service接口:
SetmealService
接口定义了查询体检套餐的方法querySetmeals
,该方法接受一个type
参数用于查询指定类型的体检套餐。
public interface SetmealService {
List<Setmeal> querySetmeals(int type);
}
service实现类:
SetmealServiceImpl
实现了SetmealService
接口,通过自动装配SetmealMapper
接口实现数据库查询,并将结果返回给调用方。
@Service
public class SetmealServiceImpl implements SetmealService {
@Autowired
private SetmealMapper mapper;
@Override
public List<Setmeal> querySetmeals(int type) {
// TODO 自动生成的方法存根
return mapper.querySetmeals(type);
}
}
controller类:
SetmealController
类是一个基于Spring框架的RESTful风格的Web服务控制器,使用@RestController
和@RequestMapping
注解声明了一个”/setmeal
“的请求处理器。该类通过自动装配SetmealService接口来实现查询体检套餐的功能,当收到GET请求后,调用querySetmeals
方法,并将结果返回给调用方。其中,type
参数由请求URL中的查询参数指定。
@RestController
@RequestMapping("/setmeal")
public class SetmealController {
@Autowired
private SetmealService service;
@GetMapping
public List<Setmeal> query(int type){
return service.querySetmeals(type);
}
}
测试:(JSON格式)
前端:
<template>
<!-- 定义模板 -->
<div class="wrapper">
<!-- 定义页面外层容器 -->
<header>
<!-- 定义头部 -->
<span><</span>
<!-- 定义左侧返回按钮 -->
请您选择体检套餐
<!-- 显示标题 -->
</header>
<div class="taocan" v-for="(setItem, i) in setItems" :key="i">
<!-- 设置套餐容器,并遍历数据,为每个套餐设置唯一的key值 -->
<div class="top">
<!-- 定义套餐顶部栏 -->
<div class="title">
<!-- 定义套餐标题 -->
<h3>{
{
this.text }}套餐</h3>
<!-- 显示套餐名 -->
<p>{
{
setItem.name }}</p>
<!-- 显示套餐描述 -->
</div>
<div class="xiangqing">
<!-- 定义套餐详情栏 -->
<span>详情</span>
<!-- 显示"详情"按钮 -->
<i class="fa fa-sort-down" @click="showit(i)"></i>
<!-- 显示下拉箭头 -->
</div>
</div>
<div class="xiangmu" v-show="showFlag[i]">
<!-- 定义项目表格,根据showFlag变量判断是否展开表格 -->
<table>
<tr>
<!-- 定义表头 -->
<th>检查类型</th>
<th>检查内容</th>
<th>检查意义</th>
</tr>
<tr class="tr" v-for="(setmealdetailed, j) in setItems[i].setmealdetailed" :key="j">
<!-- 遍历数据,显示检查类型、检查内容、检查意义 -->
<th> {
{
setmealdetailed.checkitem.ciName }} </th>
<th> {
{
setmealdetailed.checkitem.ciContent }} </th>
<th> {
{
setmealdetailed.checkitem.meaning }} </th>
</tr>
</table>
</div>
</div>
</div>
</template>
<!-- 定义javascript代码 -->
<script>
import axios from 'axios';
export default {
data() {
// 数据初始化
return {
user: {
}, // 用户数据
setItems: [], // 套餐数据
text: '女士', // 默认套餐标题中“女士”
showFlag: [] // 用于判断项目表格是否展开的标识数组
}
},
methods: {
// 定义方法
showit(i) {
this.showFlag[i] = !this.showFlag[i]
// 点击详情按钮切换表格的展开状态
}
},
components: {
},
computed: {
},
watch: {
},
mounted() {
// 组件挂载时执行的代码 (获取用户信息)
this.user = JSON.parse(sessionStorage.getItem('login'))
// 获取用户信息
axios.get(`/api/setmeal?type=${
this.user.sex}`)
.then(resp => {
// 获取套餐信息(返回的数据 )
this.setItems = resp.data
// 将后台获取的套餐数据赋值给setItems
this.showFlag = Array(this.setItems.length).fill(false)
// 使用长度为setItems.length,初始值为false的数组,用于判断项目表格是否展开
if (this.user.sex == 1) {
this.text = '男士'
// 如果登录用户性别为男,将套餐标题中的“女士”修改为“男士”
}
})
}
}
</script>
css样式:
<style scoped>
.wrapper {
/* 定义样式,设置首页外层容器的属性 */
width: 100vw;
min-height: 100%;
height: auto;
background-color: #f7f7f7; /* 设置背景颜色为#f7f7f7 */
}
header {
/* 定义样式,设置头部的属性 */
width: 100vw;
height: 12vw;
flex: 0 0 12vw;
background-color: #fff; /* 设置背景颜色为#fff */
display: flex; /* 将header设置成flex布局 */
align-items: center; /* 设置垂直居中 */
font-size: 6vw; /* 设置字体大小为6vw */
color: 4vw; /* 设置字体颜色为#4vw */
}
header span {
/* 定义样式,设置头部标题的属性 */
margin-left: 4vw;
margin-right: 18vw;
}
.taocan {
/* 定义样式,设置套餐容器的属性 */
width: 94vw;
display: flex; /* 将套餐容器设置成flex布局 */
flex-direction: column; /* 设置flex布局方向为垂直 */
}
.top {
/* 定义样式,设置套餐顶部栏的属性 */
width: 94vw;
height: 15vh;
background-color: #fff; /* 设置背景颜色为#fff */
margin-left: 3vw;
margin-top: 4vw;
display: flex; /* 将套餐顶部栏设置成flex布局 */
align-items: center; /* 设置垂直居中 */
}
.title {
/* 定义样式,设置套餐标题的属性 */
width: 40vw;
height: 15vh;
font-size: 4vw; /* 设置字体大小为4vw */
display: flex; /* 将套餐标题设置成flex布局 */
flex-direction: column; /* 设置flex布局方向为垂直 */
justify-content: center; /* 设置垂直居中 */
margin-left: 10vw;
}
.title p {
/* 定义样式,设置套餐标题中的段落的属性 */
color: #575656; /* 设置字体颜色为#575656 */
}
.xiangqing {
/* 定义样式,设置套餐详情栏的属性 */
width: 15vw;
height: 10vw;
font-size: 4vw; /* 设置字体大小为4vw */
display: flex; /* 将套餐详情栏设置成flex布局 */
align-items: center; /* 设置垂直居中 */
justify-content: space-around; /* 设置子元素之间平均分配空间 */
margin-left: 20vw;
border-left: 0.3px solid #cdcdcd; /* 设置左侧边框为0.3px宽度、#cdcdcd颜色 */
color: #575656;
}
.xiangmu {
/* 定义样式,设置项目表格的属性 */
width: 94vw;
margin-left: 3vw;
}
table {
/* 定义样式,设置表格的属性 */
border-collapse: collapse; /* 设置表格边框合并 */
}
.xiangmu th {
/* 定义样式,设置表头的属性 */
font-size: 3.5vw;
width: 94vw;
border: 0.2vw solid #bcbcbc; /* 设置表格边框宽度为0.2vw、颜色为#bcbcbc */
}
.tr {
/* 定义样式,设置表格行的属性 */
background-color: #fff; /* 设置背景颜色为#fff */
}
</style>
还没有评论,来说两句吧...