Spring Boot

SpringBoot

Tutorial SpringBoot: Borrar datos de base de datos

Aprende a eliminar datos en base de datos usando métodos delete en Spring Data JPA con buenas prácticas y manejo de asociaciones.

Aprende SpringBoot y certifícate

Métodos delete en repositorios

Spring Data JPA proporciona múltiples formas de eliminar entidades de la base de datos a través de los repositorios. Estas operaciones van desde métodos simples hasta consultas personalizadas que permiten borrar registros según criterios específicos.

Métodos básicos de eliminación

El repositorio base de Spring Data JPA incluye varios métodos predefinidos para eliminar entidades. El más directo es deleteById(), que elimina una entidad utilizando su identificador único:

@Service
public class ProductoService {
    
    @Autowired
    private ProductoRepository productoRepository;
    
    public void eliminarProducto(Long id) {
        productoRepository.deleteById(id);
    }
}

También puedes eliminar una entidad completa utilizando el método delete(), que recibe la instancia del objeto:

public void eliminarProductoCompleto(Producto producto) {
    productoRepository.delete(producto);
}

Para eliminar múltiples entidades de una vez, Spring Data JPA ofrece deleteAll() con diferentes variantes:

// Eliminar todas las entidades
public void eliminarTodosLosProductos() {
    productoRepository.deleteAll();
}

// Eliminar una colección específica de entidades
public void eliminarProductosSeleccionados(List<Producto> productos) {
    productoRepository.deleteAll(productos);
}

Métodos de consulta personalizados

Spring Data JPA permite crear métodos de eliminación personalizados utilizando la convención de nombres. Estos métodos siguen el patrón deleteBy seguido del nombre de la propiedad:

public interface ProductoRepository extends JpaRepository<Producto, Long> {
    
    // Eliminar por categoría
    void deleteByCategoriaId(Long categoriaId);
    
    // Eliminar por precio menor a un valor
    void deleteByPrecioLessThan(BigDecimal precio);
    
    // Eliminar por nombre que contenga una cadena
    void deleteByNombreContaining(String texto);
}

Para utilizar estos métodos personalizados, es importante marcarlos con la anotación @Transactional en el servicio:

@Service
@Transactional
public class ProductoService {
    
    @Autowired
    private ProductoRepository productoRepository;
    
    public void eliminarProductosPorCategoria(Long categoriaId) {
        productoRepository.deleteByCategoriaId(categoriaId);
    }
    
    public void eliminarProductosBaratos(BigDecimal precioMaximo) {
        productoRepository.deleteByPrecioLessThan(precioMaximo);
    }
}

Consultas JPQL para eliminación

Para casos más complejos, puedes utilizar consultas JPQL personalizadas con la anotación @Query y @Modifying:

public interface ProductoRepository extends JpaRepository<Producto, Long> {
    
    @Modifying
    @Query("DELETE FROM Producto p WHERE p.fechaCreacion < :fecha")
    int eliminarProductosAntiguos(@Param("fecha") LocalDateTime fecha);
    
    @Modifying
    @Query("DELETE FROM Producto p WHERE p.categoria.nombre = :nombreCategoria")
    int eliminarPorNombreCategoria(@Param("nombreCategoria") String nombreCategoria);
}

El uso de @Modifying es obligatorio para operaciones de modificación como DELETE. Esta anotación indica a Spring que la consulta modifica datos en lugar de solo leerlos.

Verificación antes de eliminar

Es una buena práctica verificar la existencia de una entidad antes de intentar eliminarla. Spring Data JPA proporciona métodos útiles para esta validación:

@Service
@Transactional
public class ProductoService {
    
    @Autowired
    private ProductoRepository productoRepository;
    
    public boolean eliminarProductoSiExiste(Long id) {
        if (productoRepository.existsById(id)) {
            productoRepository.deleteById(id);
            return true;
        }
        return false;
    }
    
    public void eliminarProductoConValidacion(Long id) {
        Producto producto = productoRepository.findById(id)
            .orElseThrow(() -> new EntityNotFoundException("Producto no encontrado"));
        
        productoRepository.delete(producto);
    }
}

Eliminación en lote optimizada

Para operaciones masivas, Spring Data JPA ofrece métodos más eficientes que ejecutan una sola consulta SQL en lugar de múltiples operaciones individuales:

public interface ProductoRepository extends JpaRepository<Producto, Long> {
    
    @Modifying
    @Query("DELETE FROM Producto p WHERE p.id IN :ids")
    int eliminarPorIds(@Param("ids") List<Long> ids);
    
