phoenix7360
phoenix7360

Reputation: 2917

JPA/Hibernate mapping table for One-to-many relationships

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)

Upvotes: 5

Views: 13547

Answers (1)

JB Nizet
JB Nizet

Reputation: 692151

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.

Upvotes: 8

Related Questions