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
