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
相关产品推荐
相关产品推荐

