删除含多关联关系的AppUser实体时触发Hibernate PropertyValueException问题求助
看起来你遇到的问题主要来自两个核心点:实体映射的配置错误,以及级联操作的不合理设置。让我们一步步拆解并修复:
1. 最关键的映射错误:AppUser的@OneToMany关联字段写错了
在你的AppUser实体中,@OneToMany(mappedBy = "book")这行明显是错误的——mappedBy应该指向BookCard中关联AppUser的字段名,也就是user,而不是book。这个错误直接导致Hibernate无法正确识别用户和借书卡的关联关系,进而引发后续的属性值异常。
修正后的AppUser的bookCards字段:
@EqualsAndHashCode.Exclude @OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE, orphanRemoval = true) private Set<BookCard> bookCards = new HashSet<>();
这里我们添加了cascade = CascadeType.REMOVE和orphanRemoval = true,这样当删除AppUser时,Hibernate会自动删除其关联的所有BookCard,不需要手动调用bookCardService的删除方法。
2. 调整BookCard实体的级联和关联配置
你的BookCard实体中存在两个需要修正的点:
- user字段的级联设置:
cascade = CascadeType.ALL过于宽泛,我们不需要从BookCard级联操作AppUser,所以应该去掉这个级联配置(如果需要保存BookCard时自动关联用户,可保留CascadeType.PERSIST,但通常用户是提前创建好的)。同时保留@OnDelete(action = OnDeleteAction.CASCADE)来确保数据库层面的级联删除,形成双重保障。 - book字段的级联设置:
cascade = CascadeType.ALL是绝对不能留的!这会导致删除BookCard时,Hibernate试图级联删除或修改关联的Book实体,而你的Book字段是optional = false(非空),这就触发了你看到的PropertyValueException。我们要保留Book,所以这里要移除级联配置,@OnDelete(action = OnDeleteAction.NO_ACTION)的设置是正确的,确保数据库不会改动Book。
修正后的BookCard相关字段:
@ManyToOne(fetch = FetchType.EAGER) @OnDelete(action = OnDeleteAction.CASCADE) @JoinColumn(name = "user_id", nullable = false) private AppUser user; @ManyToOne(fetch = FetchType.EAGER, optional = false) @OnDelete(action = OnDeleteAction.NO_ACTION) @JoinColumn(name = "book_id") private Book book;
3. 简化你的删除方法
当实体配置正确后,删除用户的方法可以大幅简化,不需要手动处理BookCard的删除,Hibernate会自动帮你完成:
@Transactional public void deleteUser(Long id) { userRepository.deleteById(id); }
为什么之前的方法报错?
你之前手动删除BookCard时,因为BookCard的book字段配置了cascade = CascadeType.ALL,Hibernate会尝试对Book执行级联操作,但你又明确不想修改Book,加上AppUser的映射错误导致关联关系混乱,最终触发了非空属性的异常。
另外,Book实体中的Set<BookCard> bookCards配置是没问题的,但如果不需要从Book端频繁操作BookCard,也可以把fetch = FetchType.EAGER改成LAZY来优化查询性能。
内容的提问来源于stack exchange,提问作者Oleksandr Ovcharenko

