Spring Data Repository 不删除 ManyToOne 实体
Spring Data Repository does not delete ManyToOne Entity
我目前正在尝试使用 Spring 数据存储库来删除我的一些实体。删除调用在没有任何 exceptions/error 消息的情况下工作,但之后不会删除该实体。
这些是我的实体:
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>();
}
和
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;
}
存储库尽可能简单:
@Repository
public interface PostRepository extends CrudRepository<Post, Long> {
}
删除调用类似于
postRepository.delete(50);
知道为什么这个更改没有反映在数据库中吗?
编辑 1:
我找到了解决方法,但我仍然不明白真正的问题是什么。
它 "works" 如果我像这样删除 Post (由于违反约束有几个例外,但 Post 仍然被删除):
post.setBoard(null);
postRepo.delete(post);
编辑 2:
当我查看执行的 SQL 语句时,我可以看到休眠甚至没有尝试删除。唯一发生的事情是那两个 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=?
编辑 3
原来帖子上的 cascade=CascadeType.ALL 似乎是问题所在。没有它,删除工作正常(但我现在错过了对帖子的级联更改)
这是因为你在Postclass上设置了mappedBy = "board",这样做你告诉[=16的主人=] 是董事会。
问题似乎是您正在使用 cascade=CascadeType.ALL
,其中还包括 CascadeType.PERSIST
。 CascadeType.PERSIST
表示子实体完全由父实体管理,不能直接删除。为了删除你只需要从父级中删除它。
您可以只添加另一个 CascadeTypes
而不是全部。例如 CascadeType.REMOVE
,如果您唯一想要的是在删除父项时删除子项。
基于上面 user2936091 的出色回答,我只想提一下我今天偶然发现的一个(相关的)解决方法:如果父实体没有被提取到 Hibernate 上下文中,您可以直接将其删除。
在我的例子中,这是通过在 @ManyToOne
关系上设置 fetch = FetchType.LAZY
来实现的。无论如何,出于性能原因,我想要进行此更改,并且注意到如果没有父级急切获取的 Hibernate 可以通过存储库方法调用自由删除它。
一个简单的选择:
@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);
}
cascade=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;
@user2936091 说的对,但是没有解决问题。我也有和你一样的问题。
解决方案是从父 class 中删除 fetch=FetchType.EAGER
。如果使用 EAGER 获取类型,post 对象中的 board 实例将具有
private List<Post> posts = new ArrayList<Post>();
填充是因为它被急切地加载了,这意味着它仍然有对自身的引用,就像@user2936091 提到的那样。
我仍然热切地加载相关属性的解决方法是创建一个 DBService class 并像这样从那里加载对象
@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
}
}
我目前正在尝试使用 Spring 数据存储库来删除我的一些实体。删除调用在没有任何 exceptions/error 消息的情况下工作,但之后不会删除该实体。
这些是我的实体:
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>();
}
和
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;
}
存储库尽可能简单:
@Repository
public interface PostRepository extends CrudRepository<Post, Long> {
}
删除调用类似于
postRepository.delete(50);
知道为什么这个更改没有反映在数据库中吗?
编辑 1:
我找到了解决方法,但我仍然不明白真正的问题是什么。 它 "works" 如果我像这样删除 Post (由于违反约束有几个例外,但 Post 仍然被删除):
post.setBoard(null);
postRepo.delete(post);
编辑 2:
当我查看执行的 SQL 语句时,我可以看到休眠甚至没有尝试删除。唯一发生的事情是那两个 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=?
编辑 3
原来帖子上的 cascade=CascadeType.ALL 似乎是问题所在。没有它,删除工作正常(但我现在错过了对帖子的级联更改)
这是因为你在Postclass上设置了mappedBy = "board",这样做你告诉[=16的主人=] 是董事会。
问题似乎是您正在使用 cascade=CascadeType.ALL
,其中还包括 CascadeType.PERSIST
。 CascadeType.PERSIST
表示子实体完全由父实体管理,不能直接删除。为了删除你只需要从父级中删除它。
您可以只添加另一个 CascadeTypes
而不是全部。例如 CascadeType.REMOVE
,如果您唯一想要的是在删除父项时删除子项。
基于上面 user2936091 的出色回答,我只想提一下我今天偶然发现的一个(相关的)解决方法:如果父实体没有被提取到 Hibernate 上下文中,您可以直接将其删除。
在我的例子中,这是通过在 @ManyToOne
关系上设置 fetch = FetchType.LAZY
来实现的。无论如何,出于性能原因,我想要进行此更改,并且注意到如果没有父级急切获取的 Hibernate 可以通过存储库方法调用自由删除它。
一个简单的选择:
@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);
}
cascade=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;
@user2936091 说的对,但是没有解决问题。我也有和你一样的问题。
解决方案是从父 class 中删除 fetch=FetchType.EAGER
。如果使用 EAGER 获取类型,post 对象中的 board 实例将具有
private List<Post> posts = new ArrayList<Post>();
填充是因为它被急切地加载了,这意味着它仍然有对自身的引用,就像@user2936091 提到的那样。
我仍然热切地加载相关属性的解决方法是创建一个 DBService class 并像这样从那里加载对象
@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
}
}