    @Modifying
    @Query("DELETE FROM Producto p WHERE p.activo = false")
    int eliminarProductosInactivos();
}

Estos métodos devuelven un int que representa el número de registros eliminados, información útil para confirmar el resultado de la operación:

@Service
@Transactional
public class ProductoService {
    
    public int limpiarProductosInactivos() {
        int eliminados = productoRepository.eliminarProductosInactivos();
        System.out.println("Se eliminaron " + eliminados + " productos inactivos");
        return eliminados;
    }
}

Consideraciones al borrar con asociaciones

Cuando trabajas con entidades que tienen relaciones entre sí, eliminar datos se convierte en una operación más compleja que requiere especial atención. Las asociaciones JPA pueden generar restricciones de integridad referencial que impiden el borrado directo, o pueden provocar efectos en cascada no deseados.

Restricciones de clave foránea

El problema más común al eliminar entidades relacionadas son las restricciones de clave foránea. Si intentas eliminar una entidad que es referenciada por otras, la base de datos lanzará una excepción:

@Entity
public class Categoria {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String nombre;
    
    @OneToMany(mappedBy = "categoria")
    private List<Producto> productos = new ArrayList<>();
}

@Entity
public class Producto {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String nombre;
    
    @ManyToOne
    @JoinColumn(name = "categoria_id")
    private Categoria categoria;
}

Si intentas eliminar una categoría que tiene productos asociados, obtendrás un error de violación de restricción:

@Service
@Transactional
public class CategoriaService {
    
    public void eliminarCategoria(Long categoriaId) {
        // Esto fallará si la categoría tiene productos
        categoriaRepository.deleteById(categoriaId);
    }
}

Estrategias de eliminación en cascada

JPA ofrece la opción cascade = CascadeType.REMOVE para propagar automáticamente las operaciones de eliminación a las entidades relacionadas:

@Entity
public class Categoria {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String nombre;
    
    @OneToMany(mappedBy = "categoria", cascade = CascadeType.REMOVE)
    private List<Producto> productos = new ArrayList<>();
}

Con esta configuración, eliminar una categoría eliminará automáticamente todos sus productos asociados:

@Service
@Transactional
public class CategoriaService {
    
    public void eliminarCategoriaConProductos(Long categoriaId) {
        // Eliminará la categoría y todos sus productos
        categoriaRepository.deleteById(categoriaId);
    }
}

Sin embargo, esta aproximación puede ser peligrosa si no es el comportamiento deseado, ya que podrías eliminar datos importantes sin darte cuenta.

Eliminación manual de dependencias

Una estrategia más controlada consiste en eliminar manualmente las entidades dependientes antes de eliminar la entidad principal:

@Service
@Transactional
public class CategoriaService {
    
    @Autowired
    private CategoriaRepository categoriaRepository;
    
    @Autowired
    private ProductoRepository productoRepository;
    
    public void eliminarCategoriaSegura(Long categoriaId) {
        // Primero eliminar todos los productos de la categoría
        productoRepository.deleteByCategoriaId(categoriaId);
        
        // Después eliminar la categoría
        categoriaRepository.deleteById(categoriaId);
    }
}

Validación antes de eliminar

Es recomendable verificar las dependencias antes de proceder con la eliminación, especialmente en aplicaciones donde los datos tienen valor crítico:

@Service
@Transactional
public class CategoriaService {
    
    public void eliminarCategoriaConValidacion(Long categoriaId) {
        // Verificar si la categoría tiene productos
        long cantidadProductos = productoRepository.countByCategoriaId(categoriaId);
        
        if (cantidadProductos > 0) {
            throw new IllegalStateException(
                "No se puede eliminar la categoría porque tiene " + 
                cantidadProductos + " productos asociados"
            );
        }
        
        categoriaRepository.deleteById(categoriaId);
    }
}

Eliminación lógica como alternativa

En lugar de eliminar físicamente los registros, puedes implementar eliminación lógica mediante un campo booleano que marque los registros como inactivos:

@Entity
public class Categoria {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String nombre;
    
    @Column(name = "activo")
    private boolean activo = true;
    
    @OneToMany(mappedBy = "categoria")
    private List<Producto> productos = new ArrayList<>();
}

Esta aproximación preserva la integridad referencial mientras permite "eliminar" entidades de forma segura:

@Service
@Transactional
public class CategoriaService {
    
