JPA移除@GeneratedValue后OneToMany关联返回数据异常问题求助
问题根因
这个问题是Spring Data JPA的实体状态判断逻辑和配置的级联类型不匹配导致的:
- Spring Data JPA的
save方法默认判断实体是否为新实例的规则:对于没有实现Persistable接口的实体,会直接检查主键字段是否为null:主键为null判定为新实体,调用EntityManager.persist();主键非null判定为已有实体,调用EntityManager.merge()。 - 你去掉
@GeneratedValue注解之后,提前手动给Post设置主键值再调用save,Spring Data检测到Post主键非空,就会走merge流程。但你在@OneToMany上配置的级联类型只有CascadeType.PERSIST,只有执行persist操作时才会级联处理关联的Comment,merge操作不会触发这个级联,因此Comment没有被正确纳入持久化上下文处理。 - 用
saveAndFlush时返回的Comment只有ID有值,是因为flush操作强制把SQL刷到数据库,Comment主键是自增的,数据库生成ID后会被回写到实体,但其他字段因为没有被级联持久化,所以都是默认的null值。 - 加回
@GeneratedValue之后恢复正常,是因为此时你实体主键配置了insertable=false,手动设置的ID不会被真正写入数据库,Hibernate会识别实体为瞬时态,调用persist方法,触发CascadeType.PERSIST级联保存所有Comment字段。
解决方案
你可以任选以下一种方案修复:
- 调整关联注解的级联配置,新增
CascadeType.MERGE,或者直接使用CascadeType.ALL,这样merge操作也能级联处理关联的Comment:@OneToMany(mappedBy = "post", fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) Set<Comment> comments = new HashSet(); - 让Post实体实现
Persistable<Long>接口,自定义新实体判断逻辑,避免因为手动设置主键就被判定为已有实体:@Entity class Post implements Persistable<Long> { @Id @Column(name = "id", updatable = false) private Long id; @Transient private boolean isNew = true; @Override public boolean isNew() { return isNew; } @PrePersist @PostLoad void markNotNew() { this.isNew = false; } // 其余字段和方法不变 } - 直接调用
EntityManager.persist(post)替代postRepository.save(post),强制走persist流程触发已配置的级联规则。
内容的提问来源于stack exchange,提问作者VeryNiceArgumentException
相关产品推荐
相关产品推荐

