INSERT en Java

Insertar datos en MySQL desde Java se realiza con PreparedStatement y su metodo executeUpdate(). JDBC ofrece soporte nativo para recuperar claves generadas automaticamente, insertar por lotes con addBatch()/executeBatch() y manejar transacciones para garantizar la integridad de inserciones multiples. En este articulo aprenderas todas las tecnicas de insercion disponibles en Java.

Requisitos previos

Necesitas una conexion JDBC configurada y la tabla productos creada. Si no la tienes, consulta los articulos anteriores.

Codigo completo

Este ejemplo inserta un producto y recupera el ID generado automaticamente:

import java.sql.*;
 
public class InsertMySQL {
    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")) {
 
            String sql = "INSERT INTO productos (nombre, categoria, precio, stock) VALUES (?, ?, ?, ?)";
 
            try (PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
                pstmt.setString(1, "Tablet Samsung Galaxy Tab S9");
                pstmt.setString(2, "Tablets");
                pstmt.setDouble(3, 8999.00);
                pstmt.setInt(4, 35);
 
                int filasAfectadas = pstmt.executeUpdate();
 
                try (ResultSet keys = pstmt.getGeneratedKeys()) {
                    if (keys.next()) {
                        long nuevoId = keys.getLong(1);
                        System.out.println("Producto insertado exitosamente");
                        System.out.println("ID generado: " + nuevoId);
                        System.out.println("Filas afectadas: " + filasAfectadas);
                    }
                }
            }
 
        } catch (SQLException e) {
            System.err.println("Error: " + e.getMessage());
        }
    }
}

Salida esperada:

Producto insertado exitosamente
ID generado: 7
Filas afectadas: 1

Explicacion paso a paso

El metodo executeUpdate() ejecuta sentencias INSERT, UPDATE o DELETE y devuelve el numero de filas afectadas. Para recuperar el ID auto-generado, debes pasar Statement.RETURN_GENERATED_KEYS como segundo argumento de prepareStatement(). Luego llamas a getGeneratedKeys() que devuelve un ResultSet con las claves generadas.

Los metodos setXxx() asignan valores a los placeholders ? en la sentencia SQL. Cada metodo corresponde a un tipo Java: setString() para VARCHAR, setInt() para INT, setDouble() para DECIMAL/DOUBLE, setBigDecimal() para DECIMAL de alta precision, setTimestamp() para DATETIME, entre otros.

Insertar con variables

public static long insertarProducto(Connection conn, String nombre, String categoria,
                                     double precio, int stock) throws SQLException {
    String sql = "INSERT INTO productos (nombre, categoria, precio, stock) VALUES (?, ?, ?, ?)";
 
    try (PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
        pstmt.setString(1, nombre);
        pstmt.setString(2, categoria);
        pstmt.setDouble(3, precio);
        pstmt.setInt(4, stock);
 
        pstmt.executeUpdate();
 
        try (ResultSet keys = pstmt.getGeneratedKeys()) {
            if (keys.next()) {
                return keys.getLong(1);
            }
        }
    }
 
    return -1;
}
 
// Uso
long id = insertarProducto(conn, "Cargador USB-C 65W", "Accesorios", 599.00, 300);
System.out.println("Producto creado con ID: " + id);

Insertar con tipos especiales

Cuando trabajas con fechas, valores NULL o tipos decimales de alta precision, necesitas metodos especificos:

String sql = "INSERT INTO pedidos (cliente_id, fecha, estado, total, notas) VALUES (?, ?, ?, ?, ?)";
 
try (PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
    pstmt.setInt(1, 1);
    pstmt.setTimestamp(2, Timestamp.valueOf(java.time.LocalDateTime.now()));
    pstmt.setString(3, "pendiente");
    pstmt.setBigDecimal(4, new java.math.BigDecimal("16197.99"));
    pstmt.setNull(5, Types.VARCHAR);  // Valor NULL explicito
 
    pstmt.executeUpdate();
}

Insercion por lotes con addBatch/executeBatch

Cuando necesitas insertar muchos registros, la insercion por lotes es significativamente mas rapida que ejecutar cada INSERT individualmente. El metodo addBatch() acumula las sentencias y executeBatch() las envia todas al servidor en una sola operacion de red:

