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

Spring Boot中JpaRepository delete/deleteById执行无效问题排查

排查JpaRepository deleteById()执行无效问题

问题描述

删除Archivo记录时,调用iArchivosDao.deleteById(archId)无任何操作(无日志输出),但注释掉validarArchivoDeUsuario()方法内的循环、硬编码返回true时,delete操作可正常执行。验证方法本身功能正常,能正确判断文件归属。

核心代码

删除方法

@Override
public RespuestaVo delete(Integer archId) {
    RespuestaVo respuestaVo = new RespuestaVo();

    try {
       if (!validarArchivoDeUsuario(archId)){
            respuestaVo = new RespuestaVo(401, HttpStatus.UNAUTHORIZED, "Acceso a recursos no autorizado.");
            return respuestaVo;
        }
        respuestaVo = new RespuestaVo(201, HttpStatus.OK, "El archivo ha sido eliminado con éxito.");
        iArchivosDao.deleteById(archId);

    } catch (Exception ex) {
        respuestaVo = new RespuestaVo(400, HttpStatus.BAD_REQUEST, "No ha sido posible eliminar el archivo, inténtelo más tarde.");
        Logger.getLogger(LogosEmprendimientosServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
    }
    return respuestaVo;
}

验证方法(正常逻辑)

private boolean validarArchivoDeUsuario(int archId) {
    Personas persona = jwtProvider.getUserID();
    List<Emprendimientos> emprendimientos = iEmprendimientosDao.findByPersIdAndEmprEstado(persona, ACTIVO);
    
    for (Emprendimientos emprendimiento: emprendimientos){
        for (Archivos archivo: emprendimiento.getArchivosCollection()){
            if (archivo.getArchId().equals(Integer.valueOf(archId))){
                return true;
            }
        }
    }
    return false;
}

实体映射关系

Emprendimientos.java

@OneToMany(cascade = CascadeType.ALL, mappedBy = "emprId")
private Collection<Archivos> archivosCollection;

Archivos.java

@Entity
@Table(name = "ARCHIVOS")
@Data
public class Archivos {
    // ...其他字段

    @JsonBackReference("emprId-Archivo")
    @JoinColumn(name = "EMPR_ID", referencedColumnName = "EMPR_ID")
    @ManyToOne(optional = false)
    private Emprendimientos emprId;
}

排查思路与解决方案

原因分析

问题根源在于持久化上下文的缓存机制:

  1. 验证方法中,通过emprendimiento.getArchivosCollection()获取到目标Archivo实体,该实体被加入当前Hibernate持久化上下文(一级缓存)。
  2. Emprendimientos的@OneToMany关联设置了cascade = CascadeType.ALL,持久化上下文会认为该实体仍被关联对象引用,导致deleteById()的标记删除操作被覆盖或忽略。
  3. 注释循环后,验证方法未加载目标Archivo到缓存,deleteById()直接执行数据库操作,因此生效。

解决步骤

方案1:调整级联操作类型

将Emprendimientos中的@OneToMany级联类型从CascadeType.ALL改为仅包含必要的操作(如PERSIST、MERGE),避免级联规则干扰删除:

@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "emprId")
private Collection<Archivos> archivosCollection;

方案2:手动移除关联关系

在验证通过后,先从关联集合中移除目标Archivo,再执行删除:

@Override
public RespuestaVo delete(Integer archId) {
    RespuestaVo respuestaVo = new RespuestaVo();

    try {
       Personas persona = jwtProvider.getUserID();
       List<Emprendimientos> emprendimientos = iEmprendimientosDao.findByPersIdAndEmprEstado(persona, ACTIVO);
       Archivos targetArchivo = null;
       Emprendimientos targetEmprendimiento = null;

       // 定位目标文件及所属实体
       for (Emprendimientos emprendimiento: emprendimientos){
           for (Archivos archivo: emprendimiento.getArchivosCollection()){
               if (archivo.getArchId().equals(archId)){
                   targetArchivo = archivo;
                   targetEmprendimiento = emprendimiento;
                   break;
               }
           }
           if (targetArchivo != null) break;
       }

       if (targetArchivo == null){
            respuestaVo = new RespuestaVo(401, HttpStatus.UNAUTHORIZED, "Acceso a recursos no autorizado.");
            return respuestaVo;
        }

        // 移除关联并更新
        targetEmprendimiento.getArchivosCollection().remove(targetArchivo);
        iEmprendimientosDao.save(targetEmprendimiento);

        // 执行删除
        iArchivosDao.delete(targetArchivo);
        respuestaVo = new RespuestaVo(201, HttpStatus.OK, "El archivo ha sido eliminado con éxito.");

    } catch (Exception ex) {
        respuestaVo = new RespuestaVo(400, HttpStatus.BAD_REQUEST, "No ha sido posible eliminar el archivo, inténtelo más tarde.");
        Logger.getLogger(LogosEmprendimientosServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
    }
    return respuestaVo;
}

方案3:清空持久化上下文缓存

在执行deleteById()前,调用EntityManager.clear()清空一级缓存,强制JPA直接访问数据库:

@PersistenceContext
private EntityManager entityManager;

@Override
public RespuestaVo delete(Integer archId) {
    RespuestaVo respuestaVo = new RespuestaVo();

    try {
       if (!validarArchivoDeUsuario(archId)){
            respuestaVo = new RespuestaVo(401, HttpStatus.UNAUTHORIZED, "Acceso a recursos no autorizado.");
            return respuestaVo;
        }
        // 清空缓存
        entityManager.clear();
        respuestaVo = new RespuestaVo(201, HttpStatus.OK, "El archivo ha sido eliminado con éxito.");
        iArchivosDao.deleteById(archId);

    } catch (Exception ex) {
        respuestaVo = new RespuestaVo(400, HttpStatus.BAD_REQUEST, "No ha sido posible eliminar el archivo, inténtelo más tarde.");
        Logger.getLogger(LogosEmprendimientosServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
    }
    return respuestaVo;
}

验证建议

  • 开启Hibernate SQL日志,观察deleteById()执行时是否有DELETE语句输出。
  • 确认删除方法被@Transactional注解标记,确保操作在事务内提交。

内容的提问来源于stack exchange,提问作者Diego Izquierdo Dussan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 07:24:57