SpringBoot 实现「新增班级 + 批量关联教师」多对多业务实战

一、需求分析

核心需求:

  • 前端提交「班级基本信息」和「需要绑定的教师 ID 列表」
  • 后端完成:校验班级唯一性 → 新增班级 → 批量绑定教师与班级的关系
  • 要求:所有数据库操作要么全部成功,要么全部失败(事务保障)

核心原则:教师和班级是多对多关系,无需重复插入教师数据,只需维护两者的关联关系表。

二、数据库设计

多对多关系的核心是「主表 + 关系表」设计,避免直接在主表中嵌套关联数据。

1. 核心表结构

(1)班级表(t_class)
sql 复制代码
CREATE TABLE t_class (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '班级ID',
    class_name VARCHAR(50) NOT NULL COMMENT '班级名称',
    class_code VARCHAR(20) NOT NULL COMMENT '班级编号',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    UPDATE_TIME DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    UNIQUE KEY uk_class_code (class_code) COMMENT '班级编号唯一'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='班级表';
(2)教师表(t_teacher)
sql 复制代码
CREATE TABLE t_teacher (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '教师ID',
    teacher_name VARCHAR(50) NOT NULL COMMENT '教师姓名',
    teacher_no VARCHAR(20) NOT NULL COMMENT '教师工号',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    UPDATE_TIME DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    UNIQUE KEY uk_teacher_no (teacher_no) COMMENT '教师工号唯一'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='教师表';
(3)班级 - 教师关系表(t_teacher_class)
sql 复制代码
CREATE TABLE t_teacher_class (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
    class_id BIGINT NOT NULL COMMENT '班级ID',
    teacher_id BIGINT NOT NULL COMMENT '教师ID',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    UNIQUE KEY uk_class_teacher (class_id, teacher_id) COMMENT '一个教师只能关联一个班级一次'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='班级教师关联表';

三、代码实现

1. 基础依赖(pom.xml)

核心依赖:SpringBoot Starter Web、MyBatis-Plus、MySQL 驱动、SpringBoot Starter Transaction

XML 复制代码
<dependencies>
    <!-- SpringBoot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- MyBatis-Plus -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.3.1</version>
    </dependency>
    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!-- 事务支持 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
</dependencies>

2. 实体类设计

(1)班级实体(ClassEntity)
java 复制代码
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;

@Data
@TableName("t_class")
public class ClassEntity {
    /**
     * 班级ID
     */
    @TableId(type = IdType.AUTO)
    private Long id;
    
    /**
     * 班级名称
     */
    private String className;
    
    /**
     * 班级编号
     */
    private String classCode;
    
    /**
     * 创建时间
     */
    private LocalDateTime createTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
}
(2)班级 - 教师关系实体(TeacherClassEntity)
java 复制代码
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;

@Data
@TableName("t_teacher_class")
public class TeacherClassEntity {
    /**
     * 主键ID
     */
    @TableId(type = IdType.AUTO)
    private Long id;
    
    /**
     * 班级ID
     */
    private Long classId;
    
    /**
     * 教师ID
     */
    private Long teacherId;
    
    /**
     * 创建时间
     */
    private LocalDateTime createTime;
}

3. DTO

java 复制代码
import lombok.Data;
import java.util.List;

/**
 * 新增班级并关联教师的入参DTO
 */
@Data
public class ClassTeacherDTO {
    /**
     * 班级基本信息
     */
    private ClassEntity classInfo;
    
    /**
     * 要关联的教师ID列表
     */
    private List<Long> teacherIds;
}

4. Mapper 层

(1)班级 Mapper(ClassMapper)
XML 复制代码
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import com.example.demo.entity.ClassEntity;

@Repository
public interface ClassMapper extends BaseMapper<ClassEntity> {
    /**
     * 根据班级编号查询班级是否存在
     */
    ClassEntity selectByClassCode(@Param("classCode") String classCode);
}
(2)班级 - 教师关系 Mapper(TeacherClassMapper)
java 复制代码
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import com.example.demo.entity.TeacherClassEntity;
import java.util.List;

@Repository
public interface TeacherClassMapper extends BaseMapper<TeacherClassEntity> {
    /**
     * 批量插入班级-教师关系
     */
    @Insert("<script>" +
            "INSERT INTO t_teacher_class (class_id, teacher_id, create_time) " +
            "VALUES " +
            "<foreach collection='list' item='item' separator=','> " +
            "(#{item.classId}, #{item.teacherId}, NOW()) " +
            "</foreach>" +
            "</script>")
    int batchInsert(@Param("list") List<TeacherClassEntity> list);
}

5. Service 层(核心业务逻辑)

(1)Service 接口
java 复制代码
import com.example.demo.dto.ClassTeacherDTO;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.entity.ClassEntity;

public interface ClassService extends IService<ClassEntity> {
    /**
     * 新增班级并批量关联教师
     */
    boolean addClassWithTeachers(ClassTeacherDTO dto);
}
(2)Service 实现类(核心)
java 复制代码
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.dto.ClassTeacherDTO;
import com.example.demo.entity.ClassEntity;
import com.example.demo.entity.TeacherClassEntity;
import com.example.demo.mapper.ClassMapper;
import com.example.demo.mapper.TeacherClassMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@Service
public class ClassServiceImpl extends ServiceImpl<ClassMapper, ClassEntity> implements ClassService {

    @Resource
    private ClassMapper classMapper;
    
    @Resource
    private TeacherClassMapper teacherClassMapper;

    /**
     * 新增班级并批量关联教师(添加事务注解,保证多表操作原子性)
     */
    @Override
    @Transactional(rollbackFor = Exception.class) // 异常时回滚所有操作
    public boolean addClassWithTeachers(ClassTeacherDTO dto) {
        // 步骤1:校验班级是否已存在(根据班级编号)
        ClassEntity existClass = classMapper.selectByClassCode(dto.getClassInfo().getClassCode());
        if (existClass != null) {
            throw new RuntimeException("班级编号已存在,无法新增");
        }
        
        // 步骤2:插入班级信息
        boolean saveClass = save(dto.getClassInfo());
        if (!saveClass) {
            throw new RuntimeException("班级新增失败");
        }
        Long newClassId = dto.getClassInfo().getId(); // 获取新增班级的ID
        
        // 步骤3:批量插入班级-教师关系(核心:只维护关系,不插入教师)
        List<Long> teacherIds = dto.getTeacherIds();
        if (teacherIds != null && !teacherIds.isEmpty()) {
            List<TeacherClassEntity> relationList = new ArrayList<>();
            for (Long teacherId : teacherIds) {
                TeacherClassEntity relation = new TeacherClassEntity();
                relation.setClassId(newClassId);
                relation.setTeacherId(teacherId);
                relationList.add(relation);
            }
            // 批量插入关系数据
            int insertCount = teacherClassMapper.batchInsert(relationList);
            if (insertCount != teacherIds.size()) {
                throw new RuntimeException("教师与班级关联失败");
            }
        }
        
        return true;
    }
}

6. Controller 层

java 复制代码
import com.example.demo.dto.ClassTeacherDTO;
import com.example.demo.service.ClassService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/class")
public class ClassController {

    @Resource
    private ClassService classService;

    /**
     * 新增班级并关联教师
     */
    @PostMapping("/addWithTeachers")
    public Map<String, Object> addClassWithTeachers(@RequestBody ClassTeacherDTO dto) {
        Map<String, Object> result = new HashMap<>();
        try {
            boolean success = classService.addClassWithTeachers(dto);
            result.put("code", 200);
            result.put("msg", "操作成功");
            result.put("data", success);
        } catch (RuntimeException e) {
            result.put("code", 500);
            result.put("msg", e.getMessage());
            result.put("data", false);
        }
        return result;
    }
}

四、核心要点说明

1. 事务控制

  • 使用 @Transactional(rollbackFor = Exception.class) 注解,确保「新增班级」和「关联教师」两步操作要么全成、要么全败。
  • rollbackFor = Exception.class 表示所有异常都触发回滚(默认只回滚运行时异常)。

2. 多对多关系处理

  • 不直接操作教师表,仅维护「班级 - 教师」关系表,符合数据库设计规范。
  • 关系表添加唯一索引 uk_class_teacher,避免重复关联。

3. 数据校验

  • 新增前校验班级编号唯一性,避免重复数据。
  • 批量插入关系数据后,校验插入条数与传入的教师 ID 数量一致,确保关联完整。

五、接口测试示例

请求参数(JSON)

复制代码
{
  "classInfo": {
    "className": "三年级1班",
    "classCode": "CLASS001"
  },
  "teacherIds": [1001, 1002, 1003]
}

响应结果(成功)

复制代码
{
  "code": 200,
  "msg": "操作成功",
  "data": true
}

响应结果(班级已存在)

复制代码
{
  "code": 500,
  "msg": "班级编号已存在,无法新增",
  "data": false
}

六、总结

  1. 多对多关系必须通过中间关系表维护,避免主表冗余。
  2. 多表操作一定要加事务控制,保证数据一致性。
  3. 前端入参设计为「主数据 + 关联 ID 列表」,符合实际业务场景。
  4. 核心流程:数据校验 → 新增主数据 → 批量插入关系数据,每一步都要做异常处理和结果校验。
相关推荐
雾山大叔12 小时前
多会话浏览器串口调试助手
经验分享·笔记·学习
桂花很香,旭很美13 小时前
Anthropic Agent 工程实战笔记 · 延伸阅读
笔记·架构·agent
今儿敲了吗13 小时前
29| 高考志愿
c++·笔记·学习·算法
山岚的运维笔记14 小时前
SQL Server笔记 -- 第85章:查询提示
数据库·笔记·sql·microsoft·sqlserver
浅念-14 小时前
C++ 模板进阶
开发语言·数据结构·c++·经验分享·笔记·学习·模版
桂花很香,旭很美16 小时前
Anthropic Agent 工程实战笔记(二)工具设计
笔记·架构·language model
梅羽落17 小时前
XPath笔记
笔记
桂花很香,旭很美18 小时前
Anthropic Agent 工程实战笔记(六)安全与生产
笔记·架构·agent
sponge'18 小时前
opencv学习笔记14:transformer
笔记·学习·transformer