Hibernate recreates join table when adding to List

I have a Story containing a list of Comment, mapped with a JoinTable. I noticed that every time I add a new Comment to the list, hibernate deletes and recreates all entries in the join table related to the story. I’d expect it to just add a new row to the table. In the code below, the save methods are implemented by Spring Data. Am I missing something? Thank you.

Story.java:

@Entity
public class Story implements Serializable {
    @OneToMany
    @JoinTable(name="UserComment", joinColumns = @JoinColumn(name = "Story_id"), inverseJoinColumns = @JoinColumn(name = "Comment_id"))
    private List<Comment> userComments;
    ...

Comment.java:

@Entity
public class Comment implements Serializable {
...

Adding a new Comment:

Comment comment = new Comment();
comment.setContent(content);
commentRepository.save(comment);
story.getUserComments().add(comment);
storyRepository.save(story);

Hibernate log on the storyRepository.save(story) execution:

Hibernate: delete from UserComment where Story_id=?
Hibernate: insert into UserComment (Story_id, Comment_id) values (?, ?)
Hibernate: insert into UserComment (Story_id, Comment_id) values (?, ?)
Hibernate: insert into UserComment (Story_id, Comment_id) values (?, ?)
Hibernate: insert into UserComment (Story_id, Comment_id) values (?, ?)
Hibernate: insert into UserComment (Story_id, Comment_id) values (?, ?)
Hibernate: insert into UserComment (Story_id, Comment_id) values (?, ?)
Hibernate: insert into UserComment (Story_id, Comment_id) values (?, ?)

Library versions:

  • Hibernate 4.3.5
  • Spring Data JPA 1.6.0

Answer

That’s the expected behavior for a unidirectional bag used. According to [Hibernate Anti-Patterns][1]:

The bag semantics has the worst performance when it comes to the number of operations since it always re-creates the entire collection. Hibernate issues a delete statement to remove all associations of the old collection from the association table. Then, it issues N inserts to add all associations representing the new collection to the association table. Hibernate does not analyze how many elements have been changed in the collection.

  1. You can optimize it by turning it into an id-bag, which is an indexed List.
  2. You can map the UserComment mapping having 2 @ManyToOne associations to both Story and Comment, so your bag will turned into a mappedBy bidirectional bag which is much more efficient than its unidirectional counterpart (because the mappedBy bag won’t control the association, since it will be the @ManyToOne side to propagate state transitions to SQL statements).

Leave a Reply

Your email address will not be published. Required fields are marked *