DELETE en Java

Eliminar datos de MySQL desde Java requiere las mismas precauciones que en cualquier otro lenguaje: siempre usar sentencias preparadas con placeholders, incluir una clausula WHERE y verificar el resultado de la operacion. JDBC facilita estas operaciones con PreparedStatement y ofrece soporte completo para transacciones que permiten deshacer eliminaciones fallidas. En este articulo aprenderas a eliminar registros de forma segura, implementar borrado logico y manejar eliminaciones en cascada.

Requisitos previos

Necesitas una conexion JDBC configurada y tablas con datos de prueba. Si no las tienes, consulta los articulos anteriores para crearlas y poblarlas.

Codigo completo

Este ejemplo elimina un producto por su ID y verifica el resultado:

import java.sql.*;
 
public class DeleteMySQL {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/tienda?useSSL=false&serverTimezone=UTC";
 
        try (Connection conn = DriverManager.getConnection(url, "root", "tu_contraseña")) {
 
            int productoId = 5;
            String sql = "DELETE FROM productos WHERE id = ?";
 
            try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
                pstmt.setInt(1, productoId);
 
                int filasEliminadas = pstmt.executeUpdate();
 
                if (filasEliminadas > 0) {
                    System.out.println("Producto con ID " + productoId + " eliminado correctamente");
                    System.out.println("Filas eliminadas: " + filasEliminadas);
                } else {
                    System.out.println("No se encontro el producto con ID " + productoId);
                }
            }
 
        } catch (SQLException e) {
            System.err.println("Error: " + e.getMessage());
        }
    }
}

Salida esperada:

Producto con ID 5 eliminado correctamente
Filas eliminadas: 1

Explicacion paso a paso

El metodo executeUpdate() para sentencias DELETE devuelve el numero de filas eliminadas. Un resultado de 0 indica que ninguna fila coincidio con la condicion WHERE, lo cual no es un error sino una situacion que debes manejar en tu logica de negocio.

La importancia de la clausula WHERE no puede subestimarse: un DELETE FROM productos sin WHERE eliminaria todos los registros de la tabla. Por eso es fundamental usar siempre sentencias preparadas con parametros, nunca concatenar valores directamente en la consulta SQL.

Eliminar con verificacion previa

En muchas aplicaciones es necesario verificar que el registro existe y mostrar informacion sobre lo que se va a eliminar antes de proceder:

public static boolean eliminarConVerificacion(Connection conn, int productoId) throws SQLException {
    // Verificar que existe y obtener datos
    String sqlVerificar = "SELECT id, nombre, precio FROM productos WHERE id = ?";
 
    try (PreparedStatement pstmt = conn.prepareStatement(sqlVerificar)) {
        pstmt.setInt(1, productoId);
 
        try (ResultSet rs = pstmt.executeQuery()) {
            if (!rs.next()) {
                System.out.println("Producto no encontrado");
                return false;
            }
 
            System.out.printf("Eliminando: [%d] %s ($%.2f)%n",
                rs.getInt("id"), rs.getString("nombre"), rs.getDouble("precio"));
        }
    }
 
    // Proceder con la eliminacion
    String sqlDelete = "DELETE FROM productos WHERE id = ?";
 
    try (PreparedStatement pstmt = conn.prepareStatement(sqlDelete)) {
        pstmt.setInt(1, productoId);
        pstmt.executeUpdate();
        System.out.println("Producto eliminado correctamente");
        return true;
    }
}

Eliminar multiples registros

Puedes eliminar varios registros a la vez construyendo una clausula IN con placeholders dinamicos:

