Spring Data Repository不会删除ManyToOne Entity

时间:2015-03-20 17:18:47

标签: java spring hibernate spring-data-jpa

我目前正在尝试使用Spring Data存储库来删除我的一些实体。删除调用在没有任何异常/错误消息的情况下工作,但之后不会删除该实体。

这些是我的实体:

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:

我找到了解决方法,但我仍然不明白真正的问题是什么。 如果我像这样删除帖子,它会“有效”(由于约束违规而有一些例外,但邮件仍被删除):

post.setBoard(null);
postRepo.delete(post);

编辑2:

当我查看执行的SQL语句时,我可以看到hibernate甚至没有尝试删除。唯一发生的是那两个选择语句:

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似乎是个问题。没有它,删除工作正常(但我现在错过了对帖子的级联更改)

6 个答案:

答案 0 :(得分:66)

问题似乎是您使用cascade=CascadeType.ALL,其中还包含CascadeType.PERSISTCascadeType.PERSIST表示子实体完全由父实体管理,您无法直接删除它。要删除,只需将其从父目录中删除即可。

您可以添加其他CascadeTypes而不是全部。例如CascadeType.REMOVE,如果你想要的唯一一件事就是在父母被移除的情况下移除孩子。

答案 1 :(得分:3)

基于上面user2936091的优秀答案,我只想提一下我今天偶然发现的(相关)解决方法:如果未将父实体提取到Hibernate上下文中,则可以直接删除它。

在我的情况下,这是通过在fetch = FetchType.LAZY关系上设置@ManyToOne来实现的。出于性能原因,我想要进行此更改,并注意到如果没有父母急切地获取Hibernate,可以通过存储库方法调用将其删除。

答案 2 :(得分:0)

这是因为你在Post类上设置了 mappedBy =“board”,这样你就知道Post的主人是董事会。

答案 3 :(得分:0)

一个简单的替代方法:

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

}

答案 4 :(得分:0)

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;

答案 5 :(得分:0)

@ user2936091所说的是正确的,但不能解决问题。我也有和你一样的问题。

解决方案是从父类中删除fetch=FetchType.EAGER。使用Wen提取类型EAGER,post对象中board的实例将具有

 private List<Post> posts = new ArrayList<Post>();

填充是因为它已被急切加载,这意味着它仍然具有对自身的引用,例如@ user2936091提及。

我仍然急切地加载相关属性的解决方法是创建一个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
    }
}