    public void desactivarCategoria(Long categoriaId) {
        Categoria categoria = categoriaRepository.findById(categoriaId)
            .orElseThrow(() -> new EntityNotFoundException("Categoría no encontrada"));
        
        categoria.setActivo(false);
        categoriaRepository.save(categoria);
    }
}

Configuración de acciones referenciales

Puedes configurar el comportamiento de las claves foráneas a nivel de base de datos utilizando las anotaciones de JPA:

@Entity
public class Producto {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String nombre;
    
    @ManyToOne
    @JoinColumn(name = "categoria_id", 
                foreignKey = @ForeignKey(name = "fk_producto_categoria"))
    private Categoria categoria;
}

Para casos específicos, puedes usar @OnDelete de Hibernate para definir acciones a nivel de base de datos:

@Entity
public class Producto {
    @ManyToOne
    @JoinColumn(name = "categoria_id")
    @OnDelete(action = OnDeleteAction.SET_NULL)
    private Categoria categoria;
}

Manejo de relaciones bidireccionales

En relaciones bidireccionales, es importante mantener la coherencia al eliminar entidades. Debes actualizar ambos lados de la relación:

@Service
@Transactional
public class ProductoService {
    
    public void eliminarProductoDeCategoria(Long productoId) {
        Producto producto = productoRepository.findById(productoId)
            .orElseThrow(() -> new EntityNotFoundException("Producto no encontrado"));
        
        // Remover la referencia del lado de la categoría
        if (producto.getCategoria() != null) {
            producto.getCategoria().getProductos().remove(producto);
        }
        
        // Eliminar el producto
        productoRepository.delete(producto);
    }
}

Esta gestión cuidadosa de las asociaciones garantiza que las operaciones de eliminación se ejecuten correctamente sin comprometer la integridad de los datos ni generar efectos secundarios inesperados.

Aprende SpringBoot online

Otras lecciones de SpringBoot

Accede a todas las lecciones de SpringBoot y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Introducción A Spring Boot

Spring Boot

Introducción Y Entorno

Spring Boot Starters

Spring Boot

Introducción Y Entorno

Inyección De Dependencias

Spring Boot

Introducción Y Entorno

Crear Proyecto Con Spring Initializr

Spring Boot

Introducción Y Entorno

Crear Proyecto Desde Visual Studio Code

Spring Boot

Introducción Y Entorno

Controladores Spring Mvc

Spring Boot

Spring Mvc Con Thymeleaf

Vista En Spring Mvc Con Thymeleaf

Spring Boot

Spring Mvc Con Thymeleaf

Controladores Spring Rest

Spring Boot

Spring Mvc Con Thymeleaf

Open Api Y Cómo Agregarlo En Spring Boot

Spring Boot

Spring Mvc Con Thymeleaf

Servicios En Spring

Spring Boot

Spring Mvc Con Thymeleaf

Clientes Resttemplate Y Restclient

Spring Boot

Spring Mvc Con Thymeleaf

Rxjava En Spring Web

Spring Boot

Spring Mvc Con Thymeleaf

Métodos Post En Controladores Mvc

Spring Boot

Spring Mvc Con Thymeleaf

Métodos Get En Controladores Mvc

Spring Boot

Spring Mvc Con Thymeleaf

Formularios En Spring Mvc

Spring Boot

Spring Mvc Con Thymeleaf

Crear Proyecto Con Intellij Idea

Spring Boot

Spring Mvc Con Thymeleaf

Introducción A Los Modelos Mvc

Spring Boot

Spring Mvc Con Thymeleaf

Layouts Y Fragmentos En Thymeleaf

Spring Boot

Spring Mvc Con Thymeleaf

Estilización Con Bootstrap Css

Spring Boot

Spring Mvc Con Thymeleaf

Gestión De Errores Controlleradvice

Spring Boot

Spring Mvc Con Thymeleaf

Estilización Con Tailwind Css

Spring Boot

Spring Mvc Con Thymeleaf

Introducción A Controladores Rest

Spring Boot

Spring Rest

Métodos Get En Controladores Rest

Spring Boot

Spring Rest

Métodos Post En Controladores Rest

Spring Boot

Spring Rest

Métodos Delete En Controladores Rest

Spring Boot

Spring Rest

Métodos Put Y Patch En Controladores Rest

Spring Boot

Spring Rest

Gestión De Errores Restcontrolleradvice

Spring Boot

Spring Rest

Creación De Entidades Jpa

Spring Boot

Spring Data Jpa

Asociaciones De Entidades Jpa

Spring Boot

Spring Data Jpa

Repositorios Spring Data

Spring Boot

Spring Data Jpa

