Ошибка доступа к полю при обновлении сопоставления «многие ко многим»

#hibernate #spring-boot #jpa

#переход в спящий режим #весенняя загрузка #jpa

Вопрос:

Я пытаюсь сгенерировать множество Team объектов, каждый из которых состоит из нескольких Person объектов, используя JPA. Я знаю, что мне нужно вручную обновить обе стороны отношения «многие ко многим», поэтому это то, что я пытаюсь сделать.

Вот Person.java (для краткости опущены геттеры и установщики).

 /**
 * Represents any person (teaching assistant, student, or other) that exists in this application.
 */
@Entity
@Table(name = "people")
@Inheritance(
        strategy = InheritanceType.JOINED
)
public abstract class Person extends BasicEntity {

    @Column(nullable = false)
    private String firstName;

    @Column(nullable = false)
    private String lastName;

    @Column
    private String emailAddress;

    /**
     * The list of teams that this person belongs to.
     */
    @ManyToMany(
            cascade = CascadeType.ALL,
            fetch = FetchType.LAZY
    )
    @JoinTable(
            name = "team_members",
            joinColumns = @JoinColumn(name = "person_id"),
            inverseJoinColumns = @JoinColumn(name = "team_id")
    )
    private List<Team> teams;

    /**
     * Default constructor for JPA.
     */
    protected Person () {
        this.teams = new ArrayList<>();
    }

    /**
     * Constructs a new Person.
     * @param firstName The person's first name.
     * @param lastName The person's last name.
     * @param emailAddress The person's email address.
     */
    public Person(String firstName, String lastName, String emailAddress) {
        this();
        this.firstName = firstName;
        this.lastName = lastName;
        this.emailAddress = emailAddress;
    }

    public void assignToTeam(Team team) {
        this.teams.add(team);
        //team.addMember(this);
    }
  

Как вы можете видеть, в assignToTeam методе я закомментировал «рефлексивную» часть, где я обновляю Team представление отношения после обновления Person собственного.

Поскольку это необходимо для решения этой проблемы, вот Team.java .

 /**
 * A group consisting of one or more members. Child classes should define P as a sub class of Person to define custom
 * behavior if needed.
 * @param <P> The type of members this group contains.
 */
@Entity
@Inheritance(
        strategy = InheritanceType.JOINED
)
public abstract class Team<P extends Person> extends BasicEntity {

    /**
     * The list of members in this group.
     */
    @ManyToMany(
            cascade = CascadeType.ALL
    )
    @JoinTable(
            name = "team_members",
            joinColumns = @JoinColumn(name = "team_id"),
            inverseJoinColumns = @JoinColumn(name = "person_id")
    )
    protected List<P> members;

    /**
     * The course that this team belongs to.
     */
    @ManyToOne(
            optional = false
    )
    private Course course;

    /**
     * Default constructor for JPA and initializing list of members for any child classes.
     */
    protected Team() {
        this.members = new ArrayList<>();
    }

    public void addMember(P person) {
        if (!this.containsMember(person)) {
            this.members.add(person);
        }
    }
  

Проблема возникает, когда я генерирую набор тестовых данных для своего приложения, и я вызываю следующий метод:

 private List<StudentTeam> generateStudentTeams() {
        StudentGenerator studentGenerator = new StudentGenerator(this.getSeed());
        List<StudentTeam> teams = new ArrayList<>(this.studentGroupCount);
        for (int i = 0; i < this.studentGroupCount; i  ) {
            StudentTeam team = new StudentTeam();
            List<Student> students = studentGenerator.generateList(this.studentGroupSize);
            for (Student s : students) {
                s.assignToTeam(team);
            }
            teams.add(team);
        }
        return teams;
    }
  

что приводит к некоторым ошибкам следующего характера:
Caused by: org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [private nl.andrewlalis.teaching_assistant_assistant.model.people.TeachingAssistantRole nl.andrewlalis.teaching_assistant_assistant.model.people.TeachingAssistant.role] by reflection for persistent property [nl.andrewlalis.teaching_assistant_assistant.model.people.TeachingAssistant#role] : nl.andrewlalis.teaching_assistant_assistant.model.people.Student@1028bf36

Мой вопрос в том, как я могу правильно установить обе стороны отношения и избежать такой проблемы?

Комментарии:

1. Существует несколько причин для исключения PropertyAccessException. Вы пробовали комментировать атрибут пользователя «teams» только с помощью @ManyToMany (mappedBy = «members»)? Правильно ли сгенерированы таблицы с использованием внешних ключей?

2. @DiegoVictordeJesus Да, я проверил, что все таблицы сгенерированы точно так, как предполагалось, с использованием внешних ключей и так далее. Я даже протестировал создание отдельных объектов в этом вопросе, все из которых работают должным образом.

Ответ №1:

Если вы решите отобразить связь в обоих направлениях, то одно направление должно быть определено как владелец, а другое должно использовать атрибут mappedBy для определения его сопоставления. Это также позволяет избежать необходимости дублировать информацию о объединяемой таблице в обоих местах.

Если mappedBy не используется, то поставщик сохраняемости предположит, что существуют две независимые взаимосвязи, и в конечном итоге вы получите повторяющиеся строки, вставленные в таблицу объединения. Если у вас концептуальная двунаправленная связь, но в базе данных есть две разные таблицы объединения, то вы не должны использовать mappedBy, поскольку вам необходимо поддерживать две независимые таблицы.

https://en.wikibooks.org/wiki/Java_Persistence/ManyToMany#Bi-directional_Many_to_Many