Issue
I'm doing a sort of a feed with posts and comments
Post entity
public class Post extends AuditEntity {
@Lob
private String text;
@Column
private int totalCmnts = 0;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "post_id")
private Set<Comment> comments;
@ManyToOne
@JoinColumn(name = "user_id", referencedColumnName = "id")
private MEUser MEUser;
}
Comment entity
public class Comment extends AuditEntity {
@Lob
private String text;
@ManyToOne
@JoinColumn(name = "user_id", referencedColumnName = "id")
private MEUser MEUser;
}
and i have a service to delete a comment where i fetch the post and the comment, i check if the current user can execute the operation then i delete the comment and decrement the total count of comments in post entity.
the problem in the log i can see the select operation and the update operation but the delet is not executed at all and still getting HTTP 200 with @transactional annotation in the service class
I tried an SQL query and it worked fine
DELETE FROM `db-engine`.comment WHERE comment.id = 2
Service
@Override
public void deleteComment(Long commentID, Long postID, UserPrincipal currentUser) {
Comment comment = commentsRepository.findById(commentID)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
Post post = postsRepository.findById(postID)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
if (comment.getMEUser().getId().equals(currentUser.getId())
|| currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_ADMIN.toString()))
|| currentUser.getAuthorities().contains(new SimpleGrantedAuthority(RoleName.ROLE_SUPER_ADMIN.toString()))) {
commentsRepository.deleteById(comment.getId());
post.setTotalCmnts(post.getTotalCmnts() - 1);
System.out.println(comment.getId());
System.out.println("hello");
}
}
Solution
The issue is that you are changing the post
entity with post.setTotalCmnts(post.getTotalCmnts() - 1);
. This means that Hibernate will notice the difference between your entity and the data in database and flush it (thus updating the post
in the database).
Since you have @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
on comments
this means that Hibernate will cascade the persist operation to comments
entities. In this list, you still have the Comment
you want to delete and thus it is created again (there might be some Hibernate optimization here that understands this and actually does not delete it from the database instead).
You need to also remove the Comment
you want to delete from the post
comments
list as follows:
commentsRepository.deleteById(comment.getId());
post.setTotalCmnts(post.getTotalCmnts() - 1);
post.setComments(post.getComments().remove(comment));
System.out.println(comment.getId());
System.out.println("hello");
Additionally, I would add a custom setComments()
method that actually updates totalCmnts
based on the given parameter, otherwise, you might have inconsistent data. Also, the getter should return a copy of the list and not the list itself, otherwise, you might have some pretty nasty bugs because you change the list elsewhere and this change gets persisted without you noticing it.
Answered By - João Dias
Answer Checked By - Candace Johnson (JavaFixing Volunteer)