Весна: Другой объект с тем же значением идентификатора уже был связан с сеансом

#java #spring-boot #many-to-many #one-to-many #composite-key

Вопрос:

У меня есть две сущности (Курс и Пользователь), связанные @ManyToMany . User может оценивать множество курсов и Course может быть оценен несколькими пользователями. Поэтому я должен хранить в своей базе userId данных courseId и рейтинг , соответствующий этой ассоциации. Следуя этому примеру, я сделал такие сущности:

 public class Course {
    @Id
    @GeneratedValue(generator = "inc")
    @GenericGenerator(name = "inc", strategy = "increment")
    private int courseId;
    @NotBlank(message = "Add the course's title!")
    private String title;

    @OneToMany(mappedBy = "course", cascade = CascadeType.ALL)
    private Set<CourseRating> ratings;
}

public class User {
    @Id
    @GeneratedValue(generator = "inc")
    @GenericGenerator(name = "inc", strategy = "increment")
    private int userId;
    @NotBlank(message = "Add username!")
    private String name;

    @OneToMany(mappedBy = "user")
    private Set<CourseRating> ratings;
}
 

И моя ассоциативная сущность с составным ключом:

 public class CourseRating {
    @EmbeddedId
    private CourseRatingKey id;

    @ManyToOne
    @MapsId("userId")
    @JoinColumn(name = "user_id")
    private User user;

    @ManyToOne
    @MapsId("courseId")
    @JoinColumn(name = "course_id")
    private Course course;

    private Double rating = null;

    public CourseRating() {
    }

    public CourseRating(CourseRatingKey key) {
        this.id = key;
    }
 

Где составной ключ выглядит следующим образом:

 public class CourseRatingKey implements Serializable {
    @Column(name = "user_id")
    private int userId;

    @Column(name = "course_id")
    private int courseId;
  
    // equals() amp; hashCode()
}
 

Моя логика, ответственная за привязку пользователя к курсу (сейчас даже не оценена):

 @Transactional
    public CourseRating buyCourse(CourseRatingKey key) {
        var relationship = new CourseRating(key);
        var user = userRepository.findById(key.getUserId())
                .orElseThrow(() -> new IllegalArgumentException("No user with given id"));
        var course = repository.findById(key.getCourseId())
                .orElseThrow(() -> new IllegalArgumentException("No course with given id"));
        course.setRatings(new HashSet<>(Set.of(relationship)));
        user.setRatings(new HashSet<>(Set.of(relationship)));

        relationship.setCourse(course);
        relationship.setUser(user);

        // ratingRepository is `CourseRatingRepository` that extends
        // JpaReposiory<CourseRating, CourseRatingKey>
        return ratingRepository.save(relationship);
    }
 

объяснение логики: @ManyToMany на самом деле это похоже на двойную @OneToMany ( двунаправленную в моем случае ) аннотацию, поэтому я должен синхронизировать обе стороны этих ассоциаций.
когда эта логика выполняется, я получаю такую ошибку:
javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.projects.spring.udemy.relationship.CourseRating#com.projects.spring.udemy.relationship.CourseRatingKey@239a]