Java Spring用户注册时user表插入两条记录而非一条问题排查
问题根因
返回值username为null
你的Login和User是双向关联实体,且做了属性同步逻辑:当调用Login.setUsername(null)时,会同步修改关联User对象的username属性为null。你的代码里先后在两处执行了Login.setUsername(null)操作,直接把传入的User对象的username改成了null,最终构造返回值时自然拿到null。
生成两条User记录
Login实体对关联的User属性配置了CascadeType.PERSIST或CascadeType.ALL级联持久化规则:
- 你在
registerNewUser中手动调用userRepository.save(user)生成了第一条ID=1的符合预期的记录 - 因为
confirmToken方法加了@Transactional注解,方法执行结束后持久化上下文会自动flush,此时托管状态的Login对象关联的是临时状态的User对象,触发级联保存生成了第二条ID=2的多余记录,且此时username已经被改为null,所以第二条记录的username字段为null。
修复方案
- 移除冗余的
Login.setUsername操作,删掉上层confirmToken方法中的confirmationToken.getLogin().setUsername(null);这行代码,只在registerNewUser中修改一次即可 - 调整
registerNewUser的执行顺序,先保存用户,再修改Login属性,同时把Login关联的User设置为已经持久化的对象,避免触发级联保存:
public RegistrationConfirmResponse registerNewUser(User user, String password) { // 先查对应的login记录 Login login = loginRepository.findByUsername(user.getUsername()).get(); // 保存用户拿到持久化状态的对象 User savedUser = userRepository.save(user); passwordRepository.save(new Password(savedUser.getUserId(), password)); // 给login设置已持久化的user对象,避免级联生成新记录 login.setUser(savedUser); // 最后修改login的username,避免提前修改导致查询失败 login.setUsername(null); // 用已保存的user的username构造返回值 return new RegistrationConfirmResponse("done", savedUser.getUsername()); }
- 如果不需要
Login级联保存User,可以直接移除Login实体中User关联的CascadeType.PERSIST/CascadeType.ALL配置,从根源避免多余记录生成。
内容的提问来源于stack exchange,提问作者Johannes Losch
相关产品推荐
相关产品推荐

