SpringBoot实现1对1、1对多、多对多关联查询——基于SpringBoot和Vue的后台管理系统项目系列博客(十八)

系列文章目录

  1. 系统功能演示——基于SpringBoot和Vue的后台管理系统项目系列博客(一)
  2. Vue2安装并集成ElementUI——基于SpringBoot和Vue的后台管理系统项目系列博客(二)
  3. Vue2前端主体框架搭建——基于SpringBoot和Vue的后台管理系统项目系列博客(三)
  4. SpringBoot后端初始框架搭建——基于SpringBoot和Vue的后台管理系统项目系列博客(四)
  5. SpringBoot集成Mybatis——基于SpringBoot和Vue的后台管理系统项目系列博客(五)
  6. SpringBoot实现增删改查——基于SpringBoot和Vue的后台管理系统项目系列博客(六)
  7. SpringBoot实现分页查询——基于SpringBoot和Vue的后台管理系统项目系列博客(七)
  8. SpringBoot实现集成Mybatis-Plus和SwaggerUI——基于SpringBoot和Vue的后台管理系统项目系列博客(八)
  9. Vue实现增删改查——基于SpringBoot和Vue的后台管理系统项目系列博客(九)
  10. SpringBoot实现代码生成器——基于SpringBoot和Vue的后台管理系统项目系列博客(十)
  11. Vue使用路由——基于SpringBoot和Vue的后台管理系统项目系列博客(十一)
  12. SpringBoot和Vue实现导入导出——基于SpringBoot和Vue的后台管理系统项目系列博客(十二)
  13. SpringBoot和Vue实现用户登录注册与异常处理——基于SpringBoot和Vue的后台管理系统项目系列博客(十三)
  14. SpringBoot和Vue实现用户个人信息展示与保存与集成JWT——基于SpringBoot和Vue的后台管理系统项目系列博客(十四)
  15. SpringBoot和Vue实现文件上传与下载——基于SpringBoot和Vue的后台管理系统项目系列博客(十五)
  16. SpringBoot和Vue整合ECharts——基于SpringBoot和Vue的后台管理系统项目系列博客(十六)
  17. SpringBoot和Vue实现权限菜单功能——基于SpringBoot和Vue的后台管理系统项目系列博客(十七)
  18. SpringBoot实现1对1、1对多、多对多关联查询——基于SpringBoot和Vue的后台管理系统项目系列博客(十八)
  19. 用户前台页面设计与实现——基于SpringBoot和Vue的后台管理系统项目系列博客(十九)
  20. SpringBoot集成Redis实现缓存——基于SpringBoot和Vue的后台管理系统项目系列博客(二十)
  21. SpringBoot和Vue集成高德地图——基于SpringBoot和Vue的后台管理系统项目系列博客(二十一)
  22. SpringBoot和Vue集成视频播放组件——基于SpringBoot和Vue的后台管理系统项目系列博客(二十二)
  23. SpringBoot和Vue集成Markdown和多级评论——基于SpringBoot和Vue的后台管理系统项目系列博客(二十三)

项目资源下载

  1. GitHub下载地址
  2. Gitee下载地址
  3. 项目MySql数据库文件


前言

  今天的主要内容包括:创建所需要的数据库表和后端基础代码、设置用户角色之间的关系、用户角色关系的后端功能实现以及前端页面设计、实现一对一关联查询、实现一对多关联查询、实现多对多关联查询等。今天的内容也比较多,都是数据库的一些基本操作,也有一定难度,还请坚持住。下面就开始今天的学习!


一、创建所需要的数据库表和后端基础代码

  1. 按照如下内容输入数据表
    在这里插入图片描述
  2. 然后保存并命名为“course”
    在这里插入图片描述
  3. 然后resources/templates/controller.java.vm全部替换为如下内容
package ${package.Controller};


import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ironmanjay.springboot.common.Result;

import ${package.Service}.${table.serviceName};
import ${package.Entity}.${entity};

#if(${restControllerStyle})
import org.springframework.web.bind.annotation.RestController;
#else
import org.springframework.stereotype.Controller;
#end
#if(${superControllerClassPackage})
import ${superControllerClassPackage};
#end

