JDBC ofrece tres formas de ejecutar SQL. La elección correcta es fundamental para la seguridad y el rendimiento.
Statement: solo para SQL sin parámetros
try (Connection con = DatabaseConfig.getConnection();
Statement st = con.createStatement()) {
// DDL
st.execute("CREATE TABLE IF NOT EXISTS productos (id INT PRIMARY KEY AUTO_INCREMENT, nombre VARCHAR(100))");
// Consulta estática (sin parámetros de usuario)
ResultSet rs = st.executeQuery("SELECT COUNT(*) FROM productos");
if (rs.next()) System.out.println("Total: " + rs.getInt(1));
// Actualización
int afectadas = st.executeUpdate("UPDATE productos SET activo=1 WHERE stock > 0");
}
PreparedStatement: la opción correcta para parámetros
PreparedStatement protege contra SQL Injection y mejora el rendimiento porque la base de datos puede compilar y cachear el plan de ejecución:
String sql = "INSERT INTO productos (nombre, precio, categoria) VALUES (?, ?, ?)";
try (Connection con = DatabaseConfig.getConnection();
PreparedStatement ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, "Teclado mecánico");
ps.setDouble(2, 89.99);
ps.setString(3, "Periféricos");
ps.executeUpdate();
// Obtener el ID generado
try (ResultSet keys = ps.getGeneratedKeys()) {
if (keys.next()) System.out.println("ID insertado: " + keys.getLong(1));
}
}
Leer resultados con ResultSet
String sql = "SELECT id, nombre, precio, fecha_alta FROM productos WHERE categoria=? AND precio BETWEEN ? AND ?";
try (Connection con = DatabaseConfig.getConnection();
PreparedStatement ps = con.prepareStatement(sql)) {
ps.setString(1, "Periféricos");
ps.setDouble(2, 20.0);
ps.setDouble(3, 200.0);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
int id = rs.getInt("id");
String nombre = rs.getString("nombre");
double precio = rs.getDouble("precio");
LocalDate alta = rs.getObject("fecha_alta", LocalDate.class); // java.time directo en JDBC 4.2+
System.out.printf(" %d %-30s %.2f€ alta:%s%n", id, nombre, precio, alta);
}
}
}
Mapear ResultSet a registros Java 21
record Producto(int id, String nombre, double precio, LocalDate fechaAlta) {}
List<Producto> listar(String categoria) throws SQLException {
String sql = "SELECT id, nombre, precio, fecha_alta FROM productos WHERE categoria=? ORDER BY nombre";
List<Producto> lista = new ArrayList<>();
try (Connection con = DatabaseConfig.getConnection();
PreparedStatement ps = con.prepareStatement(sql)) {
ps.setString(1, categoria);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
lista.add(new Producto(
rs.getInt("id"),
rs.getString("nombre"),
rs.getDouble("precio"),
rs.getObject("fecha_alta", LocalDate.class)
));
}
}
}
return lista;
}
CallableStatement: procedimientos almacenados
// Llamar a: CALL calcular_descuento(?, ?, ?)
try (Connection con = DatabaseConfig.getConnection();
CallableStatement cs = con.prepareCall("{CALL calcular_descuento(?, ?, ?)}")) {
cs.setInt(1, 42); // parámetro IN: id_producto
cs.setDouble(2, 0.15); // parámetro IN: porcentaje
cs.registerOutParameter(3, Types.DOUBLE); // parámetro OUT: precio_final
cs.execute();
double precioFinal = cs.getDouble(3);
System.out.println("Precio con descuento: " + precioFinal);
}
