我在更新应用程序中的课程实体时遇到问题,其中涉及学生和课程实体之间的多对多关系。当我更新课程信息时,操作成功,没有任何问题。但是,当我尝试向课程中添加新学生时,Hibernate 尝试再次插入学生,导致 SQL 错误:
SQL错误:0,SQL状态:23505 错误:重复的键值违反了唯一约束“student_lessons_pkey”
您能否提供帮助来解决此问题?以下是相关更新函数供参考和实体。
@Override
@Transactional
public LessonResponse updateLesson(LessonRequest lessonRequest) {
Long lessonId = lessonRequest.getId();
if(lessonId == null)
throw new EntityNotFoundException("Lesson id is required");
Lesson lesson = lessonRepository.findById(lessonId)
.orElseThrow(() -> new EntityNotFoundException("Lesson not found"));
// Update lesson properties
updateLessonProperties(lesson, lessonRequest);
// Update students associated with the lesson
updateLessonStudents(lesson, lessonRequest.getStudents());
log.info("Lesson: {}", lesson);
lessonRepository.save(lesson);
return Mapper.toLessonResponse(lesson);
}
private void updateLessonProperties(Lesson lesson, LessonRequest lessonRequest) {
// Update lesson properties from the request
lesson.setModul(lessonRequest.getModul());
lesson.setStartAt(lessonRequest.getStartAt());
lesson.setUnits(lessonRequest.getUnits());
lesson.setLessonType(lessonRequest.getLessonType());
lesson.setDescription(lessonRequest.getDescription());
// Optionally update the teacher if it has changed
if (!Objects.equals(lesson.getTeacher().getId(), lessonRequest.getTeacher().getId())) {
Teacher teacher = teacherRepository.findById(lessonRequest.getTeacher().getId())
.orElseThrow(() -> new EntityNotFoundException("Teacher not found"));
lesson.setTeacher(teacher);
}
}
private void updateLessonStudents(Lesson lesson, List<Student> updatedStudents) {
List<Long> updatedStudentIds = updatedStudents.stream().map(User::getId).toList();
log.info("updatedStudentsU: {}", updatedStudentIds);
// Fetch the existing students associated with the lesson
List<Long> existingStudentIds = lesson.getStudents().stream().map(User::getId).toList();
log.info("existingStudents: {}", existingStudentIds);
existingStudentIds.stream()
.filter(existingStudentId -> !updatedStudentIds.contains(existingStudentId))
.forEach(existingStudentId -> {
log.info("Removing student with ID: {}", existingStudentId);
Student student = studentRepository.findById(existingStudentId)
.orElseThrow(() -> new InternalError("Error removing student from lesson!"));
lesson.removeStudent(student);
student.removeLesson(lesson);
});
// Add new students from the updated list
updatedStudents.stream()
.filter(student -> !existingStudentIds.contains(student.getId()))
.forEach(student -> {
log.info("Add student with ID: {}", student.getId());
Student existingStudent = studentRepository.findById(student.getId())
.orElseThrow(() -> new EntityNotFoundException("Student not found for lesson!"));
log.info("Student: {}", existingStudent);
lesson.addStudent(existingStudent);
});
}
这是我的课程实体
@Entity
@Data
@Builder
@NoArgsConstructor(force = true)
@AllArgsConstructor
@Table(name = "lessons")
@SoftDelete
public class Lesson {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private ModuleType modul;
@Column(nullable = false)
@JsonDeserialize(using = LocalDateDeserializer.class)
private LocalDateTime startAt;
@Column(nullable = false)
private double units;
@Enumerated(EnumType.STRING)
@Column(length = 50, nullable = false) // Adjust the length as needed
private LessonType lessonType;
@Column(length = 512, nullable = false)
@NonNull
private String description;
@ManyToMany
@JoinTable(
name = "student_lessons",
joinColumns = @JoinColumn(name = "lesson_id"),
inverseJoinColumns = @JoinColumn(name = "student_id")
)
@SoftDelete
@Column(nullable = false)
@Builder.Default
private List<Student> students = new ArrayList<>();
@ManyToOne
@JoinColumn(name = "teacher_id", nullable = false)
private Teacher teacher;
private LocalDateTime lastUpdatedTimestamp;
private String lastUpdatedBy;
// Method to add a student to the lesson
public void addStudent(Student student) {
this.students.add(student);
student.getLessons().add(this);
}
// Method to remove a student from the lesson
public void removeStudent(Student student) {
this.students.remove(student);
student.getLessons().remove(this);
}
}
这是我的学生实体
@Entity
@Table(name = "students")
public class Student extends User {
private String level;
private boolean portalAccess;
@ManyToMany(fetch = FetchType.LAZY
,mappedBy = "students")
@Builder.Default
private List<Lesson> lessons = new ArrayList<>();
@OneToMany(mappedBy = "student")
@Builder.Default
private List<Contract> contracts = new ArrayList<>();
@ManyToMany(mappedBy = "students")
@Builder.Default
private List<Teacher> teachers = new ArrayList<>();
public void addTeacher(Teacher teacher) {
teachers.add(teacher);
teacher.getStudents().add(this);
}
public void removeTeacher(Teacher teacher) {
teachers.remove(teacher);
teacher.getStudents().remove(this);
}
public void addLesson(Lesson lesson) {
lessons.add(lesson);
lesson.getStudents().add(this);
}
public void removeLesson(Lesson lesson) {
lessons.remove(lesson);
lesson.getStudents().remove(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
Student student = (Student) o;
return Objects.equals(level, student.level) && Objects.equals(lessons, student.lessons) && Objects.equals(contracts, student.contracts) && Objects.equals(teachers, student.teachers);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), level, lessons, contracts, teachers);
}
}
这是日志信息
INFO 17540 --- [school_system] [nio-8080-exec-2] c.s.service.impl.LessonServiceImpl : updatedStudentsU: [25, 75]
2024-02-17T12:15:03.041+01:00 INFO 17540 --- [school_system] [nio-8080-exec-2] c.s.service.impl.LessonServiceImpl : existingStudents: [25]
2024-02-17T12:15:03.043+01:00 INFO 17540 --- [school_system] [nio-8080-exec-2] c.s.service.impl.LessonServiceImpl : Add student with ID: 75
2024-02-17T12:15:03.072+01:00 INFO 17540 --- [school_system] [nio-8080-exec-2] c.s.service.impl.LessonServiceImpl : Student: User(id=75, title=HERR, firstName=Fayre, lastName=Dudney, phoneNumber=+358 (355) 234-9484, comment=null, [email protected], accountNonExpired=true, accountNonLocked=true, credentialsNonExpired=true, enabled=true, verified=false, hasProvidedAllInfo=true, lastUpdatedTimestamp=2024-02-01T21:05:55.710775, lastUpdatedBy=null, deleted=false, roles=[Role(id=2, name=ROLE_TEACHER, description=)], address=Address(id=75, street=Quincy, hausnummer=748, city=Kristinestad, state=null, country=Finland, postal=41270, lastUpdatedTimestamp=2024-02-01T21:06:35.662254, lastUpdatedBy=null))
2024-02-17T12:15:03.073+01:00 INFO 17540 --- [school_system] [nio-8080-exec-2] c.s.service.impl.LessonServiceImpl : Lesson: Lesson(id=7, modul=DEUTSCH, startAt=2023-11-14T10:30, units=2.0, lessonType=PRESENTS_SCHOOL, description=gsd hrt hgre erh, students=[User(id=25, title=HERR, firstName=Stefan, lastName=Polhill, phoneNumber=+86 (937) 393-8812, comment=null, [email protected], accountNonExpired=true, accountNonLocked=true, credentialsNonExpired=true, enabled=true, verified=false, hasProvidedAllInfo=true, lastUpdatedTimestamp=2024-02-01T21:05:55.710775, lastUpdatedBy=null, deleted=false, roles=[Role(id=2, name=ROLE_TEACHER, description=)], address=Address(id=25, street=Atwood, hausnummer=9614, city=Axili, state=null, country=China, postal=0, lastUpdatedTimestamp=2024-02-01T21:06:35.662254, lastUpdatedBy=null)), User(id=75, title=HERR, firstName=Fayre, lastName=Dudney, phoneNumber=+358 (355) 234-9484, comment=null, [email protected], accountNonExpired=true, accountNonLocked=true, credentialsNonExpired=true, enabled=true, verified=false, hasProvidedAllInfo=true, lastUpdatedTimestamp=2024-02-01T21:05:55.710775, lastUpdatedBy=null, deleted=false, roles=[Role(id=2, name=ROLE_TEACHER, description=)], address=Address(id=75, street=Quincy, hausnummer=748, city=Kristinestad, state=null, country=Finland, postal=41270, lastUpdatedTimestamp=2024-02-01T21:06:35.662254, lastUpdatedBy=null))], teacher=Teacher(qualifications=Waelchi-Waelchi, education=Cremin Group, students=[]), lastUpdatedTimestamp=2024-02-03T14:10:30.160628, lastUpdatedBy=null)
2024-02-17T12:15:03.128+01:00 WARN 17540 --- [school_system] [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 23505
2024-02-17T12:15:03.128+01:00 ERROR 17540 --- [school_system] [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: duplicate key value violates unique constraint "student_lessons_pkey"
Detail: Key (student_id, lesson_id)=(25, 7) already exists.
2024-02-17T12:15:03.156+01:00 ERROR 17540 --- [school_system] [nio-8080-exec-2] c.s.exception.CustomExceptionHandler : could not execute statement [ERROR: duplicate key value violates unique constraint "student_lessons_pkey"
Detail: Key (student_id, lesson_id)=(25, 7) already exists.] [insert into student_lessons (lesson_id,student_id,deleted) values (?,?,false)]; SQL [insert into student_lessons (lesson_id,student_id,deleted) values (?,?,false)]; constraint [student_lessons_pkey]
org.springframework.dao.DataIntegrityViolationException: could not execute statement [ERROR: duplicate key value violates unique constraint "student_lessons_pkey"
Detail: Key (student_id, lesson_id)=(25, 7) already exists.] [insert into student_lessons (lesson_id,student_id,deleted) values (?,?,false)]; SQL [insert into student_lessons (lesson_id,student_id,deleted) values (?,?,false)]; constraint [student_lessons_pkey]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:290) ~[spring-orm-6.1.3.jar:6.1.3]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:241) ~[spring-orm-6.1.3.jar:6.1.3]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:565) ~[spring-orm-6.1.3.jar:6.1.3]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:794) ~[spring-tx-6.1.3.jar:6.1.3]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:757) ~[spring-tx-6.1.3.jar:6.1.3]
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:669) ~[spring-tx-6.1.3.jar:6.1.3]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:419) ~[spring-tx-6.1.3.jar:6.1.3]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-6.1.3.jar:6.1.3]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.3.jar:6.1.3]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:765) ~[spring-aop-6.1.3.jar:6.1.3]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:717) ~[spring-aop-6.1.3.jar:6.1.3]
at com.school_system.service.impl.LessonServiceImpl$$SpringCGLIB$$0.updateLesson(<generated>) ~[classes/:na]
at com.school_system.controller.LessonController.updateLesson(LessonController.java:42) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:351) ~[spring-aop-6.1.3.jar:6.1.3]
我尝试更改 ManyToMany CasCadeType 和 FetchType 我已经添加了这个方法,但它没有帮助 addStudent(Student s)、removeStudent(Student s)、addLesson(Lesson l) 和 removeLesson(Lesson l)
您定义了唯一的键约束。以下对(student_id、lesson_id)构成表中的唯一键。
根据定义:UNIQUE 约束确保列中的所有值都不同。
您似乎正在尝试插入值 (25, 7),这违反了定义的唯一约束。
尝试插入不违反唯一约束的数据,它应该可以工作。