helloApp
helloApp

Reputation: 459

Spring + Hibernate :Unable to delete child in unidirectional relationship

i tried finding similar issue here and i found this here but it did not help much.

this here is the exact issue i have. Thing is the @OnDelete forces me to make a bidirectional relationship right? I would like to keep it unidirectional if possible.

What i want to do is delete a comment (child) from a post (parent);

every time i am trying to do that i receive this error

java.sql.SQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`blogapp2`.`post_comments`, CONSTRAINT `FKrvgf8o4dg5kamt01me5gjqodf` FOREIGN KEY (`comments_id`) REFERENCES `comment` (`id`))

this is my code the delete method

  @GetMapping("/dashboard/showposts/deletecomment/{id}")
    public String deleteComment(@PathVariable("id") Long id) {
        commentService.deleteComment(id);
        return "redirect:/admin/dashboard/showposts";
    }

in Post

 @OneToMany(cascade = CascadeType.ALL)
    private List<Comment> comments = new ArrayList<>();

in Comment there is no reference to the Parent. How can i delete the Comment from the database? i mention that cascade works with no issues so when i delete the parent all the children are deleted aswell. Any help would be greatly appreciated !

L.E:I modified my handler method to look like this

 @GetMapping("/dashboard/showposts/deletecomment/{id}")
    public String deleteComment(@PathVariable("id") Long id) {
        List<Comment> comments = commentService.findAll();
        Comment comment = commentService.findBYId(id);
        comments.remove(comment);
        return "redirect:/admin/dashboard/showposts";
    }

Upvotes: 1

Views: 2149

Answers (2)

code_mechanic
code_mechanic

Reputation: 1148

You can do it without creating bidirectional relationship but there is one caveat in doing so and I would come to that later, let's see how you can do that in unidirectional relationship.

First you need to specify the orphanRemoval=true on your entity relation

@OneToMany(cascade = { CascadeType.ALL }, orphanRemoval = true)
List<Comment> comments = new ArrayList();

// Now I am assuming you have equals and hash code methods are implemented in your comment class,

// So all you need to load the Comment Entity by its id and then have to call

Comment comment = dao.findById(id);
comments.remove(comment);

// This will delete the comment from table and will keep all comments as it is.

// Another way is to iterate the comments list and find matching Comment object (add the method in transaction)

@Service
class PostService {

    @Transactional
    public Comment deleteComment(Integer commentId) {
       Post post = repository.findById(id);
       List<Comment> comments = post.getComments();
       Comment comment = comments.stream().filter(c -> c.getId().equals(commentId)).findAny()
     .orElseThrow(() -> new IllegalArgumentException("Invalid comment id"));
      
      comments.remove(comment);

      return comment;
    }

}

 

Caveats:

1. post.getComments() // will load all comments from database
2. comments.remove(comment) // will trigger additional INSERT statements

Why additional insert statements will be triggered?

When you use uni-directional mapping, JPA provider (hibernate) will create additional junction table and your relation becomes @ManyToMany at the background.

So it will first Delete all the entries from junction table passing associated post_id and then insert all the records back to junction table, leaving the record we deleted.

Then it will delete the entry from comment table, so its a performance penalty you have to pay using unidirectional relation.

Upvotes: 2

francisco neto
francisco neto

Reputation: 807

By the error message, I assume that besides tables post and comments, you have a join table named post_comments.

If thats the case, hibernate is not aware of it. You should use not only @OneToMany but @JoinTable annotation as well in the commentsfield of Post class.

Upvotes: 0

Related Questions