Вставляемый объект в классе с аннотацией @IdClass

Можно ли сделать каскадное сохранение в Section.class? Я создаю объект Раздела и добавляю новые вопросы без идентификатора. Когда я пытаюсь сохранить его, я получаю сообщение об ошибке:

org.postgresql.util.PSQLException: ОШИБКА: вставка или обновление таблицы "question_to_section" нарушает ограничение внешнего ключа "fk_2br9f09ok965403a9rv5y2n10" Подробности: Ключ (question_id)=(0) отсутствует в таблице "вопрос".

Я также пытался использовать аннотацию @Embedded, но безуспешно.

Класс раздела:

@Table(name = "section")
@Entity(name = "section")
public class Section implements Serializable{   

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id",    unique = true, nullable = false)
    @JsonProperty
    long id;    

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "section")
    Set<QuestionToSection> questions = new HashSet<QuestionToSection>(0);
    ...
}

Вопрос к классу раздела

@Entity(name = "question_to_section")
@Table(name = "question_to_section")
@IdClass(QuestionSectionId.class)
public class QuestionToSection implements Serializable {

    @Id
    long sectionId;

    @Id
    long questionId;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "sectionId", nullable = false, updatable = false, insertable = false, referencedColumnName = "id")
    Section section;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL )
    @JoinColumn(name = "questionId", nullable = false, updatable = false, insertable = false, referencedColumnName = "id")
    Question question;
    ...
}

QuestionSectionId

public class QuestionSectionId implements Serializable {

    long questionId;
    long sectionId; 

}

2 ответа

Решение

Не обязательно, чтобы каждая таблица в вашей базе данных была сопоставлена ​​с сущностью. В частности, таблицы чистого соединения обычно лучше не отображать как объекты. Для отношения "многие ко многим", например:

@Table(name = "section")
public class Section implements Serializable{   

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    @JsonProperty
    long id;    

    // Note: probably do not want to cascade REMOVE operations
    @ManyToMany(fetch = FetchType.LAZY,
        cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH},
        mappedBy = "sections")
    @JoinTable(
        name = "question_to_section"
        joinColumns =
            @JoinColumn(name="sectionId", referencedColumnName="ID"),
        inverseJoinColumns=
            @JoinColumn(name="questionId", referencedColumnName="ID")
    )
    Set<Question> questions = new HashSet<Question>(0);
    ...
}

Это предполагает двунаправленную связь с соответствующими аннотациями в сущности Question; для однонаправленного, удалите mappedBy атрибут ManyToMany, В любом случае, это освобождает вас от управления question_to_entity таблицы, и уменьшает количество отдельных объектов в системе.


Если, с другой стороны, вам нужно добросовестно QuestionToSection слабая сущность - т. е. если у экземпляров есть свойства, отличные от вопросов и разделов, которые они связывают, - то у вас действительно есть проблема с каскадным постоянством. То, что вы пытаетесь сделать, отображает более одного свойства объекта в один и тот же столбец в вашей БД, что дает возможность для несогласованности.

И есть несоответствие, если один или оба из Question а также Section Участвующие объекты являются новыми, так что у него еще нет идентификатора. JPA не может знать, что он должен установить QuestionToSection поля идентификаторов из идентификаторов связанных Question а также Section юридические лица. Когда они получают свои автоматически сгенерированные идентификаторы, QuestionToSection связанные с ними сущности станут противоречивыми.

Вполне возможно, что вы могли бы обойти проблему, установив @Access(AccessType = PROPERTY) на QuestionToSection и играть в игры с помощью методов доступа к постоянным свойствам, но это довольно неприятно. Если вам здесь нужна слабая сущность, то, вероятно, лучше не пытаться каскадировать PERSIST операции к нему, хотя вы можете каскадировать другие операции. И на самом деле, в этом случае вы, вероятно, хотите каскадировать REMOVE операции со слабой сущностью, но не от нее до других связанных сущностей.

Вы должны удалить insertable = false от обоих @JoinColumn в классе QuestionToSection, Делая это, вы отменяете каскадную вставку, поэтому JPA не будет вставлять, если они не существуют. Просто удалите вставку из аннотации, и это может решить вашу проблему.

Другие вопросы по тегам