Java Spring Data Repository 不会删除 ManyToOne 实体
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29172313/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring Data Repository does not delete ManyToOne Entity
提问by Nitek
I'm am currently trying to use a Spring Data repository to delete some of my entities. The delete call works without any exceptions/error messages, but the entity is not deleted afterwards.
我目前正在尝试使用 Spring Data 存储库来删除我的一些实体。删除调用没有任何异常/错误消息,但之后不会删除实体。
Those are my entities:
这些是我的实体:
public class Board implements Serializable {
@Id
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Column(columnDefinition = "BINARY(16)")
private UUID uuid;
@OneToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL, orphanRemoval = true, mappedBy = "board")
private List<Post> posts = new ArrayList<Post>();
}
and
和
public class Post implements Serializable {
@Id
@GeneratedValue
private long id;
@ManyToOne(optional = false)
@JoinColumn(name="board_uuid", updatable = false, nullable = false)
@JsonBackReference
private Board board;
}
The repository is as simple as it can be:
存储库非常简单:
@Repository
public interface PostRepository extends CrudRepository<Post, Long> {
}
The delete call is something like
删除调用类似于
postRepository.delete(50);
Any ideas why this change doesn't reflect in the database?
任何想法为什么此更改未反映在数据库中?
Edit 1:
编辑1:
I found a workaround, but I still don't understand what the real problem is. It "works" if I delete the Post like this (there are a couple of exceptions because of constraint violations, but still the Post gets deleted):
我找到了一个解决方法,但我仍然不明白真正的问题是什么。如果我像这样删除帖子,它“有效”(由于违反约束,有几个例外,但帖子仍然被删除):
post.setBoard(null);
postRepo.delete(post);
Edit 2:
编辑2:
When I have a look at the SQL statements executed I can see that hibernate is not even trying to delete. The only thing that happens are those two select statements:
当我查看执行的 SQL 语句时,我可以看到 hibernate 甚至没有尝试删除。唯一发生的是这两个 select 语句:
Hibernate: select post0_.id as id1_1_0_, post0_.board_uuid as board_uu6_1_0_, post0_.content as content2_1_0_, post0_.x as x3_1_0_, post0_.y as y4_1_0_, post0_.z as z5_1_0_, board1_.uuid as uuid1_0_1_ from Post post0_ left outer join Board board1_ on post0_.board_uuid=board1_.uuid where post0_.id=?
Hibernate: select posts0_.board_uuid as board_uu6_0_0_, posts0_.id as id1_1_0_, posts0_.id as id1_1_1_, posts0_.board_uuid as board_uu6_1_1_, posts0_.content as content2_1_1_, posts0_.x as x3_1_1_, posts0_.y as y4_1_1_, posts0_.z as z5_1_1_ from Post posts0_ where posts0_.board_uuid=?
Edit 3
编辑 3
Turns out the cascade=CascadeType.ALL on posts seems to be the problem. Without it the delete works fine (but I am missing the cascade of changes to posts now)
原来帖子上的 cascade=CascadeType.ALL 似乎是问题所在。没有它,删除工作正常(但我现在错过了对帖子的一系列更改)
采纳答案by user2936091
The problem seems to be that you are using cascade=CascadeType.ALL
, which also includes CascadeType.PERSIST
. CascadeType.PERSIST
means that the child entity is completely managed by the parent and you cannot delete it directly. In order to delete you just need to remove it from the parent.
问题似乎是您正在使用cascade=CascadeType.ALL
,其中还包括CascadeType.PERSIST
. CascadeType.PERSIST
意味着子实体完全由父实体管理,您不能直接删除它。为了删除,您只需将其从父项中删除。
You could just add the other CascadeTypes
instead of all. e.g CascadeType.REMOVE
, if the only thing you would want is to remove the child if the parent is removed.
你可以只添加另一个CascadeTypes
而不是全部。例如CascadeType.REMOVE
,如果您唯一想要的是在删除父项时删除子项。
回答by Vyncent
This is because you set mappedBy = "board"on Post class, doing so you tell that the master of Post is Board.
这是因为你在 Post 类上设置了mappingBy = "board",这样做你就知道 Post 的主人是 Board。
回答by chut
Building on the excellent answer from user2936091 above, I just wanted to mention a (related) workaround I stumbled upon today: if the parent entity is not fetched into the Hibernate context, you are able to delete it directly.
基于上面来自 user2936091 的出色回答,我只想提一下我今天偶然发现的一个(相关)解决方法:如果父实体没有被提取到 Hibernate 上下文中,你可以直接删除它。
In my case this was achieved by setting fetch = FetchType.LAZY
on the @ManyToOne
relationship. I wanted this change for performance reasons anyway, and noticed that without the parent eagerly fetched Hibernate was free to delete it via the repository method call.
在我的情况下,这是通过设置实现fetch = FetchType.LAZY
的@ManyToOne
关系。无论如何,出于性能原因,我希望进行此更改,并注意到没有父级急切地获取的 Hibernate 可以通过存储库方法调用自由地删除它。
回答by Michel Fortes
A simple alternative:
一个简单的替代方案:
@Repository
public interface PostRepository extends CrudRepository<Post, Long> {
@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query(value = "DELETE FROM post WHERE id = ?1", nativeQuery = true)
void deleteById(long postId);
}
回答by Shubham
cascade=CascadeType.PERSISTis used for updating insert in both side but for delete it will restrict.
级联 = CascadeType.PERSIST用于更新两侧的插入但删除它会限制。
@ManyToOne(cascade=CascadeType.PERSIST, fetch = FetchType.LAZY)
// @JoinColumn(name = "qid")
@JoinColumn(name = "qid", referencedColumnName = "qid", foreignKey = @ForeignKey(name = "qid"), nullable = false)
// @JsonIgnore
@JsonBackReference
private QueueGroup queueGroup;
回答by user2049132
What @user2936091 said is true but it doesn't solve the problem. I also had the exact same problem like you.
@user2936091 说的是对的,但并不能解决问题。我也有和你一样的问题。
The solution is to remove fetch=FetchType.EAGER
from the parent class. Wen fetch type EAGER is used, the instance of board in the post object will have the
解决方案是fetch=FetchType.EAGER
从父类中删除。使用wen fetch类型EAGER,post对象中的board实例会有
private List<Post> posts = new ArrayList<Post>();
populated because it was eagerly loaded, which means it still has a reference to itself, like @user2936091 mentions.
填充是因为它被急切加载,这意味着它仍然有一个对自身的引用,就像 @user2936091 提到的那样。
My workaround to still load related properties eagerly was to create a DBService class and load the objects from there like this
我仍然急切地加载相关属性的解决方法是创建一个 DBService 类并像这样从那里加载对象
@Service
public class DBService {
BoardRepository boardRepo;
@Transactional
public Board getBoardProcess(Long id) {
Board board = boardRepo.findById(id).get();
board.getPosts().size(); // This will trigger the db query so that posts gets loaded
// It needs to be done within a transactional so that the session is still the same to populate the fields. If it's done from outside of this method. It will not work
}
}