public static int eliminarPorIds(Connection conn, int... ids) throws SQLException {
    if (ids.length == 0) return 0;
 
    // Construir placeholders: ?, ?, ?
    StringBuilder placeholders = new StringBuilder();
    for (int i = 0; i < ids.length; i++) {
        if (i > 0) placeholders.append(", ");
        placeholders.append("?");
    }
 
    String sql = "DELETE FROM productos WHERE id IN (" + placeholders + ")";
 
    try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
        for (int i = 0; i < ids.length; i++) {
            pstmt.setInt(i + 1, ids[i]);
        }
 
        int filas = pstmt.executeUpdate();
        System.out.printf("%d productos eliminados de %d solicitados%n", filas, ids.length);
        return filas;
    }
}
 
// Uso
eliminarPorIds(conn, 4, 5, 6);

Eliminar por condicion

public static int eliminarPorCategoria(Connection conn, String categoria) throws SQLException {
    // Contar antes de eliminar
    String sqlContar = "SELECT COUNT(*) FROM productos WHERE categoria = ?";
    int total;
 
    try (PreparedStatement pstmt = conn.prepareStatement(sqlContar)) {
        pstmt.setString(1, categoria);
        try (ResultSet rs = pstmt.executeQuery()) {
            rs.next();
            total = rs.getInt(1);
        }
    }
 
    if (total == 0) {
        System.out.println("No hay productos en la categoria '" + categoria + "'");
        return 0;
    }
 
    String sqlDelete = "DELETE FROM productos WHERE categoria = ?";
 
    try (PreparedStatement pstmt = conn.prepareStatement(sqlDelete)) {
        pstmt.setString(1, categoria);
        int filas = pstmt.executeUpdate();
        System.out.printf("%d productos eliminados de la categoria '%s'%n", filas, categoria);
        return filas;
    }
}

Eliminacion por lotes

Para eliminar muchos registros de forma eficiente, puedes usar addBatch():

public static int eliminarLote(Connection conn, List<Integer> ids) throws SQLException {
    String sql = "DELETE FROM productos WHERE id = ?";
 
    conn.setAutoCommit(false);
 
    try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
        for (int id : ids) {
            pstmt.setInt(1, id);
            pstmt.addBatch();
        }
 
        int[] resultados = pstmt.executeBatch();
        conn.commit();
 
        int totalEliminados = 0;
        for (int r : resultados) {
            if (r >= 0) totalEliminados += r;
        }
 
        System.out.println("Eliminados: " + totalEliminados + " de " + ids.size());
        return totalEliminados;
 
    } catch (SQLException e) {
        conn.rollback();
        throw e;
    } finally {
        conn.setAutoCommit(true);
    }
}

Caso practico

Veamos un sistema completo de borrado logico (soft delete) con papelera de reciclaje y purga programada:

import java.sql.*;
import java.util.ArrayList;
import java.util.List;
 
public class PapeleraProductos {
 
