Spring + Hibernate: 无法在单向关系中删除子项

huangapple go评论76阅读模式
英文:

Spring + Hibernate :Unable to delete child in unidirectional relationship

问题

我在这里尝试找到类似的问题,我找到了这个链接,但它并没有帮助太多。

这个链接是我所遇到的确切问题。问题是@OnDelete是否强制我建立双向关系?如果可能的话,我想保持单向关系。

我想要做的是从帖子(父级)中删除评论(子级);

每次我尝试这样做,我都会收到以下错误:

java.sql.SQLIntegrityConstraintViolationException: 无法删除或更新父行外键约束失败(`blogapp2`.`post_comments`,CONSTRAINT `FKrvgf8o4dg5kamt01me5gjqodf` FOREIGN KEY (`comments_id`) REFERENCES `comment` (`id`))

这是我的代码
删除方法

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

在Post类中

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

在Comment类中没有对父级的引用。我如何从数据库中删除评论?我提到级联在没有问题的情况下工作,所以当我删除父级时,所有子级也会被删除。
任何帮助将不胜感激!

附注:我修改了处理方法,使其如下所示

@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";
}
英文:

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(&quot;/dashboard/showposts/deletecomment/{id}&quot;)
    public String deleteComment(@PathVariable(&quot;id&quot;) Long id) {
        commentService.deleteComment(id);
        return &quot;redirect:/admin/dashboard/showposts&quot;;
    }

in Post

 @OneToMany(cascade = CascadeType.ALL)
    private List&lt;Comment&gt; comments = new ArrayList&lt;&gt;();

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(&quot;/dashboard/showposts/deletecomment/{id}&quot;)
    public String deleteComment(@PathVariable(&quot;id&quot;) Long id) {
        List&lt;Comment&gt; comments = commentService.findAll();
        Comment comment = commentService.findBYId(id);
        comments.remove(comment);
        return &quot;redirect:/admin/dashboard/showposts&quot;;
    }

答案1

得分: 2

你可以在不创建双向关系的情况下完成这个操作,但这样做有一个需要注意的地方,稍后我会谈到,让我们看看如何在单向关系中实现。

首先,你需要在实体关系上指定orphanRemoval=true

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

假设你的评论类中已经实现了equalshashCode方法,

然后你只需要根据评论的id加载评论实体,然后调用:

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

这将从表中删除评论,而其他评论将保持不变。

另一种方法是遍历评论列表,查找匹配的评论对象(将该方法添加到事务中)。

@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;
    }
}

注意:

1. post.getComments() // 会从数据库加载所有评论
2. comments.remove(comment) // 会触发额外的INSERT语句

为什么会触发额外的INSERT语句?

当你使用单向映射时,JPA提供程序(如Hibernate)会创建额外的连接表,实际上会在后台创建@ManyToMany关系。

因此,它首先会从连接表中删除所有与关联的post_id相关的条目,然后再将所有记录重新插入连接表,只是留下了我们删除的记录。

然后它会从评论表中删除该条目,所以在使用单向关系时,你需要承担性能损耗。

英文:

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&lt;Comment&gt; 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&lt;Comment&gt; comments = post.getComments();
       Comment comment = comments.stream().filter(c -&gt; c.getId().equals(commentId)).findAny()
     .orElseThrow(() -&gt; new IllegalArgumentException(&quot;Invalid comment id&quot;));
      
      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.

答案2

得分: 0

根据错误消息,我假设除了 postcomments 表之外,您还有一个名为 post_comments 的连接表。

如果是这种情况,Hibernate 不知道它。您应该在 Post 类的 comments 字段中不仅使用 @OneToMany,还应该使用 @JoinTable 注解。

英文:

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.

huangapple
  • 本文由 发表于 2020年9月20日 01:32:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/63971573.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定