How to delete an ManyToMany related object when one part is empty?
Here is the BlogPost model :
@Entity
@Table(name = "blog_posts")
public class BlogPost extends Model {
@Required
@MaxSize(50)
@MinSize(3)
@Column(length=50)
public String title;
@Lob
@Required
@MaxSize(10000)
public String description;
@Temporal(TemporalType.TIMESTAMP)
public Date created;
@ManyToMany(targetEntity=PostTag.class, cascade=CascadeType.DETACH)
@OrderBy("name ASC")
public List<PostTag> tags;
// + other fields
}
And the PostTag :
@Entity
@Table(name = "post_tags")
public class PostTag extends Model {
@Match("^([a-zA-Z0-9\\-]{3,25})$")
@MinSize(3)
@Max开发者_JAVA百科Size(25)
@Required
@Column(length=25, unique=true)
public String name;
@ManyToMany(targetEntity=BlogPost.class, mappedBy="tags", cascade=CascadeType.REMOVE)
public List<BlogPost> posts;
}
The relation works fine, but what I'd like, is when I remove a tag from my post, if no other post use this tag, then I can remove it definitely from my database.
Here's the code I tried, but generate an Hibernate exception :
public boolean removeTag(PostTag tag) {
if (!tags.contains(tag)) {
return false;
}
tags.remove(tag);
save();
if (tag.posts == null || tag.posts.isEmpty()) {
tag.delete();
}
return true;
}
The exact exception is:
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key constraint fails
I tried to change CascadeType to DELETE_ORPHANS, but it's deprecated, and the new version only seems to works for OneToOne and OneToMany relationships only :/
How can I do ?
Thanks for your help!
Ok I've found where the problem was, so I'll explain it here in case someone have the same issue.
The exception was because I was trying to delete a Tag that still had relationship (it was not left alone).
Here is a working solution on how to delete Tags when there is no Posts using it anymore:
public boolean removeTag(PostTag tag) {
if (!tags.contains(tag)) {
return false;
}
tags.remove(tag);
save();
if (PostTag.find("name = ? AND size(posts) = 0", tag.name).first() != null) {
tag.delete();
}
return true;
}
Since you are using a bidirectional relation you should update the "backlink"
public boolean removeTag(PostTag tag) {
if (!tags.contains(tag)) {
return false;
}
tags.remove(tag); // remove post -> tag link
tag.posts.remove(this); // added: remove tag -> post link
save();
if (tag.posts == null || tag.posts.isEmpty()) {
tag.delete();
}
return true;
}
精彩评论