You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JPA删除Post方法执行但未删除数据库对象问题求助

问题描述

我有一个User类,和Comment、Post是一对多关系:每个用户可拥有多个帖子和评论,每个帖子也可拥有多个评论。现在执行Post删除方法时,方法正常运行但数据库中对象未被删除。奇怪的是,若不从请求头获取User而是传入new User(),删除就能正常工作,求解决。

User实体类

@Entity
@Getter @Setter
public class User {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long userId;
    private String email;

    @OneToMany (mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Post> posts;

    @OneToMany (mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Comment> comments;
}

Post实体类

@Entity
@Getter @Setter
public class Post {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long postId;

    @OneToMany (mappedBy = "post", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Comment> comments;

    @ManyToOne(fetch = FetchType.LAZY)
    @OnDelete(action = OnDeleteAction.CASCADE)
    @JsonIgnore
    @JoinColumn (name = "user_id")
    private User user;
}

Post控制器类

@RestController
@RequestMapping("/api/posts")
@AllArgsConstructor
public class PostController {

    private final PostService postService;
    private final UserService userService;

    @DeleteMapping("/{postId}")
    public ResponseEntity<?> deletePostById (@PathVariable Long postId, @RequestHeader("Authorization") String authorization) {

        Post post = postService.getPostById(postId).getData();
        String email = JwtUtils.getEmailFromHeader(authorization);
        User user= userService.getUserByEmail(email);
        Result result = postService.deletePost(post, user);
        return new ResponseEntity<>(result, HttpStatus.OK);        
    }
}

Post服务类中的删除方法

public Result deletePost(Post post, User user) {

        try {
            if (post == null) {
                return new Result(false, POST_NOT_FOUND);
            }
            else if (user.getUserId().equals(post.getUser().getUserId())) {

                postRepository.delete(post);
                return new Result(true, POST_DELETED);
            }
            else {
                return new Result(false, PERMISSION_DENIED);
            }
        }
        catch (Exception e) {
            return new Result(false, e.getMessage());
        }
    }

问题原因及解决方案

核心问题:实体持久化状态冲突

从请求头获取的User是Spring Data JPA从数据库加载的持久化状态实例(受EntityManager管理),而new User()是未被管理的瞬时状态。当你执行post.getUser().getUserId()时,因为Post的user配置了懒加载,这里会触发EntityManager加载关联的User实例,此时对比两个User的ID后,执行postRepository.delete(post)时,EntityManager的状态同步出现异常,导致删除操作未同步到数据库。

具体修复方案

  1. 给删除方法加上事务注解
    Spring Data JPA的delete操作必须在事务上下文才能生效,给deletePost方法加上@Transactional注解:

    @Transactional
    public Result deletePost(Post post, User user) {
        // 原有逻辑不变
    }
    
  2. 优化权限校验逻辑,避免不必要的懒加载
    不要通过post.getUser().getUserId()触发懒加载,而是在查询Post时就关联加载User,或者直接通过Post的外键字段校验:

    • 方法一:修改getPostById方法,用Fetch Join加载User
      在PostRepository中添加查询方法:

      @Query("SELECT p FROM Post p JOIN FETCH p.user WHERE p.postId = :postId")
      Post findByIdWithUser(@Param("postId") Long postId);
      

      然后在服务类中用这个方法获取Post,这样Post的User已经被加载,不会触发懒加载,实体状态一致。

    • 方法二:直接通过ID组合删除,跳过实体加载
      在PostRepository中添加批量删除方法:

      @Modifying
      @Transactional
      @Query("DELETE FROM Post p WHERE p.postId = :postId AND p.user.userId = :userId")
      int deleteByPostIdAndUserId(@Param("postId") Long postId, @Param("userId") Long userId);
      

      然后修改服务类的删除逻辑:

      public Result deletePost(Long postId, User user) {
          try {
              int deletedCount = postRepository.deleteByPostIdAndUserId(postId, user.getUserId());
              if (deletedCount == 0) {
                  return new Result(false, "帖子不存在或无权限删除");
              }
              return new Result(true, POST_DELETED);
          } catch (Exception e) {
              return new Result(false, e.getMessage());
          }
      }
      

      这种方式不需要加载整个Post和User实体,性能更高,也避免了实体状态问题。

  3. 清理重复的级联配置
    去掉Post类中的@OnDelete(action = OnDeleteAction.CASCADE),因为User的@OneToMany已经配置了cascade = CascadeType.ALL和orphanRemoval = true,重复配置可能导致冲突。

内容的提问来源于stack exchange,提问作者Adem Galitekin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 08:32:54