Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)

like image 774
phoenix7360 Avatar asked Dec 14 '12 10:12

phoenix7360


People also ask

How do you map a one-to-many relationship in Hibernate?

The <many-to-one> element is used to set the relationship between EMPLOYEE and ADDRESS entities. The name attribute is set to the defined variable in the parent class, in our case it is address. The column attribute is used to set the column name in the parent table EMPLOYEE.

How do you define one-to-many mapping in JPA?

The One-To-Many mapping comes into the category of collection-valued association where an entity is associated with a collection of other entities. Hence, in this type of association the instance of one entity can be mapped with any number of instances of another entity.

How does Hibernate handle one-to-many relationships?

One To Many Mapping in Hibernate. In simple terms, one to many mapping means that one row in a table can be mapped to multiple rows in another table. For example, think of a Cart system where we have another table for Items. A cart can have multiple items, so here we have one to many mapping.

How do you write one-to-many relationship in JPA?

Define Data Model for JPA One to Many mapping – @Entity annotation indicates that the class is a persistent Java class. – @Table annotation provides the table that maps this entity. – @Id annotation is for the primary key. – @GeneratedValue annotation is used to define generation strategy for the primary key.


1 Answers

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.

like image 185
JB Nizet Avatar answered Sep 30 '22 20:09

JB Nizet