    /**
     * Mover un producto a la papelera (soft delete)
     */
    public static boolean softDelete(Connection conn, int productoId) throws SQLException {
        String sql = "UPDATE productos SET activo = 0, eliminado_en = NOW() WHERE id = ? AND activo = 1";
 
        try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setInt(1, productoId);
            int filas = pstmt.executeUpdate();
 
            if (filas > 0) {
                System.out.println("Producto " + productoId + " movido a la papelera");
                return true;
            } else {
                System.out.println("Producto " + productoId + " no encontrado o ya eliminado");
                return false;
            }
        }
    }
 
    /**
     * Restaurar un producto de la papelera
     */
    public static boolean restaurar(Connection conn, int productoId) throws SQLException {
        String sql = "UPDATE productos SET activo = 1, eliminado_en = NULL WHERE id = ? AND activo = 0";
 
        try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setInt(1, productoId);
            int filas = pstmt.executeUpdate();
 
            if (filas > 0) {
                System.out.println("Producto " + productoId + " restaurado");
                return true;
            } else {
                System.out.println("Producto " + productoId + " no esta en la papelera");
                return false;
            }
        }
    }
 
    /**
     * Listar productos en la papelera
     */
    public static List<String> listarPapelera(Connection conn) throws SQLException {
        String sql = "SELECT id, nombre, eliminado_en FROM productos WHERE activo = 0 ORDER BY eliminado_en DESC";
        List<String> items = new ArrayList<>();
 
        try (PreparedStatement pstmt = conn.prepareStatement(sql);
             ResultSet rs = pstmt.executeQuery()) {
 
            while (rs.next()) {
                String item = String.format("[%d] %s (eliminado: %s)",
                    rs.getInt("id"), rs.getString("nombre"), rs.getTimestamp("eliminado_en"));
                items.add(item);
            }
        }
 
        return items;
    }
 
    /**
     * Purgar productos eliminados hace mas de N dias
     */
    public static int purgarPapelera(Connection conn, int diasLimite) throws SQLException {
        conn.setAutoCommit(false);
 
        try {
            // Listar productos a purgar
            String sqlListar = "SELECT id, nombre, eliminado_en FROM productos "
                             + "WHERE activo = 0 AND eliminado_en < DATE_SUB(NOW(), INTERVAL ? DAY)";
 
            List<Integer> idsPurgar = new ArrayList<>();
 
            try (PreparedStatement pstmt = conn.prepareStatement(sqlListar)) {
                pstmt.setInt(1, diasLimite);
 
                try (ResultSet rs = pstmt.executeQuery()) {
                    System.out.println("Productos a purgar:");
                    while (rs.next()) {
                        int id = rs.getInt("id");
                        idsPurgar.add(id);
                        System.out.printf("  [%d] %s (eliminado: %s)%n",
                            id, rs.getString("nombre"), rs.getTimestamp("eliminado_en"));
                    }
                }
            }
 
            if (idsPurgar.isEmpty()) {
                System.out.println("No hay productos para purgar");
                return 0;
            }
 
            // Construir y ejecutar DELETE
            StringBuilder placeholders = new StringBuilder();
            for (int i = 0; i < idsPurgar.size(); i++) {
                if (i > 0) placeholders.append(", ");
                placeholders.append("?");
            }
 
            String sqlDelete = "DELETE FROM productos WHERE id IN (" + placeholders + ")";
 
            try (PreparedStatement pstmt = conn.prepareStatement(sqlDelete)) {
                for (int i = 0; i < idsPurgar.size(); i++) {
                    pstmt.setInt(i + 1, idsPurgar.get(i));
                }
                pstmt.executeUpdate();
            }
 
            conn.commit();
            System.out.println(idsPurgar.size() + " productos purgados permanentemente");
            return idsPurgar.size();
 
        } catch (SQLException e) {
            conn.rollback();
            System.err.println("Error durante la purga: " + e.getMessage());
            return 0;
        } finally {
            conn.setAutoCommit(true);
        }
    }
 
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/tienda?useSSL=false&serverTimezone=UTC";
 
        try (Connection conn = DriverManager.getConnection(url, "root", "tu_contraseña")) {
 
            // Soft delete
            softDelete(conn, 3);
            softDelete(conn, 4);
 
            // Listar papelera
            System.out.println("\nContenido de la papelera:");
            for (String item : listarPapelera(conn)) {
                System.out.println("  " + item);
            }
 
            // Restaurar uno
            System.out.println();
            restaurar(conn, 3);
 
            // Purgar (0 dias = purgar todo inmediatamente para demo)
            System.out.println();
            purgarPapelera(conn, 0);
 
        } catch (SQLException e) {
            System.err.println("Error: " + e.getMessage());
        }
    }
}

Salida esperada:

Producto 3 movido a la papelera
Producto 4 movido a la papelera

Contenido de la papelera:
  [4] Monitor Samsung 27" (eliminado: 2024-01-15 10:30:00.0)
  [3] Teclado Mecanico Corsair K70 (eliminado: 2024-01-15 10:30:00.0)

Producto 3 restaurado

Productos a purgar:
  [4] Monitor Samsung 27" (eliminado: 2024-01-15 10:30:00.0)