/**
 * <p>
 * $!{table.comment} 前端控制器
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
#if(${restControllerStyle})
@RestController
#else
@Controller
#end
@RequestMapping("#if(${package.ModuleName})/${package.ModuleName}#end/#if(${controllerMappingHyphenStyle})${controllerMappingHyphen}#else${table.entityPath}#end")
#if(${kotlin})
class ${table.controllerName}#if(${superControllerClass}) : ${superControllerClass}()#end

#else
    #if(${superControllerClass})
    public class ${table.controllerName} extends ${superControllerClass} {
    #else
    public class ${table.controllerName} {
    #end

@Resource
private ${table.serviceName} ${table.entityPath}Service;

// 新增或者更新
@PostMapping
public Result save(@RequestBody ${entity} ${table.entityPath}) {
    ${table.entityPath}Service.saveOrUpdate(${table.entityPath});
        return Result.success();
        }

@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
    ${table.entityPath}Service.removeById(id);
        return Result.success();
        }

@PostMapping("/del/batch")
public Result deleteBatch(@RequestBody List<Integer> ids) {
    ${table.entityPath}Service.removeByIds(ids);
        return Result.success();
        }

@GetMapping
public Result findAll() {
        return Result.success(${table.entityPath}Service.list());
        }

@GetMapping("/{id}")
public Result findOne(@PathVariable Integer id) {
        return Result.success(${table.entityPath}Service.getById(id));
        }

@GetMapping("/page")
public Result findPage(@RequestParam Integer pageNum,
@RequestParam Integer pageSize) {
        QueryWrapper<${entity}> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("id");
        return Result.success(${table.entityPath}Service.page(new Page<>(pageNum, pageSize), queryWrapper));
        }

        }

#end
  1. 然后将CodeGenerator.java中生成的表名(course)输入进去
    在这里插入图片描述
  2. 还要注意代码生成器自己的输出路径
    在这里插入图片描述
  3. 然后使用代码生成器生成代码,发现已经成功生成代码了
    在这里插入图片描述

二、设置用户角色之间的关系

  1. 然后在后台中编辑和新增角色
    在这里插入图片描述
  2. 然后设置每个用户的角色,当然这个完全可以根据用户自己的喜好创建
    在这里插入图片描述

三、用户角色关系的后端功能实现以及前端页面设计

  1. 新建关于“课程管理”页面的路由
    在这里插入图片描述
  2. 最后生成新的菜单如下所示
    在这里插入图片描述
  3. 然后给管理员添加“课程管理”的页面权限

在这里插入图片描述

  1. 绑定之后需要重新登陆
    在这里插入图片描述
  2. 重新登陆后我们发现已经成功将“课程管理”页面授权了,但是还没有页面内容,下面的工作就是完成“课程管理”的页面内容
    在这里插入图片描述
  3. 在UserController.java中新增查询当前角色的所有用户的功能函数
    在这里插入图片描述
  4. 然后将Course.vue中的内容全部替换为如下内容
<template>
    <div>
        <div style="margin: 10px 0">
            <el-input style="width: 200px" placeholder="请输入名称" suffix-icon="el-icon-search" v-model="name"></el-input>
            <el-button class="ml-5" type="primary" @click="load">搜索</el-button>
            <el-button type="warning" @click="reset">重置</el-button>
        </div>
        <div style="margin: 10px 0">
            <el-button type="primary" @click="handleAdd">新增 <i class="el-icon-circle-plus-outline"></i></el-button>
            <el-popconfirm
                    class="ml-5"
                    confirm-button-text='确定'
                    cancel-button-text='我再想想'
                    icon="el-icon-info"
                    icon-color="red"
                    title="您确定批量删除这些数据吗?"
                    @confirm="delBatch"
            >
                <el-button type="danger" slot="reference">批量删除 <i class="el-icon-remove-outline"></i></el-button>
            </el-popconfirm>

        </div>
        <el-table :data="tableData" border stripe :header-cell-class-name="'headerBg'"
                  @selection-change="handleSelectionChange">
            <el-table-column type="selection" width="55"></el-table-column>
            <el-table-column prop="id" label="ID" width="80"></el-table-column>
            <el-table-column prop="name" label="课程名称"></el-table-column>
            <el-table-column prop="score" label="分数"></el-table-column>
            <el-table-column prop="times" label="课时"></el-table-column>
            <el-table-column prop="teacher" label="授课老师"></el-table-column>
            <el-table-column label="启用">
                <template slot-scope="scope">
                    <el-switch v-model="scope.row.state" active-color="#13ce66" inactive-color="#ccc"
                               @change="changeEnable(scope.row)"></el-switch>
                </template>
            </el-table-column>
            <el-table-column label="操作" width="200" align="center">
                <template slot-scope="scope">
                    <el-button type="success" @click="handleEdit(scope.row)">编辑 <i class="el-icon-edit"></i></el-button>
                    <el-popconfirm
                            class="ml-5"
                            confirm-button-text='确定'
                            cancel-button-text='我再想想'
                            icon="el-icon-info"
                            icon-color="red"
                            title="您确定删除吗?"
                            @confirm="del(scope.row.id)">
                        <el-button type="danger" slot="reference">删除 <i class="el-icon-remove-outline"></i></el-button>
                    </el-popconfirm>
                </template>
            </el-table-column>
        </el-table>

        <div style="padding: 10px 0">
            <el-pagination
                    @size-change="handleSizeChange"
                    @current-change="handleCurrentChange"
                    :current-page="pageNum"
                    :page-sizes="[2, 5, 10, 20]"
                    :page-size="pageSize"
                    layout="total, sizes, prev, pager, next, jumper"
                    :total="total">
            </el-pagination>
        </div>

        <el-dialog title="课程信息" :visible.sync="dialogFormVisible" width="30%">
            <el-form label-width="80px" size="small">
                <el-form-item label="名称">
                    <el-input v-model="form.name" autocomplete="off"></el-input>
                </el-form-item>
                <el-form-item label="学分">
                    <el-input v-model="form.score" autocomplete="off"></el-input>
                </el-form-item>
                <el-form-item label="课时">
                    <el-input v-model="form.times" autocomplete="off"></el-input>
                </el-form-item>
                <el-form-item label="老师">
                    <el-select clearable v-model="form.teacherId" placeholder="请选择">
                        <el-option v-for="item in teachers" :key="item.id" :label="item.nickname"
                                   :value="item.id"></el-option>
                    </el-select>
                </el-form-item>
            </el-form>
            <div slot="footer" class="dialog-footer">
                <el-button @click="dialogFormVisible = false">取 消</el-button>
                <el-button type="primary" @click="save">确 定</el-button>
            </div>
        </el-dialog>

    </div>
</template>

<script>
    export default {
        name: "Course",
        data() {
            return {
                form: {},
                tableData: [],
                name: '',
                multipleSelection: [],
                pageNum: 1,
                pageSize: 10,
                total: 0,
                dialogFormVisible: false,
                teachers: [],
                user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {},
            }
        },
        created() {
            this.load()
        },
        methods: {
            load() {
                this.request.get("/course/page", {
                    params: {
                        pageNum: this.pageNum,
                        pageSize: this.pageSize,
                        name: this.name,
                    }
                }).then(res => {
                    this.tableData = res.data.records
                    this.total = res.data.total
                });
                this.request.get("/user/role/ROLE_TEACHER").then(res => {
                    this.teachers = res.data
                })
            },
            changeEnable(row) {
                this.request.post("/course/update", row).then(res => {
                    if (res.code === '200') {
                        this.$message.success("操作成功")
                    }
                })
            },
            del(id) {
                this.request.delete("/course/" + id).then(res => {
                    if (res.code === '200') {
                        this.$message.success("删除成功")
                        this.load()
                    } else {
                        this.$message.error("删除失败")
                    }
                })
            },
            handleSelectionChange(val) {
                console.log(val)
                this.multipleSelection = val
            },
            delBatch() {
                let ids = this.multipleSelection.map(v => v.id)  // [{}, {}, {}] => [1,2,3]
                this.request.post("/course/del/batch", ids).then(res => {
                    if (res.code === '200') {
                        this.$message.success("批量删除成功")
                        this.load()
                    } else {
                        this.$message.error("批量删除失败")
                    }
                })
            },
            save() {
                this.request.post("/course", this.form).then(res => {
                    if (res) {
                        this.$message.success("保存成功")
                        this.dialogFormVisible = false
                        this.load()
                    } else {
                        this.$message.error("保存失败")
                    }
                })
            },
            reset() {
                this.name = ""
                this.load()
            },
            handleAdd() {
                this.dialogFormVisible = true
                this.form = {}
            },
            handleEdit(row) {
                this.form = row
                this.dialogFormVisible = true
            },
            handleSizeChange(pageSize) {
                console.log(pageSize)
                this.pageSize = pageSize
                this.load()
            },
            handleCurrentChange(pageNum) {
                console.log(pageNum)
                this.pageNum = pageNum
                this.load()
            },
            download(url) {
                window.open(url)
            }
        }
    }
</script>

<style scoped>

</style>
  1. 然后来到前端发现已经成功显示“课程管理”界面了,我们在里面新增数据,这个数据可以随意新增
    在这里插入图片描述
  2. 然后我们发现已经成功新增课程信息了
    在这里插入图片描述
  3. 接下来我们还要给本门课程分配授课老师
    在这里插入图片描述
  4. 分配之后页面并没有显示
    在这里插入图片描述
  5. 但是数据库已经存进数据了
    在这里插入图片描述
  6. 因为我们在数据库中存储的是授课老师的id,而在页面要展示授课老师的名称,如下所示。那么接下来的任务就是实现关联查询,可以根据授课老师的id显示授课老师的名称
    在这里插入图片描述

四、实现一对一关联查询

  1. 在前端的request.js中修改超时时间为30s
    在这里插入图片描述
  2. 然后在Course.java中新增teacher字段,要注意注解要和我保持一样,因为这个是默认不存在的,如果不加这个注解就会报错
    在这里插入图片描述
  3. 然后在CourseMapper.xml中新建关联查询的sql语句
    在这里插入图片描述
  4. 然后在CourseMapper.java中新建如下mapper
    在这里插入图片描述
  5. 然后在CourseServiceImpl.java加入如下代码
    在这里插入图片描述
  6. 然后在ICourseService.java中加入如下代码
    在这里插入图片描述
  7. 然后修改CourseController.java中的findPage函数,返回所需的数据
    在这里插入图片描述
  8. 然后此时我们来到前端发现,我们刚才分配的授课老师已经成功显示了,这样我们就实现了关联查询
    在这里插入图片描述

五、实现一对多关联查询

  1. 当我们授课老师讲授多门课的时候,就要建立授课老师和课程名称的多对一关系,所以接下来的任务就是如何实现查看老师所讲授的所有课程
    在这里插入图片描述
  2. 我们首先修改User.vue中的如下几个部分,给老师角色新增查看所有教授课程和课程学分的点击表格
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  3. 然后我们来到前端测试发现已经成功显示了,当然现在还没有数据,那么接下来的任务就是成功显示数据
    在这里插入图片描述
  4. 然后我们再修改User.vue中的如下部分
    在这里插入图片描述
  5. 然后来到前端测试,发现角色一栏已经不是英文标记了,而是英文标记对应的中文标签,这样好看一些
    在这里插入图片描述
  6. 接下来就要实现我们授课老师和课程名称的多对一关系了,首先在User.java实体类中新增如下字段,为了保存当前老师教授的所有课程
    在这里插入图片描述
  7. 然后修改User.vue中此部分内容
    在这里插入图片描述
  8. 然后将UserMapper.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.ironmanjay.springboot.mapper.UserMapper">

    <update id="updatePassword">
        update sys_user set `password` = #{userPasswordDTO.newPassword} where `username` = #{userPasswordDTO.username};
    </update>

    <resultMap id="pageUser" type="com.ironmanjay.springboot.entity.User">
        <result column="id" property="id"></result>
        <result column="username" property="username"></result>
        <result column="nickname" property="nickname"></result>
        <result column="email" property="email"></result>
        <result column="phone" property="phone"></result>
        <result column="address" property="address"></result>
        <result column="create_time" property="createTime"></result>
        <result column="avatar" property="avatar"></result>
        <result column="role" property="role"></result>
        <collection property="courses" javaType="java.util.ArrayList" ofType="com.ironmanjay.springboot.entity.Course">
            <result column="name" property="name"></result>
            <result column="score" property="score"></result>
        </collection>
    </resultMap>

    <select id="findPage" resultMap="pageUser">
        select sys_user.*,course.* from sys_user
        left join course
        on sys_user.id = course.teacher_id
        <where>
            <if test="username != null and username != ''">
                and sys_user.username like concat('%',#{username},'%')
            </if>
            <if test="email != null and email != ''">
                and sys_user.email like concat('%',#{email},'%')
            </if>
            <if test="address != null and address != ''">
                and sys_user.address like concat('%',#{address},'%')
            </if>
        </where>
    </select>

</mapper>
  1. 然后在UserMapper.java中加入如下内容
    在这里插入图片描述
  2. 然后在UserServiceImpl.java中加入如下内容
    在这里插入图片描述
  3. 然后在IUserService.java中加入如下内容
    在这里插入图片描述
  4. 然后修改UserController.java中的如下部分
    在这里插入图片描述
  5. 最后来到前端测试,发现已经成功建立起了授课老师和课程名称的一对多关系
    在这里插入图片描述

六、实现多对多关联查询

  1. 首先新建一个关系数据表,如下配置
    在这里插入图片描述
  2. 名称为student_course
    在这里插入图片描述
  3. 为了让学生可以看到课程信息,给学生角色分配课程管理菜单
    在这里插入图片描述
  4. 为了避免学生角色新增、删除和编辑课程,在Course.vue中添加如下代码
    在这里插入图片描述
    在这里插入图片描述
  5. 然后在Course.vue中新增学生选课的按钮,并修改按钮间距
    在这里插入图片描述
  6. 然后在CourseMapper.xml中新增如下内容
    在这里插入图片描述
  7. 然后在CourseMapper.java中加入如下内容
    在这里插入图片描述
  8. 然后在CourseServiceImpl.java中加入如下内容
    在这里插入图片描述
  9. 然后在ICourseService.java中加入如下内容
    在这里插入图片描述
  10. 然后在CourseController.java中加入如下内容
    在这里插入图片描述
  11. 然后在Course.vue中加入如下函数
    在这里插入图片描述
  12. 然后此时前端界面已经有了选课按钮
    在这里插入图片描述
  13. 为了后面可以成功以学生角色选课,我们还需要保存当前学生角色的id,我么我们需要在UserDTO.java中新增一个字段,这样在学生角色登录的时候我们就可以获取到他的id了
    在这里插入图片描述
  14. 然后我们使用学生角色重新登陆
    在这里插入图片描述
  15. 然后我们使用登录的学生角色选课,发现已经成功选课了
    在这里插入图片描述
  16. 然后查看数据表,发现数据也已经成功保存了
    在这里插入图片描述
  17. 为了让管理员可以看到学生选了哪些课程,我们首先以管理员角色登录
    在这里插入图片描述
  18. 然后在User.vue中新增查看选课信息的按钮
    在这里插入图片描述
  19. 然后修改User.vue中如下几处内容
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  20. 此时已经可以在前端看见查看学生的选课记录按钮了,现在我们只需要获取到相应数据即可
    在这里插入图片描述
  21. 然后来到User.java中新增学生选课字段
    在这里插入图片描述
  22. 为了查到学生的选课记录,将UserMapper.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.ironmanjay.springboot.mapper.UserMapper">

    <update id="updatePassword">
        update sys_user set `password` = #{userPasswordDTO.newPassword} where `username` = #{userPasswordDTO.username};
    </update>

    <resultMap id="pageUser" type="com.ironmanjay.springboot.entity.User">
        <result column="id" property="id"></result>
        <result column="username" property="username"></result>
        <result column="nickname" property="nickname"></result>
        <result column="email" property="email"></result>
        <result column="phone" property="phone"></result>
        <result column="address" property="address"></result>
        <result column="create_time" property="createTime"></result>
        <result column="avatar" property="avatar"></result>
        <result column="role" property="role"></result>
        <collection property="courses" javaType="java.util.ArrayList" ofType="com.ironmanjay.springboot.entity.Course">
            <result column="teacherCourseName" property="name" />
            <result column="teacherScore" property="score" />
        </collection>
        <collection property="stuCourses" javaType="java.util.ArrayList" ofType="com.ironmanjay.springboot.entity.Course">
            <result column="stuCourseName" property="name" />
            <result column="stuScore" property="score" />
        </collection>
    </resultMap>

    <select id="findPage" resultMap="pageUser">
        select sys_user.*, sc.name as stuCourseName, tc.name as teacherCourseName, tc.score as teacherScore, sc.score as stuScore from sys_user
        left join student_course
        on sys_user.id = student_course.student_id
        left join course sc
        on student_course.course_id = sc.id
        left join course tc
        on sys_user.id = tc.teacher_id
        <where>
            <if test="username != null and username != ''">
                and sys_user.username like concat('%',#{username},'%')
            </if>
            <if test="email != null and email != ''">
                and sys_user.email like concat('%',#{email},'%')
            </if>
            <if test="address != null and address != ''">
                and sys_user.address like concat('%',#{address},'%')
            </if>
        </where>
    </select>

</mapper>
  1. 然后来到前端测试,发现已经成功显示了学生的选课信息,我们也就完成了学生与课程信息的多对多关联关系
    在这里插入图片描述

总结

  以上就是今天学习的全部内容了,内容虽然比昨天的少了一些,但还是不少啊,继续加油!明天将会带来用户前台页面设计与实现的相关内容。明天见!

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IronmanJay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值