Spring Boot JPA保存UserMeta时重复创建User实体问题
问题原因
你遇到的重复插入User记录的问题,根本不需要移除外键配置,核心原因有两个:
- 级联关系配置反了:你在子表
UserMeta的@OneToOne注解上配置了cascade = CascadeType.ALL,意味着所有对UserMeta的持久化操作,都会自动级联操作关联的User对象。正常情况下级联持久化应该配置在父表到子表的方向,子表持有父表引用时不应该配置全级联。 - 传入的User是游离态对象:你分步调用接口创建UserMeta时,请求体里携带的User对象是Jackson反序列化生成的游离对象,不在JPA当前的持久化上下文中。JPA保存UserMeta时检查到关联的User不是托管状态,又配置了级联持久化,就会把这个游离User当成新实体执行插入,最终生成重复的User记录。
解决方案
按以下两步修改即可,不需要删除外键约束:
- 修正级联配置
去掉UserMeta实体中@OneToOne上的级联配置,子表不需要级联操作父表记录:
// UserMeta类中的user字段修改后 @OneToOne(fetch = FetchType.LAZY, optional = false) @JoinColumn(name = "user_Id", referencedColumnName="userId") private User user;
如果后续有同时保存User和关联UserMeta的需求,可以把级联配置在父表User侧:
// User类中的userMeta字段可选配置 @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) private UserMeta userMeta;
- 保存UserMeta前关联托管状态的User对象
不要直接把请求反序列化得到的游离User对象关联到UserMeta上,先通过User的主键ID获取JPA托管的User实例,再做关联。修改UserMetaServiceImpl,先注入UserRepository,再调整保存逻辑:
@Service public class UserMetaServiceImpl implements UserMetaService{ private UserMetaRepository userMetaRepo; // 注入UserRepository private UserRepository userRepository; public UserMetaServiceImpl(UserMetaRepository userMetaRepo, UserRepository userRepository) { this.userMetaRepo = userMetaRepo; this.userRepository = userRepository; } @Override public UserMeta saveUserMeta(UserMeta userMeta) { // 用getReferenceById获取代理对象,不需要查询全量字段,性能更好 User existUser = userRepository.getReferenceById(userMeta.getUser().getUserId()); userMeta.setUser(existUser); return this.userMetaRepo.save(userMeta); } }
修改后再分步调用接口:先调用/user创建User记录,拿到返回的userId;再调用/userMeta接口传入UserMeta信息和对应的userId,就只会生成一条UserMeta记录,关联到已存在的User上,不会出现重复插入User的问题。
注意:不要删除数据库外键配置,外键是保证数据参照完整性的核心约束,删除后会提升脏数据产生的风险,完全没必要通过这种方式解决问题。
内容的提问来源于stack exchange,提问作者Filip_k_island
相关产品推荐
相关产品推荐

