JPA保存游离实体时关联异常:角色被删令牌保留
为什么更新AppUser时Roles关联被删除但Tokens保留?
核心原因:关联关系的所有者不同
JPA中关联关系的所有者决定了谁负责维护数据库中的关联数据(比如外键、中间表),这直接导致了两种关联的行为差异:
1. ManyToMany(Roles):AppUser是关联所有者
你的AppUser类里定义了@ManyToMany字段,但Role类没有对应的反向关联,所以JPA默认将AppUser认定为这个关联的所有者,负责维护用户与角色之间的中间表。
当你创建新的appUser对象且roles为null,调用save()时,JPA会判定你要清空该用户的所有角色关联,于是删除中间表中该用户与角色的关联记录。如果你的业务里角色是用户专属的,可能会误以为Role实体被删除,但实际只是关联关系被清除(因为你没给@ManyToMany加cascade=CascadeType.REMOVE,Role实体本身不会被删除)。
2. OneToMany(Tokens):Token是关联所有者
AppUser的tokens字段加了mappedBy="appUser",这个注解表示AppUser是关联的被维护方,真正的所有者是Token类里的@ManyToOne字段(负责维护tokens表中的app_user_id外键)。
JPA只会同步关联所有者的状态,当你保存的appUser的tokens为null时,因为AppUser不负责维护这个关联,JPA不会去修改tokens表的外键值,所以Token的外键依然指向原用户ID,Token数据自然保留。
解决办法
如果你不想丢失用户的角色关联,有两种常见处理方式:
- 直接修改从数据库查询到的持久化对象
savedCustomer,而非创建新对象:public void replace(AppUserPutRequestBody appUserPutRequestBody) { AppUser savedCustomer = findByIdOrThrowBadRequestException(appUserPutRequestBody.getId()); Optional<AppUser> appUserOptional = appUserRepository.findByEmail(appUserPutRequestBody.getEmail()); if (appUserOptional.isPresent() && !appUserOptional.get().getId().equals(savedCustomer.getId())) { throw new BadRequestException("email taken"); } // 直接修改持久化对象的字段,保留原有的roles和tokens savedCustomer.setEmail(appUserPutRequestBody.getEmail()); // 其他需要更新的字段同理 appUserRepository.save(savedCustomer); } - 若必须使用映射后的新对象,需将原用户的roles赋值给新对象:
AppUser appUser = appUserMapper.toAppUser(appUserPutRequestBody); appUser.setId(savedCustomer.getId()); appUser.setPassword(savedCustomer.getPassword()); appUser.setRoles(savedCustomer.getRoles()); // 保留原角色关联 appUserRepository.save(appUser);
内容的提问来源于stack exchange,提问作者Kauan Mocelin
相关产品推荐
相关产品推荐

