项目结构

controller
package com.qcby.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class HomeController { @GetMapping("/") public String index() { return "index"; } }
package com.qcby.controller; import com.qcby.domain.Student; import com.qcby.domain.Teacher; import com.qcby.service.StudentService; import com.qcby.service.TeacherService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; @Controller @RequestMapping("/students") public class StudentController { @Autowired private StudentService studentService; @Autowired private TeacherService teacherService; // 显示所有学生 @GetMapping public String list(Model model) { model.addAttribute("students", studentService.findAll()); return "student/list"; } // 显示添加学生表单 @GetMapping("/add") public String addForm(Model model) { model.addAttribute("student", new Student()); model.addAttribute("teachers", teacherService.findAll()); return "student/form"; } // 保存学生 @PostMapping("/save") public String save(@ModelAttribute Student student) { if (student.getId() == null) { studentService.insert(student); } else { studentService.update(student); } return "redirect:/students"; } // 显示编辑学生表单 @GetMapping("/edit/{id}") public String editForm(@PathVariable Integer id, Model model) { model.addAttribute("student", studentService.findById(id)); model.addAttribute("teachers", teacherService.findAll()); return "student/form"; } // 删除学生 @GetMapping("/delete/{id}") public String delete(@PathVariable Integer id) { studentService.delete(id); return "redirect:/students"; } // 按教师ID查询学生 @GetMapping("/by-teacher/{teacherId}") public String listByTeacher(@PathVariable Integer teacherId, Model model) { model.addAttribute("students", studentService.findByTeacherId(teacherId)); Teacher teacher = teacherService.findById(teacherId); model.addAttribute("teacherName", teacher != null ? teacher.getName() : "未知教师"); return "student/list-by-teacher"; } }
package com.qcby.controller; import com.qcby.domain.Teacher; import com.qcby.service.TeacherService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; @Controller @RequestMapping("/teachers") public class TeacherController { @Autowired private TeacherService teacherService; // 显示所有教师 @GetMapping public String list(Model model) { model.addAttribute("teachers", teacherService.findAll()); return "teacher/list"; } // 显示添加教师表单 @GetMapping("/add") public String addForm(Model model) { model.addAttribute("teacher", new Teacher()); return "teacher/form"; } // 保存教师 @PostMapping("/save") public String save(@ModelAttribute Teacher teacher) { if (teacher.getId() == null) { teacherService.insert(teacher); } else { teacherService.update(teacher); } return "redirect:/teachers"; } // 显示编辑教师表单 @GetMapping("/edit/{id}") public String editForm(@PathVariable Integer id, Model model) { model.addAttribute("teacher", teacherService.findById(id)); return "teacher/form"; } // 删除教师 @GetMapping("/delete/{id}") public String delete(@PathVariable Integer id) { teacherService.delete(id); return "redirect:/teachers"; } }
domain
package com.qcby.domain; import lombok.Data; import java.time.LocalDateTime; @Data public class Student { private Integer id; private String name; private Integer age; private String gender; private String grade; private Integer teacherId; private String teacherName; // 用于显示教师姓名,不映射到数据库 private LocalDateTime createTime; private LocalDateTime updateTime; }
package com.qcby.domain; import lombok.Data; import java.time.LocalDateTime; @Data public class Teacher { private Integer id; private String name; private Integer age; private String subject; private String phone; private String email; private LocalDateTime createTime; private LocalDateTime updateTime; }
mapper
package com.qcby.mapper; import com.qcby.domain.Student; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper public interface StudentMapper { // 查询所有学生 List<Student> findAll(); // 根据ID查询学生 Student findById(Integer id); // 添加学生 int insert(Student student); // 更新学生 int update(Student student); // 删除学生 int delete(Integer id); // 根据教师ID查询学生 List<Student> findByTeacherId(Integer teacherId); }
package com.qcby.mapper; import com.qcby.domain.Teacher; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper public interface TeacherMapper { // 查询所有教师 List<Teacher> findAll(); // 根据ID查询教师 Teacher findById(Integer id); // 添加教师 int insert(Teacher teacher); // 更新教师 int update(Teacher teacher); // 删除教师 int delete(Integer id); }
service
package com.qcby.service; import com.qcby.domain.Student; import java.util.List; public interface StudentService { List<Student> findAll(); Student findById(Integer id); int insert(Student student); int update(Student student); int delete(Integer id); List<Student> findByTeacherId(Integer teacherId); }
package com.qcby.service; import com.qcby.domain.Teacher; import java.util.List; public interface TeacherService { List<Teacher> findAll(); Teacher findById(Integer id); int insert(Teacher teacher); int update(Teacher teacher); int delete(Integer id); }
package com.qcby.service.impl; import com.qcby.domain.Student; import com.qcby.mapper.StudentMapper; import com.qcby.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 StudentMapper studentMapper; @Override public List<Student> findAll() { return studentMapper.findAll(); } @Override public Student findById(Integer id) { return studentMapper.findById(id); } @Override public int insert(Student student) { return studentMapper.insert(student); } @Override public int update(Student student) { return studentMapper.update(student); } @Override public int delete(Integer id) { return studentMapper.delete(id); } @Override public List<Student> findByTeacherId(Integer teacherId) { return studentMapper.findByTeacherId(teacherId); } }
package com.qcby.service.impl; import com.qcby.domain.Teacher; import com.qcby.mapper.TeacherMapper; import com.qcby.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 TeacherMapper teacherMapper; @Override public List<Teacher> findAll() { return teacherMapper.findAll(); } @Override public Teacher findById(Integer id) { return teacherMapper.findById(id); } @Override public int insert(Teacher teacher) { return teacherMapper.insert(teacher); } @Override public int update(Teacher teacher) { return teacherMapper.update(teacher); } @Override public int delete(Integer id) { return teacherMapper.delete(id); } }
mapper.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.qcby.mapper.StudentMapper"> <resultMap id="StudentResultMap" type="com.qcby.domain.Student"> <id property="id" column="id"/> <result property="name" column="name"/> <result property="age" column="age"/> <result property="gender" column="gender"/> <result property="grade" column="grade"/> <result property="teacherId" column="teacher_id"/> <result property="teacherName" column="teacher_name"/> <result property="createTime" column="create_time"/> <result property="updateTime" column="update_time"/> </resultMap> <select id="findAll" resultMap="StudentResultMap"> SELECT s.*, t.name as teacher_name FROM student s LEFT JOIN teacher t ON s.teacher_id = t.id ORDER BY s.id DESC </select> <select id="findById" parameterType="int" resultMap="StudentResultMap"> SELECT s.*, t.name as teacher_name FROM student s LEFT JOIN teacher t ON s.teacher_id = t.id WHERE s.id = #{id} </select> <select id="findByTeacherId" parameterType="int" resultMap="StudentResultMap"> SELECT s.*, t.name as teacher_name FROM student s LEFT JOIN teacher t ON s.teacher_id = t.id WHERE s.teacher_id = #{teacherId} ORDER BY s.id DESC </select> <insert id="insert" parameterType="com.qcby.domain.Student"> INSERT INTO student (name, age, gender, grade, teacher_id) VALUES (#{name}, #{age}, #{gender}, #{grade}, #{teacherId}) </insert> <update id="update" parameterType="com.qcby.domain.Student"> UPDATE student SET name = #{name}, age = #{age}, gender = #{gender}, grade = #{grade}, teacher_id = #{teacherId} WHERE id = #{id} </update> <delete id="delete" parameterType="int"> DELETE FROM student WHERE id = #{id} </delete> </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"> <mapper namespace="com.qcby.mapper.TeacherMapper"> <resultMap id="TeacherResultMap" type="com.qcby.domain.Teacher"> <id property="id" column="id"/> <result property="name" column="name"/> <result property="age" column="age"/> <result property="subject" column="subject"/> <result property="phone" column="phone"/> <result property="email" column="email"/> <result property="createTime" column="create_time"/> <result property="updateTime" column="update_time"/> </resultMap> <select id="findAll" resultMap="TeacherResultMap"> SELECT * FROM teacher ORDER BY id DESC </select> <select id="findById" parameterType="int" resultMap="TeacherResultMap"> SELECT * FROM teacher WHERE id = #{id} </select> <insert id="insert" parameterType="com.qcby.domain.Teacher"> INSERT INTO teacher (name, age, subject, phone, email) VALUES (#{name}, #{age}, #{subject}, #{phone}, #{email}) </insert> <update id="update" parameterType="com.qcby.domain.Teacher"> UPDATE teacher SET name = #{name}, age = #{age}, subject = #{subject}, phone = #{phone}, email = #{email} WHERE id = #{id} </update> <delete id="delete" parameterType="int"> DELETE FROM teacher WHERE id = #{id} </delete> </mapper>
html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title th:text="${student.id != null ? '编辑学生' : '添加学生'}"></title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container mt-4"> <h2 th:text="${student.id != null ? '编辑学生' : '添加学生'}"></h2> <form th:action="@{/students/save}" th:object="${student}" method="post" class="mt-4"> <input type="hidden" th:field="*{id}" /> <div class="mb-3"> <label for="name" class="form-label">姓名</label> <input type="text" class="form-control" id="name" th:field="*{name}" required /> </div> <div class="mb-3"> <label for="age" class="form-label">年龄</label> <input type="number" class="form-control" id="age" th:field="*{age}" min="6" max="20" /> </div> <div class="mb-3"> <label for="gender" class="form-label">性别</label> <select class="form-select" id="gender" th:field="*{gender}"> <option value="">请选择</option> <option value="男">男</option> <option value="女">女</option> </select> </div> <div class="mb-3"> <label for="grade" class="form-label">年级</label> <input type="text" class="form-control" id="grade" th:field="*{grade}" /> </div> <div class="mb-3"> <label for="teacherId" class="form-label">班主任</label> <select class="form-select" id="teacherId" th:field="*{teacherId}"> <option value="">请选择</option> <option th:each="teacher : ${teachers}" th:value="${teacher.id}" th:text="${teacher.name}"></option> </select> </div> <div class="mb-3"> <button type="submit" class="btn btn-primary">保存</button> <a href="/students" class="btn btn-secondary ms-2">取消</a> </div> </form> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> </body> </html>
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>学生列表</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container mt-4"> <div class="d-flex justify-content-between align-items-center mb-4"> <h2>学生列表</h2> <a href="/students/add" class="btn btn-primary">添加学生</a> </div> <a href="/" class="btn btn-secondary mb-3">返回首页</a> <table class="table table-striped table-hover"> <thead class="table-dark"> <tr> <th>ID</th> <th>姓名</th> <th>年龄</th> <th>性别</th> <th>年级</th> <th>班主任</th> <th>操作</th> </tr> </thead> <tbody> <tr th:each="student : ${students}"> <td th:text="${student.id}"></td> <td th:text="${student.name}"></td> <td th:text="${student.age}"></td> <td th:text="${student.gender}"></td> <td th:text="${student.grade}"></td> <td th:text="${student.teacherName}"></td> <td> <a th:href="@{/students/edit/{id}(id=${student.id})}" class="btn btn-sm btn-warning">编辑</a> <a th:href="@{/students/delete/{id}(id=${student.id})}" class="btn btn-sm btn-danger" onclick="return confirm('确定要删除吗?')">删除</a> </td> </tr> </tbody> </table> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> </body> </html>
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>学生列表</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container mt-4"> <div class="d-flex justify-content-between align-items-center mb-4"> <h2> <span th:text="${teacherName}"></span> 的学生列表 </h2> <a href="/students/add" class="btn btn-primary">添加学生</a> </div> <div class="mb-3"> <a href="/" class="btn btn-secondary">返回首页</a> <a href="/teachers" class="btn btn-info ms-2">返回教师列表</a> <a href="/students" class="btn btn-success ms-2">查看所有学生</a> </div> <table class="table table-striped table-hover"> <thead class="table-dark"> <tr> <th>ID</th> <th>姓名</th> <th>年龄</th> <th>性别</th> <th>年级</th> <th>操作</th> </tr> </thead> <tbody> <tr th:each="student : ${students}"> <td th:text="${student.id}"></td> <td th:text="${student.name}"></td> <td th:text="${student.age}"></td> <td th:text="${student.gender}"></td> <td th:text="${student.grade}"></td> <td> <a th:href="@{/students/edit/{id}(id=${student.id})}" class="btn btn-sm btn-warning">编辑</a> <a th:href="@{/students/delete/{id}(id=${student.id})}" class="btn btn-sm btn-danger" onclick="return confirm('确定要删除吗?')">删除</a> </td> </tr> </tbody> </table> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> </body> </html>
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title th:text="${teacher.id != null ? '编辑教师' : '添加教师'}"></title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container mt-4"> <h2 th:text="${teacher.id != null ? '编辑教师' : '添加教师'}"></h2> <form th:action="@{/teachers/save}" th:object="${teacher}" method="post" class="mt-4"> <input type="hidden" th:field="*{id}" /> <div class="mb-3"> <label for="name" class="form-label">姓名</label> <input type="text" class="form-control" id="name" th:field="*{name}" required /> </div> <div class="mb-3"> <label for="age" class="form-label">年龄</label> <input type="number" class="form-control" id="age" th:field="*{age}" min="18" max="65" /> </div> <div class="mb-3"> <label for="subject" class="form-label">科目</label> <input type="text" class="form-control" id="subject" th:field="*{subject}" /> </div> <div class="mb-3"> <label for="phone" class="form-label">电话</label> <input type="text" class="form-control" id="phone" th:field="*{phone}" /> </div> <div class="mb-3"> <label for="email" class="form-label">邮箱</label> <input type="email" class="form-control" id="email" th:field="*{email}" /> </div> <div class="mb-3"> <button type="submit" class="btn btn-primary">保存</button> <a href="/teachers" class="btn btn-secondary ms-2">取消</a> </div> </form> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> </body> </html>
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>教师列表</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container mt-4"> <div class="d-flex justify-content-between align-items-center mb-4"> <h2>教师列表</h2> <a href="/teachers/add" class="btn btn-primary">添加教师</a> </div> <a href="/" class="btn btn-secondary mb-3">返回首页</a> <table class="table table-striped table-hover"> <thead class="table-dark"> <tr> <th>ID</th> <th>姓名</th> <th>年龄</th> <th>科目</th> <th>电话</th> <th>邮箱</th> <th>学生</th> <th>操作</th> </tr> </thead> <tbody> <tr th:each="teacher : ${teachers}"> <td th:text="${teacher.id}"></td> <td th:text="${teacher.name}"></td> <td th:text="${teacher.age}"></td> <td th:text="${teacher.subject}"></td> <td th:text="${teacher.phone}"></td> <td th:text="${teacher.email}"></td> <td> <a th:href="@{/students/by-teacher/{id}(id=${teacher.id})}" class="btn btn-sm btn-info">查看学生</a> </td> <td> <a th:href="@{/teachers/edit/{id}(id=${teacher.id})}" class="btn btn-sm btn-warning">编辑</a> <a th:href="@{/teachers/delete/{id}(id=${teacher.id})}" class="btn btn-sm btn-danger" onclick="return confirm('确定要删除吗?')">删除</a> </td> </tr> </tbody> </table> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> </body> </html>
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>教师学生管理系统</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container mt-5"> <div class="jumbotron text-center"> <h1 class="display-4">教师学生管理系统</h1> <p class="lead">简单高效的教学管理解决方案</p> <hr class="my-4"> <div class="d-flex justify-content-center gap-3"> <a class="btn btn-primary btn-lg" href="/teachers" role="button">教师管理</a> <a class="btn btn-success btn-lg" href="/students" role="button">学生管理</a> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> </body> </html>
properties
# ????? server.port=8080 # ????? spring.datasource.url=jdbc:mysql://localhost:3306/teacher_student_management?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # MyBatis?? mybatis.mapper-locations=classpath:mapper/*.xml mybatis.type-aliases-package=com.qcby.domain # Thymeleaf?? spring.thymeleaf.cache=false spring.thymeleaf.mode=HTML5 spring.thymeleaf.encoding=UTF-8 spring.thymeleaf.servlet.content-type=text/html # ???? logging.level.com.example.mapper=debug