javahibernatejpaannotationsjava-persistence-api

Insertable object in a class with @IdClass annotation


Is it possible to do a cascade save in Section.class? I create Section object and add new questions without id. When I try save it I get error:

org.postgresql.util.PSQLException: ERROR: insert or update on table "question_to_section" violates foreign key constraint "fk_2br9f09ok965403a9rv5y2n10" Details: Key (question_id)=(0) is not present in table "question".

I also tried to use @Embedded annotation, but unsuccessfully.

Section class:

@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);
    ...
}

Question To Section class

@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; 

}

Solution

  • It is not necessary for every table in your database to be mapped to an entity. In particular, pure join tables are usually better not mapped as entities. For a many-to-many relationship, for example:

    @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);
        ...
    }
    

    That presumes a bidirectional relationship, with corresponding annotations in entity Question; for unidirectional, remove the mappedBy attribute of the ManyToMany. Either way, this relieves you of managing the question_to_entity table directly, and reduces the number of discrete entities in the system.


    If, on the other hand, you need a bona fide QuestionToSection weak entity -- i.e. if instances have properties other than question and section they associate -- then you indeed have a problem with cascading persistence. What you're trying to do maps more than one entity property to the same column in your DB, thus affording opportunities for inconsistency.

    And there is an inconsistency if one or both of the Question and Section entities involved is new, so that it does not yet have an ID. JPA has no way to know that it's supposed to set QuestionToSection's id fields from the IDs of the associated Question and Section entities. When they receive their autogenerated IDs, the QuestionToSection entities associated with them will become inconsistent.

    It is conceivable that you could work around the problem by setting @Access(AccessType = PROPERTY) on QuestionToSection, and playing games with the persistent property accessor methods, but this is kinda nasty. If you do need a weak entity here then it's probably better to not try to cascade PERSIST operations to it, though you could cascade other operations. And in fact, in this case you probably do want to cascade REMOVE operations to the weak entity, but not from it to the other associated entities.