逻辑
前端操作:
管理员点击”新增”按钮
弹出空白表单,填写教师信息(用户名、姓名、性别等)
点击”保存”按钮提交
后端处理:
// 1. 检查用户名是否已存在(防重复)
Teacher dbTeacher = teacherMapper.selectByUsername(teacher.getUsername());
if(已存在){
直接报错:”用户名已存在”
}
// 2. 设置默认值:
if(没填密码){
自动设置密码为”123456”
}
强制设置角色为”TEACHER”
// 3. 保存到数据库
teacherMapper.insert(teacher);
结果反馈:
成功:提示”新增成功”,列表自动刷新显示新教师
失败:提示具体原因
关键处理点
唯一性校验:通过用户名检查教师是否已存在
默认值设置:
密码为空时自动设为”123456”
角色固定设为”TEACHER”
异常处理:用户名存在时抛出业务异常
数据流向:Controller → Service → Mapper → DB(数据库)
创建教师信息表
1 2 3 4 5 6 7 8 9 10 11 12
| CREATE TABLE `teacher` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键id', `username` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '用户名', `password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '密码', `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '姓名', `sex` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '性别', `title` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '职称', `speciality_id` int DEFAULT NULL COMMENT '专业id', `role` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '角色', `avatar` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '头像', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='教师信息';
|

idea中Mac快捷键
替换

get,set方法:
找到插件GenerateAllSetter
一键调用一个对象的所有的set方法,get方法等
在方法上生成两个对象的转换

快捷键:control+enter

