Spring JPA多对多带额外列保存成功更新报错如何处理
问题根因
- 每次调用save方法时都直接新建
Post、PostTag实例,没有查询数据库中已存在的对应记录 - 当相同postId+tagId的
PostTag已经在库中存在时,新创建的同主键PostTag对象被加入持久化上下文后,JPA判定为主键冲突,抛出EntityExistsException - 额外映射错误:
Post实体中@OneToMany的mappedBy属性值写错了,应该是小写的post(对应PostTag实体里的post字段),当前大写的Post会导致关联映射失效
解决方案
步骤1:修复实体类映射
修改Post实体的关联注解,可选添加orphanRemoval属性实现废弃关联自动删除:
@Entity public class Post { @Id private String postId; private String postName; // 修正mappedBy值,添加orphanRemoval @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) private Collection<PostTag> postTags = new HashSet<PostTag>(); }
步骤2:新增PostTag查询接口
新增PostTagRepository用于查询已存在的关联记录:
public interface PostTagRepository extends JpaRepository<PostTag, PostTagId> { Optional<PostTag> findByPostTagId(PostTagId id); }
步骤3:修正Service层逻辑
优先查询已存在的实体,不存在再新建,存在则直接更新字段:
@Autowired private PostRepository postRepository; @Autowired private TagRepository tagRepository; @Autowired private PostTagRepository postTagRepository; public Post save(Post post){ // 先查询库中已存在的Post,不存在再新建 Post targetPost = postRepository.findById(post.getPostId()) .orElseGet(() -> { Post newPost = new Post(); newPost.setPostId(post.getPostId()); return newPost; }); // 更新Post基础字段 targetPost.setPostName(post.getPostName()); // 如需删除请求中未传的关联,可先清空原有集合 // targetPost.getPostTags().clear(); for (PostTag requestPostTag : post.getPostTags()) { String tagId = requestPostTag.getTag().getTagId(); // 构造联合主键 PostTagId pk = new PostTagId(); pk.setPostId(post.getPostId()); pk.setTagId(tagId); // 查询已存在的关联记录,不存在再新建 PostTag targetPostTag = postTagRepository.findByPostTagId(pk) .orElseGet(PostTag::new); // 处理Tag实体 Tag dbTag = tagRepository.findById(tagId) .orElseGet(() -> { Tag newTag = new Tag(); newTag.setTagId(tagId); newTag.setTagName(requestPostTag.getTag().getTagName()); return tagRepository.save(newTag); }); // 赋值关联属性 targetPostTag.setPost(targetPost); targetPostTag.setTag(dbTag); // 此处更新PostTag的额外字段,示例: // targetPostTag.setSortNum(requestPostTag.getSortNum()); // targetPostTag.setCreateTime(requestPostTag.getCreateTime()); targetPost.getPostTags().add(targetPostTag); } return postRepository.save(targetPost); }
注意事项
- 确保
PostTagId类已经正确重写equals和hashCode方法,否则联合主键的查询、比较逻辑都会失效 - 如果你不需要显式注入
PostTagRepository,也可以直接遍历targetPost已有的postTags集合匹配主键,性能更高
内容的提问来源于stack exchange,提问作者Satscreate
相关产品推荐
相关产品推荐

