You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JHipster Spring Boot多对多关系正确保存及层级字段冲突解决

问题修复方案

你遇到的问题本质是课程的层级(level)被设计为全局属性,所有学生共享同一个Sureler实体的level值,所以修改一个学生对应的课程level时,会影响所有关联该课程的学生。正确的设计应该是将level作为学生-课程关联关系的属性,而不是课程本身的属性。

以下是具体修复步骤:

1. 创建中间关联实体StudentSure

这个实体用来维护学生和课程的关联,同时存储该学生在该课程的完成层级:

import jakarta.persistence.*;
import java.util.Objects;

@Entity
@Table(name = "student_sure")
public class StudentSure {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "student_id", nullable = false)
    private Student student;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "sure_name", referencedColumnName = "name", nullable = false)
    private Sureler sureler;

    @Column(name = "level")
    private Long level;

    // 重写equals和hashCode,避免重复关联
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        StudentSure that = (StudentSure) o;
        return student.getId().equals(that.student.getId()) && sureler.getName().equals(that.sureler.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(student.getId(), sureler.getName());
    }

    // Getter和Setter省略,可通过Lombok的@Data注解自动生成
}

2. 修改Student实体

将原来的Set<Sureler>替换为Set<StudentSure>,并配置一对多关联:

// 移除原有@ManyToMany相关代码
// @JsonIgnore
// @ManyToMany(...)
// private Set<Sureler> listSureler = new HashSet<>();

// 新增关联配置
@JsonIgnore
@OneToMany(mappedBy = "student", cascade = CascadeType.ALL, orphanRemoval = true)
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@BatchSize(size = 20)
private Set<StudentSure> studentSures = new HashSet<>();

// 新增添加/更新课程层级的便捷方法
public void addOrUpdateSurelerLevel(Sureler sureler, Long level) {
    studentSures.stream()
            .filter(ss -> ss.getSureler().getName().equals(sureler.getName()))
            .findFirst()
            .ifPresentOrElse(
                    ss -> ss.setLevel(level),
                    () -> {
                        StudentSure studentSure = new StudentSure();
                        studentSure.setStudent(this);
                        studentSure.setSureler(sureler);
                        studentSure.setLevel(level);
                        studentSures.add(studentSure);
                    }
            );
}

// 保留原有字段
@Column(name = "child_birth_date")
private LocalDate childBirthDate;

@Column(name = "street")
private String street;

@Column(name = "postal")
private Long postal;

3. 修改Sureler实体

移除全局的level字段,添加对StudentSure的一对多关联:

@JsonIgnore
@OneToMany(mappedBy = "sureler", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<StudentSure> studentSures = new HashSet<>();

// 保留原有字段
@Column(name = "name")
private String name;

// 移除原有全局level字段
// @Column(name = "level")
// private Long level;

4. 新增StudentSureRepository

import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;

public interface StudentSureRepository extends JpaRepository<StudentSure, Long> {
    Optional<StudentSure> findByStudentIdAndSurelerName(Long studentId, String sureName);
}

5. 修改更新接口逻辑

现在直接操作StudentSure维护学生的课程层级,不再修改全局Sureler实体:

@PatchMapping(value = "/students/{id}", consumes = { "application/json", "application/merge-patch+json" })
public ResponseEntity<StudentDTO> partialUpdateStudent(
        @PathVariable(value = "id", required = false) final Long id,
        @RequestParam String sureName,
        @RequestParam Long level
) throws URISyntaxException {
    if (id == null) {
        throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull");
    }
    Student student = studentRepository.findById(id)
            .orElseThrow(() -> new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound"));
    log.debug("REST request to partial update Student partially : {}, {}", id, student);

    Sureler sureler = surelerRepository.findByName(sureName)
            .orElseThrow(() -> new BadRequestAlertException("Sureler not found", "Sureler", "namenotfound"));

    // 添加或更新学生的课程层级
    student.addOrUpdateSurelerLevel(sureler, level);
    Student updatedStudent = studentRepository.save(student);
    StudentDTO studentDTO = studentMapper.toDto(updatedStudent);

    return ResponseUtil.wrapOrNotFound(
            Optional.of(studentDTO),
            HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, studentDTO.getId().toString())
    );
}

内容的提问来源于stack exchange,提问作者natyus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 02:57:49