1 productos purgados permanentemente

Eliminacion en cascada con transaccion

Cuando necesitas eliminar un registro y todos sus registros dependientes en tablas relacionadas, una transaccion garantiza que la operacion sea atomica:

public static boolean eliminarCategoriaCompleta(Connection conn, String categoria) throws SQLException {
    conn.setAutoCommit(false);
 
    try {
        // Obtener IDs de productos en la categoria
        String sqlProductos = "SELECT id FROM productos WHERE categoria = ?";
        List<Integer> productoIds = new ArrayList<>();
 
        try (PreparedStatement pstmt = conn.prepareStatement(sqlProductos)) {
            pstmt.setString(1, categoria);
            try (ResultSet rs = pstmt.executeQuery()) {
                while (rs.next()) {
                    productoIds.add(rs.getInt("id"));
                }
            }
        }
 
        if (productoIds.isEmpty()) {
            System.out.println("No hay productos en la categoria '" + categoria + "'");
            return false;
        }
 
        // Eliminar detalles de pedido asociados
        StringBuilder placeholders = new StringBuilder();
        for (int i = 0; i < productoIds.size(); i++) {
            if (i > 0) placeholders.append(", ");
            placeholders.append("?");
        }
 
        String sqlDetalles = "DELETE FROM detalle_pedido WHERE producto_id IN (" + placeholders + ")";
        int detalles;
        try (PreparedStatement pstmt = conn.prepareStatement(sqlDetalles)) {
            for (int i = 0; i < productoIds.size(); i++) {
                pstmt.setInt(i + 1, productoIds.get(i));
            }
            detalles = pstmt.executeUpdate();
        }
 
        // Eliminar los productos
        String sqlDelete = "DELETE FROM productos WHERE categoria = ?";
        int productos;
        try (PreparedStatement pstmt = conn.prepareStatement(sqlDelete)) {
            pstmt.setString(1, categoria);
            productos = pstmt.executeUpdate();
        }
 
        conn.commit();
 
        System.out.printf("Categoria '%s' eliminada: %d productos, %d detalles de pedido%n",
            categoria, productos, detalles);
        return true;
 
    } catch (SQLException e) {
        conn.rollback();
        throw e;
    } finally {
        conn.setAutoCommit(true);
    }
}

Manejo de errores

Los errores comunes al eliminar registros y como manejarlos:

public static Map<String, Object> eliminarSeguro(Connection conn, int productoId) {
    Map<String, Object> resultado = new java.util.HashMap<>();
    String sql = "DELETE FROM productos WHERE id = ?";
 
    try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
        pstmt.setInt(1, productoId);
        int filas = pstmt.executeUpdate();
 
        if (filas == 0) {
            resultado.put("exito", false);
            resultado.put("mensaje", "Producto no encontrado");
        } else {
            resultado.put("exito", true);
            resultado.put("eliminados", filas);
        }
 
    } catch (SQLException e) {
        resultado.put("exito", false);
 
        switch (e.getErrorCode()) {
            case 1451:
                resultado.put("mensaje",
                    "No se puede eliminar: otros registros dependen de este producto (clave foranea)");
                break;
            case 1205:
                resultado.put("mensaje",
                    "El registro esta bloqueado por otra operacion. Intenta de nuevo");
                break;
            case 1213:
                resultado.put("mensaje",
                    "Deadlock detectado. Reintenta la operacion");
                break;
            default:
                resultado.put("mensaje", "Error [" + e.getErrorCode() + "]: " + e.getMessage());
        }
    }
 
    return resultado;
}

Con esto completamos las secciones de integracion de MySQL con lenguajes de programacion. Has aprendido a conectarte, consultar, insertar, actualizar y eliminar datos desde Java, Node.js, Python, PHP y Perl, cubriendo los lenguajes mas utilizados en el desarrollo de aplicaciones con MySQL.

Escrito por Eduardo Lázaro