JPA Cascaded Update/Delete of OneToMany

Viewed 1028

I have two entities "Article" and "Comments". Article has OneToMany relationship with Comment.

@Entity
@Table(name = "article")
public class Article implements Serializable
{
    public Article()
    {

    }
    private static final long serialVersionUID = 1L;
    @Id
    @Column(name = "article_id")
    private int articleId;
    @Column(name = "title")
    private String title;
    @Column(name = "category")
    private String category;
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "articleId", cascade = CascadeType.ALL)
    private List<Comment> comments = new ArrayList<>();
}


@Entity
@Table(name = "comment")
public class Comment
{
    private static final long serialVersionUID = 1L;
    public Comment()
    {

    }
    @Id
    @Column(name = "comment_id")
    private int commentId;
    @Column(name = "author")
    private String author;
    @Column(name = "text")
    private String text;

    @JoinColumn(name = "article_id", nullable = false)
    private int articleId;
}

I am using JPA EntityManager to perform CRUD operations on Article.

I have an article with the following data in the "article" table.

article_id         title        category 

     1       Java 8 in 30 days    Java

I have two comments with the following data in the "comment" table.

comment_id   author    text              article_id

    1         ABC     Java is awesome !        1
    2         XYZ     Nice Article !!!         1

Here is my EntityManager code which gets invoked when there is an update to an article including comments.

public Article update(Article article)
{
    return entityManager.merge(article);
}

The issue that I am facing here is that whenever I delete a Comment from an existing article using the above method call then the comment really does not get deleted from the table. I understand that "merge" is same as "upsert", but I did not find any other method in EntityManager interface to achieve the comment deletion along with other changes to article.

1 Answers

In your code, @OneToMany(... cascade = CascadeType.ALL) means that whenever a modification is done on the parent (persist, delete, etc.), it is cascaded to the children as well. So if an Article is saved, all its' corresponding Comments are saved. Or if an Article is deleted, all its' corresponding Comments are deleted.

In your case, what you want is just to delete a Comment, unrelated to operations that happen in its' Article.

An easy way to do that is use @OneToMany(... cascade = CascadeType.ALL, orphanRemoval = true) and then when you decide to trigger delete on the first Comment for example, just use Collection.remove() on it:

Article article = ...;
//...
Comment targetComment = article.getComments().iterator().next();
article.getComments().remove(targetComment);
// now Hibernate considers `targetComment` as orphan and it executes an SQL DELETE for it

Make sure your collection is the only one holding a reference to the object referred by targetComment, otherwise you will have inconsistencies in memory.

Related