Métodos Find En Repositorios

Spring Boot

Spring Data Jpa

Inserción De Datos

Spring Boot

Spring Data Jpa

Actualizar Datos De Base De Datos

Spring Boot

Spring Data Jpa

Borrar Datos De Base De Datos

Spring Boot

Spring Data Jpa

Consultas Jpql Con @Query En Spring Data Jpa

Spring Boot

Spring Data Jpa

Api Query By Example (Qbe)

Spring Boot

Spring Data Jpa

Api Specification

Spring Boot

Spring Data Jpa

Repositorios Reactivos

Spring Boot

Spring Data Jpa

Configuración Base De Datos Postgresql

Spring Boot

Spring Data Jpa

Configuración Base De Datos Mysql

Spring Boot

Spring Data Jpa

Introducción A Jpa Y Spring Data Jpa

Spring Boot

Spring Data Jpa

Configuración Base De Datos H2

Spring Boot

Spring Data Jpa

Testing Unitario De Componentes Y Servicios

Spring Boot

Testing Con Spring Test

Testing De Repositorios Spring Data Jpa

Spring Boot

Testing Con Spring Test

Testing Controladores Spring Mvc Con Thymeleaf

Spring Boot

Testing Con Spring Test

Testing Controladores Rest Con Json

Spring Boot

Testing Con Spring Test

Testing De Aplicaciones Reactivas Webflux

Spring Boot

Testing Con Spring Test

Testing De Seguridad Spring Security

Spring Boot

Testing Con Spring Test

Testing Con Apache Kafka

Spring Boot

Testing Con Spring Test

Introducción Al Testing

Spring Boot

Testing Con Spring Test

Introducción A Spring Security

Spring Boot

Seguridad Con Spring Security

Seguridad Basada En Formulario

Spring Boot

Seguridad Con Spring Security

Registro De Usuarios En Api Rest

Spring Boot

Seguridad Con Spring Security

Login De Usuarios En Api Rest

Spring Boot

Seguridad Con Spring Security

Validación Jwt En Api Rest

Spring Boot

Seguridad Con Spring Security

Autenticación Jwt Completa En Api Rest

Spring Boot

Seguridad Con Spring Security

Seguridad Jwt En Api Rest Reactiva Spring Webflux

Spring Boot

Seguridad Con Spring Security

Autenticación Y Autorización Con Anotaciones

Spring Boot

Seguridad Con Spring Security

Fundamentos De Autenticación Oauth

Spring Boot

Seguridad Con Spring Security

Autenticación Oauth Con Github

Spring Boot

Seguridad Con Spring Security

Testing Con Spring Security Test

Spring Boot

Seguridad Con Spring Security

Autenticación Oauth En Api Rest

Spring Boot

Seguridad Con Spring Security

Introducción A Spring Webflux

Spring Boot

Reactividad Webflux

Spring Data R2dbc

Spring Boot

Reactividad Webflux

Controlador Reactivo Basado En Anotaciones

Spring Boot

Reactividad Webflux

Controlador Reactivo Basado En Funciones

Spring Boot

Reactividad Webflux

Operadores Reactivos Básicos

Spring Boot

Reactividad Webflux

Operadores Reactivos Avanzados

Spring Boot

Reactividad Webflux

Cliente Reactivo Webclient

Spring Boot

Reactividad Webflux

Introducción E Instalación De Apache Kafka

Spring Boot

Mensajería Asíncrona

Crear Proyecto Con Apache Kafka

Spring Boot

Mensajería Asíncrona

Creación De Producers

Spring Boot

Mensajería Asíncrona

Creación De Consumers

Spring Boot

Mensajería Asíncrona

Kafka Streams En Spring Boot

Spring Boot

Mensajería Asíncrona

Integración Con Angular

Spring Boot

Integración Frontend

Integración Con React

Spring Boot

Integración Frontend

Integración Con Vue

Spring Boot

Integración Frontend

Accede GRATIS a SpringBoot y certifícate

Ejercicios de programación de SpringBoot

Evalúa tus conocimientos de esta lección Borrar datos de base de datos con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender los métodos básicos para eliminar entidades en Spring Data JPA.
  • Aprender a crear métodos personalizados de eliminación mediante convenciones y consultas JPQL.
  • Conocer las estrategias para manejar asociaciones y restricciones de clave foránea al eliminar datos.
  • Identificar buenas prácticas para validar y optimizar operaciones de borrado en lote.
  • Entender alternativas como la eliminación lógica para preservar la integridad de los datos.