Search code examples
javahibernateannotationsremovechild

Hibernate exception during deletion of @ManyToMany association


I have a confusing problem which I haven't figured out how to solve. if you can offer a suggestion of how I can fix my problem I would be grateful.

So I have the following entity relationship model here.

The mapping of User.class is:

@Entity
@Table(name = "CRM_USER")
public class User {    

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "USER_ID")
    private Long id;

    @Column(name = "FIRST_NAME")
    private String firstName;

    @Column(name = "LAST_NAME")
    private String lastName;

    @Column(name = "BIRTHDATE")
    private Date birthDate;

    @Column(name = "EMAIL")
    private String email;

    @OneToOne(cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private UserAdditionalInfo additionalInfo;

    @ManyToOne
    @JoinColumn(name = "TEAM_FK")
    private Team team;

    @ManyToOne
    @JoinColumn(name = "JOB_FK")
    private Job job;

    @ManyToOne
    @JoinColumn(name = "ORGANIZATION_FK", nullable = false)
    private Organization organization;

    @OneToOne(cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private Security security;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "INFO_FILE_FK")
    private InfoFile profilePicture;

    @ManyToOne
    @JoinColumn(name = "COUNTRY_FK")
    private Country country;

   // Getters and Setters
}


The mapping of Comment.class is:

@Entity
@Table(name = "CRM_COMMENT")
public class Comment implements Serializable {

    private static final long serialVersionUID = -104145851368148154L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "COMMENT_ID")
    private Long id;

    @ManyToOne
    @JoinColumn(name = "ARTICLE_ID")
    private Article article;

    @ManyToOne
    @JoinColumn(name = "USER_FK", nullable = false)
    private User createdUser;

    @Column(nullable = false)
    private String comment;

    @Column(name = "CREATION_DATE", nullable = false)
    private Date creationDate;

    @Column(name = "MODIFICATION_DATE")
    private Date modificationDate;

    @OneToMany(cascade = {CascadeType.ALL}, orphanRemoval = true)
    @JoinTable(name = "CRM_COMMENT_LIKE",
            joinColumns = {@JoinColumn(name = "COMMENT_ID")},
            inverseJoinColumns = {@JoinColumn(name = "USER_ID")})
    private Set<User> fans = new LinkedHashSet<>();

    // Getters and Setters
}


The mapping of Article.class is:

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

    // other properties

    @OneToMany(mappedBy = "article", cascade = {CascadeType.ALL}, orphanRemoval = true)
    @OrderBy("id DESC")
    private Set<Comment> comments = new LinkedHashSet<>();

    // Getters and Setters
}

The problem is related to my ManyToMany relation between the Comment and User - CRM_COMMENT_LIKE.
Actually, when I add some new 'fan' into Comment, there is no problem.

@Override
    public boolean giveAnLikeToComment(Long commentId, User fan) {
        Comment comment = commentDao.get(commentId);
        if (Objects.isNull(comment)|| BooleanUtils.isTrue(comment.getFans().contains(fan))) {
            return false;
        }

        comment.getFans().add(fan);
        commentDao.update(comment);

        return true;
    }

The problem arises when I try to delete some comment, which has at least one 'like'/'fan' to it.

@Override
    public boolean deleteCommentById(final Long commentId) {
        Comment comment = commentDao.get(commentId);
        if (Objects.nonNull(comment)) {
            Article article = comment.getArticle();
            article.getComments().remove(comment);
            comment.setFans(null); // This line fix the problem
            articleDao.update(article);
            return true;
        }
        return false;
    }

So in this case, I manage the relation between an Article ( which is parent of a Comment) and the comment itself. This is easy, because the connection between them is bidirectional. But what about the fans? I can't remove the connection between a Comment and CRM_COMMENT_LIKE relation, because the User doesn't know about the CRM_COMMENT_LIKE or about the Comments. Something more, I want, when I remove a Comment, to remove and all created relations in CRM_COMMENT_LIKE. But I'm prevent, because Hibernate throws an exception which says:

deleted object would be re-saved by cascade (remove deleted object from associations): [crm.alltogether.core.admin.model.User#1]; nested exception is org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): [crm.alltogether.core.admin.model.User#1]

This is my issue, so if you have a suggestion, I would be glad to read it :)

Best Regards,


Solution

  • You need to have a orphanRemoval=true cascading between Article and Comment. Then you would do this.

    if (Objects.nonNull(comment)) {
          Article a = comment.getArticle();
          a.getComments().remove(comment);
          articleDao.saveOrUpdate(a);
          return true;
    }
    

    This will take care of deleting orphan comment, as you already have a cascade all on fans it would delete that association as well. I would suggest you to play around with cascade orphanRemoval=true on fans as well.