Consultas SELECT en Java
Recuperar datos de MySQL desde Java se realiza a traves de PreparedStatement y ResultSet, las dos clases fundamentales de JDBC para ejecutar consultas parametrizadas y procesar resultados. PreparedStatement previene inyecciones SQL al separar la estructura de la consulta de sus valores, mientras que ResultSet proporciona acceso fila por fila a los datos devueltos. En este articulo aprenderas a dominar ambas clases para construir consultas seguras y eficientes.
Requisitos previos
Necesitas una conexion JDBC configurada y la tabla productos con datos de prueba:
USE tienda;
INSERT INTO productos (nombre, categoria, precio, stock) VALUES
('Laptop HP Pavilion', 'Computadoras', 12999.99, 25),
('Mouse Logitech MX Master', 'Perifericos', 1599.00, 150),
('Teclado Mecanico Corsair K70', 'Perifericos', 2299.50, 80),
('Monitor Samsung 27"', 'Monitores', 6499.00, 40),
('Auriculares Sony WH-1000XM5', 'Audio', 5999.99, 60),
('Webcam Logitech C920', 'Perifericos', 1299.00, 200);Codigo completo
Este ejemplo ejecuta una consulta parametrizada y muestra los resultados:
import java.sql.*;
public class ConsultasMySQL {
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 = "SELECT id, nombre, precio, stock FROM productos WHERE categoria = ? ORDER BY precio DESC";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, "Perifericos");
try (ResultSet rs = pstmt.executeQuery()) {
System.out.println("Productos en categoria \"Perifericos\":");
int total = 0;
while (rs.next()) {
int id = rs.getInt("id");
String nombre = rs.getString("nombre");
double precio = rs.getDouble("precio");
int stock = rs.getInt("stock");
System.out.printf(" [%d] %s - $%.2f (%d en stock)%n", id, nombre, precio, stock);
total++;
}
System.out.println("Total: " + total + " productos");
}
}
} catch (SQLException e) {
System.err.println("Error: " + e.getMessage());
}
}
}Salida esperada:
Productos en categoria "Perifericos":
[3] Teclado Mecanico Corsair K70 - $2299.50 (80 en stock)
[2] Mouse Logitech MX Master - $1599.00 (150 en stock)
[6] Webcam Logitech C920 - $1299.00 (200 en stock)
Total: 3 productos
Explicacion paso a paso
El flujo de una consulta JDBC sigue tres pasos: preparar la sentencia con prepareStatement(), asignar los parametros con los metodos setXxx() y ejecutar con executeQuery() que devuelve un ResultSet. El ResultSet es un cursor que avanza fila por fila con next(), devolviendo false cuando no quedan mas filas.
Metodos getXxx del ResultSet
ResultSet ofrece metodos tipados para extraer valores de cada columna. Puedes acceder a las columnas por nombre o por indice (empezando en 1):
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
// Por nombre de columna (mas legible)
String nombre = rs.getString("nombre");
double precio = rs.getDouble("precio");
int stock = rs.getInt("stock");
// Por indice de columna (mas rapido, empieza en 1)
String nombre2 = rs.getString(1);
double precio2 = rs.getDouble(2);
int stock2 = rs.getInt(3);
// Tipos especiales
java.sql.Date fecha = rs.getDate("fecha_creacion");
java.sql.Timestamp timestamp = rs.getTimestamp("fecha_creacion");
java.math.BigDecimal precioExacto = rs.getBigDecimal("precio");
boolean activo = rs.getBoolean("activo");
// Verificar si un valor es NULL
int valor = rs.getInt("stock");
if (rs.wasNull()) {
System.out.println("El stock es NULL");
}
}
}Es importante notar que getInt() devuelve 0 para valores NULL, y getDouble() devuelve 0.0. Por eso existe wasNull(), que debes llamar inmediatamente despues de extraer un valor para saber si era realmente NULL.
Consulta de un solo registro
Cuando esperas exactamente un resultado, puedes usar if en lugar de while:
public static void buscarPorId(Connection conn, int productoId) throws SQLException {
String sql = "SELECT id, nombre, categoria, precio, stock FROM productos WHERE id = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, productoId);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
System.out.println("Producto encontrado:");
System.out.println(" Nombre: " + rs.getString("nombre"));
System.out.println(" Categoria: " + rs.getString("categoria"));
System.out.printf(" Precio: $%.2f%n", rs.getDouble("precio"));
System.out.println(" Stock: " + rs.getInt("stock"));
} else {
System.out.println("Producto con ID " + productoId + " no encontrado");
}
}
}
}Consulta con multiples parametros
public static void buscarPorRangoPrecio(Connection conn, double precioMin, double precioMax, String categoria)
throws SQLException {
String sql = "SELECT nombre, categoria, precio FROM productos "
+ "WHERE precio BETWEEN ? AND ? AND categoria = ? "
+ "ORDER BY precio";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setDouble(1, precioMin);
pstmt.setDouble(2, precioMax);
pstmt.setString(3, categoria);
try (ResultSet rs = pstmt.executeQuery()) {
System.out.printf("Productos entre $%.2f y $%.2f en '%s':%n", precioMin, precioMax, categoria);
while (rs.next()) {
System.out.printf(" %s - $%.2f%n", rs.getString("nombre"), rs.getDouble("precio"));
}
}
}
}Obtener metadatos del resultado
ResultSetMetaData proporciona informacion sobre las columnas devueltas por la consulta:
try (PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM productos LIMIT 1");
ResultSet rs = pstmt.executeQuery()) {
ResultSetMetaData meta = rs.getMetaData();
int columnas = meta.getColumnCount();
System.out.println("Estructura de la tabla productos:");
for (int i = 1; i <= columnas; i++) {
System.out.printf(" %-20s %-15s nullable=%s%n",
meta.getColumnName(i),
meta.getColumnTypeName(i),
meta.isNullable(i) == ResultSetMetaData.columnNullable ? "si" : "no"
);
}
}Mapear resultados a objetos
En aplicaciones reales, es comun mapear cada fila del ResultSet a un objeto Java:
import java.util.ArrayList;
import java.util.List;
public class Producto {
private int id;
private String nombre;
private String categoria;
private double precio;
private int stock;
// Constructor, getters y setters
public Producto(int id, String nombre, String categoria, double precio, int stock) {
this.id = id;
this.nombre = nombre;
this.categoria = categoria;
this.precio = precio;
this.stock = stock;
}
public int getId() { return id; }
public String getNombre() { return nombre; }
public String getCategoria() { return categoria; }
public double getPrecio() { return precio; }
public int getStock() { return stock; }
@Override
public String toString() {
return String.format("[%d] %s (%s) - $%.2f, stock: %d", id, nombre, categoria, precio, stock);
}
}public static List<Producto> listarProductos(Connection conn, String categoria) throws SQLException {
String sql = "SELECT id, nombre, categoria, precio, stock FROM productos WHERE categoria = ?";
List<Producto> productos = new ArrayList<>();
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, categoria);
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
productos.add(new Producto(
rs.getInt("id"),
rs.getString("nombre"),
rs.getString("categoria"),
rs.getDouble("precio"),
rs.getInt("stock")
));
}
}
}
return productos;
}Caso practico
Veamos una funcion de busqueda con paginacion y filtros dinamicos:
import java.sql.*;
import java.util.*;
public class BuscadorProductos {
public static Map<String, Object> buscar(Connection conn, Map<String, Object> filtros,
int pagina, int porPagina) throws SQLException {
StringBuilder sql = new StringBuilder(
"SELECT id, nombre, categoria, precio, stock FROM productos WHERE activo = 1"
);
List<Object> params = new ArrayList<>();
// Filtro por busqueda
if (filtros.containsKey("busqueda")) {
sql.append(" AND nombre LIKE ?");
params.add("%" + filtros.get("busqueda") + "%");
}
// Filtro por categoria
if (filtros.containsKey("categoria")) {
sql.append(" AND categoria = ?");
params.add(filtros.get("categoria"));
}
// Filtro por rango de precio
if (filtros.containsKey("precioMin")) {
sql.append(" AND precio >= ?");
params.add(filtros.get("precioMin"));
}
if (filtros.containsKey("precioMax")) {
sql.append(" AND precio <= ?");
params.add(filtros.get("precioMax"));
}
// Contar total
String sqlCount = sql.toString().replaceFirst("SELECT .+ FROM", "SELECT COUNT(*) FROM");
int total;
try (PreparedStatement pstmt = conn.prepareStatement(sqlCount)) {
for (int i = 0; i < params.size(); i++) {
pstmt.setObject(i + 1, params.get(i));
}
try (ResultSet rs = pstmt.executeQuery()) {
rs.next();
total = rs.getInt(1);
}
}
// Paginacion
int offset = (pagina - 1) * porPagina;
sql.append(" ORDER BY nombre LIMIT ? OFFSET ?");
params.add(porPagina);
params.add(offset);
// Ejecutar consulta
List<Producto> productos = new ArrayList<>();
try (PreparedStatement pstmt = conn.prepareStatement(sql.toString())) {
for (int i = 0; i < params.size(); i++) {
pstmt.setObject(i + 1, params.get(i));
}
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
productos.add(new Producto(
rs.getInt("id"),
rs.getString("nombre"),
rs.getString("categoria"),
rs.getDouble("precio"),
rs.getInt("stock")
));
}
}
}
// Resultado
Map<String, Object> resultado = new HashMap<>();
resultado.put("datos", productos);
resultado.put("total", total);
resultado.put("pagina", pagina);
resultado.put("totalPaginas", (int) Math.ceil((double) total / porPagina));
return resultado;
}
@SuppressWarnings("unchecked")
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")) {
Map<String, Object> filtros = new HashMap<>();
filtros.put("categoria", "Perifericos");
filtros.put("precioMin", 1000.0);
filtros.put("precioMax", 3000.0);
Map<String, Object> resultado = buscar(conn, filtros, 1, 5);
List<Producto> datos = (List<Producto>) resultado.get("datos");
System.out.printf("Mostrando %d de %d resultados%n", datos.size(), resultado.get("total"));
System.out.printf("Pagina %d de %d%n", resultado.get("pagina"), resultado.get("totalPaginas"));
for (Producto p : datos) {
System.out.println(" " + p);
}
} catch (SQLException e) {
System.err.println("Error: " + e.getMessage());
}
}
}Salida esperada:
Mostrando 3 de 3 resultados
Pagina 1 de 1
[2] Mouse Logitech MX Master (Perifericos) - $1599.00, stock: 150
[3] Teclado Mecanico Corsair K70 (Perifericos) - $2299.50, stock: 80
[6] Webcam Logitech C920 (Perifericos) - $1299.00, stock: 200
Manejo de errores
Los errores comunes al ejecutar consultas SELECT:
public static List<Producto> consultaSegura(Connection conn, String sql, Object... params) {
List<Producto> productos = new ArrayList<>();
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
for (int i = 0; i < params.length; i++) {
pstmt.setObject(i + 1, params[i]);
}
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
productos.add(new Producto(
rs.getInt("id"),
rs.getString("nombre"),
rs.getString("categoria"),
rs.getDouble("precio"),
rs.getInt("stock")
));
}
}
} catch (SQLException e) {
switch (e.getErrorCode()) {
case 1146:
System.err.println("La tabla especificada no existe");
break;
case 1054:
System.err.println("Una de las columnas especificadas no existe");
break;
case 1064:
System.err.println("Error de sintaxis en la consulta SQL");
break;
default:
System.err.println("Error en la consulta [" + e.getErrorCode() + "]: " + e.getMessage());
}
}
return productos;
}Ahora que dominas las consultas SELECT, en el siguiente articulo aprenderas a insertar datos en MySQL desde Java.
Escrito por Eduardo Lázaro
