5

I'm using JPA annotations only and hibernate 4.0.1 as the JPA implementation.

I have anAssessmentclass that has two sets of Comment:

@Entity
@Table(name = "ASSESSMENT")
public class Assessment{

    @OneToMany(fetch = FetchType.EAGER)
    private Set<Comment> auditComments = new HashSet<Comment>();

    @OneToMany(fetch = FetchType.EAGER)
    private Set<Comment> comments = new HashSet<Comment>();
}

(I'm skipping getters, setters, cascade annotations and other irrelevant lines of code)

Comment:

@Entity
@Table(name = "COMMENT")
public class Comment{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(columnDefinition = "text")
    private String text;
}

Now, because a Comment doesn't have a reference back to Assessment (this is on purpose), I cannot uset the mappedBy property and Hibernate needs to create a mapping table between Comment and Assessment. By default, it is named ASSESSMENT_COMMENT.

If I had only one set of Comment in Assessment, that would work perfectly fine. The ASSESSMENT_COMMENT would have only two columns:

[ASSESSMENT_ID, COMMENT_ID]

which will perfectly represent the one-to-many relationship.

Now here is the problem:

Because I don't have one set of Comment but two, Hibernate tries to map the two sets using only one ASSESSMENT_COMMENT table. This table has 3 columns:

[ASSESSMENT_ID, COMMENT_ID, AUDITCOMMENT_ID]

On top of that, the 3 columns are set as non nullable.

That certainly cannot work. For instance, if I have only one item in auditComents and none in comments, hibernate tries to insert one row with a null in COMMENT_ID which creates a SQL exception.

Questions:

To me it looks like a bug. There should be two mapping tables, not one.

So, 1) is it a known bug in Hibernate? 2) is there a way around that? Can I force Hibernate to create two mapping tables, one for each of the set. Bear in mind that I cannot change the Comment class to have a reference to Assessment (business logic requirement)

1 Answer 1

8

First of all, you could use two columns in the comment table to point back to their assessment:

@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "audit_commented_assessment_id")
private Set<Comment> auditComments = new HashSet<Comment>();

@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "commented_assessment_id")
private Set<Comment> comments = new HashSet<Comment>();

If you want two join tables, the just say so:

@OneToMany(fetch = FetchType.EAGER)
@JoinTable(name = "assessment_audit_comment",
           joinColumns = @JoinColumn(name = "assessment_id"),
           inverseJoinColumns = @JoinColumn(name = "comment_id"))
private Set<Comment> auditComments = new HashSet<Comment>();

@OneToMany(fetch = FetchType.EAGER)
@JoinTable(name = "assessment_comment",
           joinColumns = @JoinColumn(name = "assessment_id"),
           inverseJoinColumns = @JoinColumn(name = "comment_id"))
private Set<Comment> comments = new HashSet<Comment>();

That is of course explained in the documentation.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks it fixed it. I never had two collection of the same type so I never had to use the @JoinTable annotation.
How would the Comment class look if you would like to have a bidirectional relation? E.g. in the Comment POJO I would like to have a reference to an Assessment? You can not use two @jointable (one against each mapping table of the parent)? How would you do it?
@nize: If the associations were bidirectional, you would have to make Comment the owner side of the associations. In Comment, you would have a field commentedAssessment and a field auditedAssessment. They would be mapped with @ManyToOne, with a join table as above, but with joinColumns and inverseJoinColumns switched. In Assessment, you would simply have @OneToMany(mappedBy = "auditedAssessment") private Set<Comment> auditComments; and @OneToMany(mappedBy = "commentedAssessment") Set<Comment> comments;
Ok, but assume the case is that a comment must either be a auditComment OR a comment, not both. That is why I would like to have the Comment to only have a reference "Assessment parent", not two references "commentedAssessment" and "auditedAssessment". Would that be possible to achieve or do I need to have two references of which one always will be NULL? Maybe then I need to extend Comment with two classes, AuditComment and NormalComment?
I tried that path, but I experienced it to be a mess since the mappedBy reference to the superclass didn't work as I expected. I decided to try another alternative: In the class Assessment I have the List<Comment> comments. In the Comment class I use a boolean isAuditComment. Then I can provide methods in the Assessment class like List<Comment> getAuditingComments() and List<Comment> getOtherComments(). Not as elegant, but less complex from a JPA perspective.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.