public static int insertarLote(Connection conn) throws SQLException {
    String sql = "INSERT INTO productos (nombre, categoria, precio, stock) VALUES (?, ?, ?, ?)";
 
    Object[][] productos = {
        {"Impresora HP LaserJet", "Impresoras", 3499.00, 20},
        {"Cable HDMI 2.1 3m", "Accesorios", 349.00, 500},
        {"Mousepad XXL Gaming", "Accesorios", 449.00, 200},
        {"Soporte Monitor Ajustable", "Accesorios", 1299.00, 75},
        {"Adaptador DisplayPort", "Accesorios", 249.00, 400},
    };
 
    conn.setAutoCommit(false);
 
    try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
        for (Object[] producto : productos) {
            pstmt.setString(1, (String) producto[0]);
            pstmt.setString(2, (String) producto[1]);
            pstmt.setDouble(3, (Double) producto[2]);
            pstmt.setInt(4, (Integer) producto[3]);
            pstmt.addBatch();
        }
 
        int[] resultados = pstmt.executeBatch();
        conn.commit();
 
        int totalInsertados = 0;
        for (int r : resultados) {
            if (r >= 0) totalInsertados += r;
        }
 
        System.out.println("Registros insertados: " + totalInsertados);
        return totalInsertados;
 
    } catch (SQLException e) {
        conn.rollback();
        throw e;
    } finally {
        conn.setAutoCommit(true);
    }
}

Salida esperada:

Registros insertados: 5

El metodo executeBatch() devuelve un array de enteros donde cada posicion indica el resultado de cada sentencia del lote. Un valor positivo indica el numero de filas afectadas, Statement.SUCCESS_NO_INFO (-2) indica exito sin informacion de filas, y Statement.EXECUTE_FAILED (-3) indica un fallo.

INSERT ON DUPLICATE KEY UPDATE

Cuando quieres insertar un registro o actualizarlo si ya existe (basado en una clave unica), puedes usar INSERT ... ON DUPLICATE KEY UPDATE:

public static void insertarOActualizar(Connection conn, String nombre, String categoria,
                                        double precio, int stock) throws SQLException {
    String sql = "INSERT INTO productos (nombre, categoria, precio, stock) VALUES (?, ?, ?, ?) "
               + "ON DUPLICATE KEY UPDATE precio = VALUES(precio), stock = stock + VALUES(stock)";
 
    try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
        pstmt.setString(1, nombre);
        pstmt.setString(2, categoria);
        pstmt.setDouble(3, precio);
        pstmt.setInt(4, stock);
 
        int resultado = pstmt.executeUpdate();
        // resultado = 1 si se inserto, 2 si se actualizo
        if (resultado == 1) {
            System.out.println("Producto insertado: " + nombre);
        } else if (resultado == 2) {
            System.out.println("Producto actualizado: " + nombre);
        }
    }
}

Caso practico

Veamos un sistema de registro de pedidos completo que inserta en multiples tablas dentro de una transaccion:

import java.sql.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
 
public class RegistroPedidos {
 
