Hmmm dobra, ustalmy fakty. Po pierwsze, używając poprawnie JPA/Hibernate nie musisz wołać jawnie save/update. Po to używasz ORM-a, żeby nie musieć - masz dirty checking z paczki. Po drugie, faktyczne inserty/updaty lecą po zacomittowaniu transakcji, a wiec poza Twoim kodem. Masz tego świadomość?
Napisz dokładnie jakie pola nie są ustawione? Dostajesz jakiś exception? Co robi ten prepareStudentScore? Pokazałeś bardzo mało istotnego kodu.
- Nie bardzo rozumiem jak to nie trzeba robić 'repository.save' żeby pchnąć zmiany do DB?
-
Napisz dokładnie jakie pola nie są ustawione? Wszystkie relacje
- Exception leci w metodzie 'PrePersist' w klasie 'StudentScoreEntity' ze względu na to że lista w tym momencie jest pusta mimo iż w metodzie 'addStudentQuestion' jest not null. W sumie wrzucę całe encje może coś przeoczyłem.
@EqualsAndHashCode(exclude = {"questions", "studentScores"})
@ToString(exclude = {"questions", "studentScores"})
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Getter(value = AccessLevel.PACKAGE)
@Entity
@Table(name = "exam")
class ExamEntity {
@Id
@Setter(value = AccessLevel.NONE)
private UUID id;
@Enumerated(EnumType.STRING)
@Column
private ExamStatus status;
@OneToMany(mappedBy = "exam", cascade = CascadeType.PERSIST)
private List<StudentScoreEntity> studentScores;
@ManyToMany
@JoinTable(name = "rel_question_exam",
joinColumns = @JoinColumn(name = "fk_exam"),
inverseJoinColumns = @JoinColumn(name = "fk_question"))
private List<QuestionEntity> questions;
void addStudentScore(final StudentScoreEntity studentScore) {
studentScores.add(studentScore);
}
void validateExamStatusForModification() {
if (status != ExamStatus.CREATED) {
throw new ExamModificationException(id, status);
}
}
void validateStudentsFromGroupAlreadyExist(final UUID groupId) {
final boolean haveStudents = studentScores.stream()
.map(StudentScoreEntity::getStudentDetails)
.map(StudentDetailsEntity::getStudentGroup)
.map(StudentGroupEntity::getId)
.anyMatch(x -> x.equals(groupId));
if (haveStudents) {
throw new StudentGroupAlreadyExistInExamException(id, groupId);
}
}
}
@EqualsAndHashCode(exclude = {"studentDetails", "exam", "studentQuestions"})
@ToString(exclude = {"studentDetails", "exam", "studentQuestions"})
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Getter(value = AccessLevel.PACKAGE)
@Entity
@Table(name = "student_score")
class StudentScoreEntity {
@Id
@GeneratedValue
private UUID id;
@Enumerated(value = EnumType.STRING)
private StudentScoreStatus status;
@JoinColumn(name = "fk_exam")
@ManyToOne
private ExamEntity exam;
@JoinColumn(name = "fk_student_details")
@ManyToOne
private StudentDetailsEntity studentDetails;
@JoinColumn(name = "fk_student_score")
@OneToMany(cascade = CascadeType.PERSIST)
private List<StudentQuestionEntity> studentQuestions;
@Builder
StudentScoreEntity(final StudentDetailsEntity studentDetails, final ExamEntity exam) {
this.studentDetails = studentDetails;
this.exam = exam;
studentQuestions = new ArrayList<>();
}
void addStudentQuestion(final StudentQuestionEntity StudentQuestion) {
studentQuestions.add(StudentQuestion);
}
@PrePersist
private void prePersist() {
status = StudentScoreStatus.CREATED;
Collections.shuffle(studentQuestions);
}
}
@EqualsAndHashCode(exclude = {"studentAnswers", "studentScore"})
@ToString(exclude = {"studentAnswers", "studentScore"})
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Getter(value = AccessLevel.PACKAGE)
@Setter(value = AccessLevel.PACKAGE)
@Entity
@Table(name = "student_question")
class StudentQuestionEntity {
@Id
@Setter(value = AccessLevel.NONE)
@GeneratedValue
private UUID id;
@JoinColumn(name = "fk_question")
@ManyToOne
private QuestionEntity question;
@JoinColumn(name = "fk_student_score")
@ManyToOne
private StudentScoreEntity studentScore;
@JoinColumn(name = "fk_student_question")
@OneToMany(cascade = CascadeType.PERSIST)
private List<StudentAnswerEntity> studentAnswers;
@Builder
StudentQuestionEntity(final QuestionEntity question, final StudentScoreEntity studentScore) {
this.question = question;
this.studentScore = studentScore;
studentAnswers = new ArrayList<>();
}
void addStudentAnswer(final StudentAnswerEntity studentAnswer) {
studentAnswers.add(studentAnswer);
}
@PrePersist
private void prePersist() {
Collections.shuffle(studentAnswers);
}
}
package com.exam.feature.admin.exam.update;
import com.exam.commons.exception.custom.not_found.ExamNotFound;
import com.exam.commons.exception.custom.not_found.StudentGroupNotFound;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.UUID;
@Slf4j
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
@Service
class UpdateExamService {
private final ExamRepository examRepository;
private final StudentGroupRepository studentGroupRepository;
@Transactional
public void addStudentsToExamByGroup(final UUID examId, final UUID groupId) {
final ExamEntity exam = findExamById(examId);
exam.validateExamStatusForModification();
exam.validateStudentsFromGroupAlreadyExist(groupId);
prepareStudentScores(groupId, exam);
examRepository.save(exam);
log.info("Exam with id '{}' has been updated by student from groups with id '{}'.", examId, groupId);
}
private void prepareStudentScores(final UUID groupId, final ExamEntity exam) {
final List<StudentDetailsEntity> students = findStudentByGroupId(groupId).getStudentDetails();
final List<QuestionEntity> question = exam.getQuestions();
for (final StudentDetailsEntity student: students) {
final StudentScoreEntity studentScore = createStudentScore(exam, student);
prepareStudentQuestions(question, studentScore);
exam.addStudentScore(studentScore);
}
}
private void prepareStudentQuestions(final List<QuestionEntity> selectedQuestions, final StudentScoreEntity studentScore) {
for (final QuestionEntity question: selectedQuestions) {
final StudentQuestionEntity studentQuestion = createStudentQuestion(studentScore, question);
prepareStudentAnswers(studentQuestion);
studentScore.addStudentQuestion(studentQuestion);
}
}
private void prepareStudentAnswers(final StudentQuestionEntity studentQuestion) {
final List<AnswerEntity> answers = studentQuestion.getQuestion().getAnswers();
for (final AnswerEntity answer: answers) {
final StudentAnswerEntity studentAnswer = createStudentAnswer(studentQuestion, answer);
studentQuestion.addStudentAnswer(studentAnswer);
}
}
private StudentScoreEntity createStudentScore(final ExamEntity exam, final StudentDetailsEntity student) {
return StudentScoreEntity.builder()
.studentDetails(student)
.exam(exam)
.build();
}
private StudentAnswerEntity createStudentAnswer(final StudentQuestionEntity studentQuestion, final AnswerEntity answer) {
return StudentAnswerEntity.builder()
.answer(answer)
.studentQuestion(studentQuestion)
.build();
}
private StudentQuestionEntity createStudentQuestion(final StudentScoreEntity studentScore, final QuestionEntity question) {
return StudentQuestionEntity.builder()
.question(question)
.studentScore(studentScore)
.build();
}
private ExamEntity findExamById(final UUID examId) {
return examRepository.findById(examId).orElseThrow(() -> new ExamNotFound(examId));
}
private StudentGroupEntity findStudentByGroupId(final UUID groupId) {
return studentGroupRepository.findById(groupId).orElseThrow(() -> new StudentGroupNotFound(groupId));
}
}