导包:
自动导包/修复导包问题:⌘ + Enter
优化导包(移除未使用的):⌃ + ⌥ + O
手动导包提示:⌘ + N
entity—teacher
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| public class Teacher extends Account { private Integer id; private String username; private String password; private String name; private String sex; private String title; private Integer specialityId; private String specialityName; private String role; private String avatar;
@Override public Integer getId() { return id; }
@Override public void setId(Integer id) { this.id = id; }
@Override public String getUsername() { return username; }
@Override public void setUsername(String username) { this.username = username; }
@Override public String getPassword() { return password; }
@Override public void setPassword(String password) { this.password = password; }
@Override public String getName() { return name; }
@Override public void setName(String name) { this.name = name; }
public String getSex() { return sex; }
public void setSex(String sex) { this.sex = sex; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public Integer getSpecialityId() { return specialityId; }
public void setSpecialityId(Integer specialityId) { this.specialityId = specialityId; }
@Override public String getRole() { return role; }
@Override public void setRole(String role) { this.role = role; }
@Override public String getAvatar() { return avatar; } @Override public void setAvatar(String avatar) { this.avatar = avatar; }
public String getSpecialityName() { return specialityName; } public void setSpecialityName(String specialityName) { this.specialityName = specialityName; } }
|

解决前端vue里面没有错警告
比如

设置(setting)里面——修改html里面

前端添加教师信息
1 2 3 4 5
| <el-menu-item index="/teacher"> <el-icon><Avatar /></el-icon> /*图标在 https://element-plus.org/ 里面找*/ <span>教师信息</span> </el-menu-item>
|

现在前端也有教师信息菜单了,图标也可以更改

配置路由,加一个teacher信息
1
| { path: 'teacher', component: () => import('@/views/manager/teacher.vue')},
|

在manager下面新建一个Teacher.vue

vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| <template> <div> <!-- 新增按钮 可以参考Admin.vue里面的内容 --> <div class="card" style="margin-bottom: 5px"> <div style="margin-bottom: 10px"> <!-- 新增教师按钮,点击触发handleAdd方法 --> <el-button type="primary" @click="handleAdd">新增</el-button> </div> </div> <!-- 教师信息弹窗对话框 --> <el-dialog title="教师信息" width="40%" v-model="data.formVisible" :close-on-click-modal="false" destroy-on-close> <el-form :model="data.form" label-width="100px" style="padding-right: 50px"> <!--prop v-model 里面要与后端保持一致不然接收不到 --> <el-form-item label="账号" prop="username"> <el-input v-model="data.form.username" autocomplete="off" placeholder="请输入账号"/> </el-form-item> <el-form-item label="姓名" prop="name"> <el-input v-model="data.form.name" autocomplete="off" placeholder="请输入姓名"/> </el-form-item> <el-form-item label="性别" prop="sex"> <el-select v-model="data.form.sex" placeholder="请选择性别" style="width: 100%"> <el-option label="男" value="男"></el-option> <el-option label="女" value="女"></el-option> </el-select> </el-form-item> <el-form-item label="职称" prop="title"> <el-select v-model="data.form.title" placeholder="请选择职称" style="width: 100%"> <el-option label="讲师" value="讲师"></el-option> <el-option label="副教授" value="副教授"></el-option> <el-option label="教授" value="教授"></el-option> </el-select> </el-form-item> </el-form> <!-- 弹窗取消,保存 --> <template #footer> <span class="dialog-footer"> <el-button @click="data.formVisible = false">取 消</el-button> <el-button type="primary" @click="save">保 存</el-button> </span> </template> </el-dialog> </div> </template>
<script setup> //导包 import {reactive} from "vue"; import request from "@/utils/request"; import {ElMessage} from "element-plus";
const data = reactive({ // 教师信息弹窗对话框 formVisible: false, // 教师信息表单数据 form: { }, }) // 新增教师按钮点击事件 const handleAdd = () => { // 清空教师信息表单数据 data.form = {
} // 打开教师信息弹窗对话框 data.formVisible = true } const save = () => { // 调用后端接口保存教师信息 // 这里可以使用axios或者其他请求库发送请求 // 例如:axios.post('/api/teacher/add', data.form) request.post('/teacher/add', data.form).then(res => { if (res.code === '200') { // 保存成功,提示用户 ElMessage.success('操作成功') // 关闭教师信息弹窗对话框 data.formVisible = false }else { // 保存失败,提示用户 ElMessage.error(res.msg) } }) } </script>
|
Controller—TeacherController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package com.example.controller;
import com.example.common.Result; import com.example.entity.Admin; import com.example.entity.Teacher; import com.example.service.AdminService; import com.example.service.TeacherService; import com.github.pagehelper.PageInfo; import jakarta.annotation.Resource; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/teacher") public class TeacherController { @Resource
private TeacherService teacherService;
@PostMapping("/add") public Result add(@RequestBody Teacher teacher){ teacherService.add(teacher); return Result.success(); } }
|
Service—TeacherService 核心逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package com.example.service;
import cn.hutool.core.util.ObjectUtil; import com.example.entity.Teacher; import com.example.exception.CustomException; import com.example.mapper.TeacherMapper; import jakarta.annotation.Resource; import org.springframework.stereotype.Service;
@Service public class TeacherService {
@Resource private TeacherMapper teacherMapper;
public void add(Teacher teacher) { Teacher dbTeacher = teacherMapper.selectByUsername(teacher.getUsername()); if (ObjectUtil.isNotEmpty(dbTeacher)) { throw new CustomException("用户名已存在"); }
if(ObjectUtil.isEmpty(teacher.getPassword())){ teacher.setPassword("123456"); } teacher.setRole("TEACHER"); teacherMapper.insert(teacher); } }
|
mapper—TeacherMapper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.example.mapper;
import com.example.entity.Teacher; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select;
@Mapper public interface TeacherMapper {
void insert(Teacher teacher); @Select("select * from teacher where username = #{username}") Teacher selectByUsername(String username); }
|
TeacherMapper.xml
1 2 3 4 5 6 7 8 9 10
| <?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.example.mapper.TeacherMapper"> <insert id="insert" parameterType="com.example.entity.Teacher" useGeneratedKeys="true"> insert into teacher (username, password, name,sex,title,speciality_id,role,avatar) values (#{username}, #{password}, #{name}, #{sex}, #{title}, #{specialityId}, #{role}, #{avatar}) </insert> </mapper>
|
简单逻辑在mapper可以用注解,复杂逻辑用xml
namespace 对应mapper接口的全类名
1. 映射文件的位置必须和mapper接口的包结构相同
2. 映射文件的名称必须和mapper接口的名称相同
3. 映射文件的后缀名必须是.xml
1. 映射文件中的sql语句的id必须和mapper接口中的方法名相同
2. 映射文件中的sql语句的参数类型必须和mapper接口中的方法参数类型相同
3. 映射文件中的sql语句的返回值类型必须和mapper接口中的方法返回值类型相同
1. Controller(控制层)
- 职责:
- 接收客户端请求(HTTP/RPC等),解析参数(如URL、Body、Header)。
- 调用 Service 层处理业务逻辑。
- 返回响应(JSON/XML等)或视图(如HTML)。
- 特点:
- 轻量级,仅做请求转发和参数校验,不包含业务逻辑。
- 通常使用
@Controller 或 @RestController 注解(Spring中)。
2. Service(业务逻辑层)
- 职责:
- 处理核心业务逻辑(如订单创建、用户权限校验)。
- 协调多个 Mapper 操作(事务管理)。
- 调用外部服务(如支付接口、消息队列)。
- 特点:
- 使用
@Service 注解,是业务逻辑的集中点。
- 可能涉及事务(
@Transactional)。
3. Mapper(数据访问层,或称DAO)
- 职责:
- 直接操作数据库(CRUD),执行SQL语句。
- 与数据库表一一对应(如MyBatis的
@Mapper 或JPA的Repository)。
- 特点:
- 无业务逻辑,仅做数据持久化。
- 使用
@Mapper(MyBatis)或继承 JpaRepository(Spring Data JPA)。
三者的协作流程
- 请求入口:
- 客户端 →
Controller(接收请求) → 调用 Service。
- 业务处理:
Service 处理逻辑 → 调用 Mapper 读写数据库。
- 数据持久化:
Mapper 执行SQL → 返回数据给 Service → Service 返回结果给 Controller。
- 响应输出:
Controller 将结果封装为响应 → 返回客户端。
关键原则
- 单向依赖:
Controller → Service → Mapper(禁止反向依赖或跨层调用)。
- 职责隔离:
Controller 不管业务,Service 不管SQL,Mapper 只管数据库。
- 复用性:
一个 Service 可能被多个 Controller 调用,一个 Mapper 可能被多个 Service 调用。
通过这种分层设计,系统更易于维护、扩展和测试,符合高内聚低耦合的软件工程原则
先写前端然后写后端——Controller → Service → Mapper