    public static long registrarPedido(Connection conn, int clienteId,
                                        List<Map<String, Object>> items) throws SQLException {
        conn.setAutoCommit(false);
 
        try {
            // Calcular total
            BigDecimal total = BigDecimal.ZERO;
            for (Map<String, Object> item : items) {
                BigDecimal cantidad = BigDecimal.valueOf((Integer) item.get("cantidad"));
                BigDecimal precioUnitario = (BigDecimal) item.get("precioUnitario");
                total = total.add(cantidad.multiply(precioUnitario));
            }
 
            // Insertar pedido
            String sqlPedido = "INSERT INTO pedidos (cliente_id, fecha, estado, total) VALUES (?, NOW(), ?, ?)";
            long pedidoId;
 
            try (PreparedStatement pstmt = conn.prepareStatement(sqlPedido, Statement.RETURN_GENERATED_KEYS)) {
                pstmt.setInt(1, clienteId);
                pstmt.setString(2, "pendiente");
                pstmt.setBigDecimal(3, total);
                pstmt.executeUpdate();
 
                try (ResultSet keys = pstmt.getGeneratedKeys()) {
                    keys.next();
                    pedidoId = keys.getLong(1);
                }
            }
 
            // Insertar detalles y descontar stock
            String sqlDetalle = "INSERT INTO detalle_pedido (pedido_id, producto_id, cantidad, precio_unitario) "
                              + "VALUES (?, ?, ?, ?)";
            String sqlStock = "UPDATE productos SET stock = stock - ? WHERE id = ? AND stock >= ?";
 
            try (PreparedStatement pstmtDetalle = conn.prepareStatement(sqlDetalle);
                 PreparedStatement pstmtStock = conn.prepareStatement(sqlStock)) {
 
                for (Map<String, Object> item : items) {
                    int productoId = (Integer) item.get("productoId");
                    int cantidad = (Integer) item.get("cantidad");
                    BigDecimal precioUnitario = (BigDecimal) item.get("precioUnitario");
 
                    // Insertar detalle
                    pstmtDetalle.setLong(1, pedidoId);
                    pstmtDetalle.setInt(2, productoId);
                    pstmtDetalle.setInt(3, cantidad);
                    pstmtDetalle.setBigDecimal(4, precioUnitario);
                    pstmtDetalle.executeUpdate();
 
                    // Descontar stock
                    pstmtStock.setInt(1, cantidad);
                    pstmtStock.setInt(2, productoId);
                    pstmtStock.setInt(3, cantidad);
                    int actualizado = pstmtStock.executeUpdate();
 
                    if (actualizado == 0) {
                        throw new SQLException("Stock insuficiente para producto " + productoId);
                    }
                }
            }
 
            conn.commit();
 
            System.out.println("Pedido #" + pedidoId + " registrado exitosamente");
            System.out.println("  Cliente: " + clienteId);
            System.out.println("  Items: " + items.size());
            System.out.printf("  Total: $%s%n", total);
 
            return pedidoId;
 
        } catch (SQLException e) {
            conn.rollback();
            throw e;
        } 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")) {
 
            List<Map<String, Object>> items = List.of(
                Map.of("productoId", 1, "cantidad", 1, "precioUnitario", new BigDecimal("12999.99")),
                Map.of("productoId", 2, "cantidad", 2, "precioUnitario", new BigDecimal("1599.00"))
            );
 
            registrarPedido(conn, 1, items);
 
        } catch (SQLException e) {
            System.err.println("Error al registrar pedido: " + e.getMessage());
        }
    }
}

Salida esperada:

Pedido #1 registrado exitosamente
  Cliente: 1
  Items: 2
  Total: $16197.99

Manejo de errores

Los errores comunes al insertar datos y como identificarlos:

public static Map<String, Object> insertarSeguro(Connection conn, String nombre, String categoria,
                                                   double precio, int stock) {
    String sql = "INSERT INTO productos (nombre, categoria, precio, stock) VALUES (?, ?, ?, ?)";
    Map<String, Object> resultado = new java.util.HashMap<>();
 
    try (PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
        pstmt.setString(1, nombre);
        pstmt.setString(2, categoria);
        pstmt.setDouble(3, precio);
        pstmt.setInt(4, stock);
 
        pstmt.executeUpdate();
 
        try (ResultSet keys = pstmt.getGeneratedKeys()) {
            if (keys.next()) {
                resultado.put("exito", true);
                resultado.put("id", keys.getLong(1));
            }
        }
 
    } catch (SQLException e) {
        resultado.put("exito", false);
 
        switch (e.getErrorCode()) {
            case 1062:
                resultado.put("mensaje", "Ya existe un producto con ese nombre");
                break;
            case 1048:
                resultado.put("mensaje", "Faltan campos obligatorios (valor NULL no permitido)");
                break;
            case 1406:
                resultado.put("mensaje", "Uno de los valores excede la longitud permitida");
                break;
            case 1452:
                resultado.put("mensaje", "Referencia a tabla externa no valida (clave foranea)");
                break;
            default:
                resultado.put("mensaje", "Error [" + e.getErrorCode() + "]: " + e.getMessage());
        }
    }
 
    return resultado;
}

Ahora que sabes como insertar datos, en el siguiente articulo aprenderas a actualizar registros en MySQL desde Java.

Escrito por Eduardo Lázaro