Introducción a JDBC

JDBC (Java Database Connectivity) es una API de Java que permite a las aplicaciones interactuar con bases de datos mediante el estándar SQL. Aprenderás desde la conexión básica hasta transacciones avanzadas.

Conexión

Establecer y manejar conexiones a diferentes bases de datos

Consultas

Ejecutar SELECT, INSERT, UPDATE, DELETE

PreparedStatement

Consultas parametrizadas para prevenir SQL Injection

Transacciones

Manejo de ACID: Atomicidad, Consistencia, Aislamiento, Durabilidad

Pool de Conexiones

Optimización de recursos en aplicaciones empresariales

Arquitectura JDBC

Aplicación Java

Usa JDBC API

Driver Manager

Gestiona drivers JDBC

Driver JDBC

Conector específico (MySQL, PostgreSQL, etc.)

Base de Datos

MySQL, Oracle, SQL Server, etc.

Drivers Comunes

mysql-connector-java

postgresql

ojdbc

mssql-jdbc

Establecer Conexión

Simulador de Conexión


// Ejemplo de conexión básica
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class ConexionBD {
    private static final String URL = "jdbc:mysql://localhost:3306/mi_base_datos";
    private static final String USER = "root";
    private static final String PASSWORD = "password123";
    
    public Connection conectar() {
        Connection conexion = null;
        try {
            // Cargar driver (opcional en JDBC 4.0+)
            Class.forName("com.mysql.cj.jdbc.Driver");
            
            // Establecer conexión
            conexion = DriverManager.getConnection(URL, USER, PASSWORD);
            
            if (conexion != null) {
                System.out.println("✅ Conexión exitosa a MySQL");
            }
        } catch (ClassNotFoundException e) {
            System.err.println("❌ Driver no encontrado: " + e.getMessage());
        } catch (SQLException e) {
            System.err.println("❌ Error de conexión: " + e.getMessage());
        }
        return conexion;
    }
}

Operaciones CRUD

Simulador de Tabla de Usuarios

Tabla: usuarios
ID Nombre Email Edad Acciones
Consultar Usuarios
// Consultar todos los usuarios
public List obtenerUsuarios() {
    List usuarios = new ArrayList<>();
    String sql = "SELECT * FROM usuarios";
    
    try (Connection conn = getConnection();
         Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery(sql)) {
        
        while (rs.next()) {
            Usuario usuario = new Usuario();
            usuario.setId(rs.getInt("id"));
            usuario.setNombre(rs.getString("nombre"));
            usuario.setEmail(rs.getString("email"));
            usuario.setEdad(rs.getInt("edad"));
            usuarios.add(usuario);
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return usuarios;
}
Insertar Nuevo Usuario
// Insertar un usuario
public void insertarUsuario(Usuario usuario) {
    String sql = "INSERT INTO usuarios (nombre, email, edad) VALUES (?, ?, ?)";
    
    try (Connection conn = getConnection();
         PreparedStatement pstmt = conn.prepareStatement(sql)) {
        
        pstmt.setString(1, usuario.getNombre());
        pstmt.setString(2, usuario.getEmail());
        pstmt.setInt(3, usuario.getEdad());
        
        int filasAfectadas = pstmt.executeUpdate();
        System.out.println("Filas insertadas: " + filasAfectadas);
        
    } catch (SQLException e) {
        e.printStackTrace();
    }
}
Actualizar Usuario
// Actualizar usuario
public void actualizarUsuario(int id, Usuario usuario) {
    String sql = "UPDATE usuarios SET nombre = ?, email = ?, edad = ? WHERE id = ?";
    
    try (Connection conn = getConnection();
         PreparedStatement pstmt = conn.prepareStatement(sql)) {
        
        pstmt.setString(1, usuario.getNombre());
        pstmt.setString(2, usuario.getEmail());
        pstmt.setInt(3, usuario.getEdad());
        pstmt.setInt(4, id);
        
        int filasAfectadas = pstmt.executeUpdate();
        System.out.println("Filas actualizadas: " + filasAfectadas);
        
    } catch (SQLException e) {
        e.printStackTrace();
    }
}
Eliminar Usuario
// Eliminar usuario
public void eliminarUsuario(int id) {
    String sql = "DELETE FROM usuarios WHERE id = ?";
    
    try (Connection conn = getConnection();
         PreparedStatement pstmt = conn.prepareStatement(sql)) {
        
        pstmt.setInt(1, id);
        
        int filasAfectadas = pstmt.executeUpdate();
        if (filasAfectadas > 0) {
            System.out.println("Usuario eliminado: ID " + id);
        } else {
            System.out.println("No se encontró usuario con ID " + id);
        }
        
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

PreparedStatement

Prevención de SQL Injection

❌ VULNERABLE (Statement)
// ❌ VULNERABLE A SQL INJECTION
public boolean loginVulnerable(String usuario, String password) {
    String sql = "SELECT * FROM usuarios WHERE username = '" + usuario + 
                 "' AND password = '" + password + "'";
    
    try (Statement stmt = connection.createStatement();
         ResultSet rs = stmt.executeQuery(sql)) {
        return rs.next(); // Si encuentra usuario
    } catch (SQLException e) {
        e.printStackTrace();
        return false;
    }
}

// Ejemplo de ataque:
// usuario: admin' --
// password: (cualquier cosa)
// SQL resultante: SELECT * FROM usuarios WHERE username = 'admin' --' AND password = ''
Prueba de Inyección
✅ SEGURO (PreparedStatement)
// ✅ SEGURO CONTRA SQL INJECTION
public boolean loginSeguro(String usuario, String password) {
    String sql = "SELECT * FROM usuarios WHERE username = ? AND password = ?";
    
    try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
        pstmt.setString(1, usuario);  // Los parámetros se escapan automáticamente
        pstmt.setString(2, password);
        
        try (ResultSet rs = pstmt.executeQuery()) {
            return rs.next();
        }
    } catch (SQLException e) {
        e.printStackTrace();
        return false;
    }
}

// Mismo ataque:
// usuario: admin' --
// password: (cualquier cosa)
// SQL resultante: SELECT * FROM usuarios WHERE username = 'admin'' --' AND password = ''

Prevención automática de SQL Injection

Mejor rendimiento (precompilación)

Código más limpio y mantenible

Generador de PreparedStatement

Transacciones

Simulador de Transacciones Bancarias

Cuentas Bancarias
Realizar Transferencia
Modo de Ejecución:
✅ CON TRANSACCIÓN
// Transferencia con transacción
public boolean transferirConTransaccion(int origen, int destino, double monto) {
    Connection conn = null;
    try {
        conn = getConnection();
        conn.setAutoCommit(false); // Iniciar transacción
        
        // 1. Retirar de cuenta origen
        String sqlRetiro = "UPDATE cuentas SET saldo = saldo - ? WHERE id = ? AND saldo >= ?";
        try (PreparedStatement pstmt = conn.prepareStatement(sqlRetiro)) {
            pstmt.setDouble(1, monto);
            pstmt.setInt(2, origen);
            pstmt.setDouble(3, monto);
            int filas = pstmt.executeUpdate();
            
            if (filas == 0) {
                conn.rollback();
                return false; // Saldo insuficiente
            }
        }
        
        // 2. Depositar en cuenta destino
        String sqlDeposito = "UPDATE cuentas SET saldo = saldo + ? WHERE id = ?";
        try (PreparedStatement pstmt = conn.prepareStatement(sqlDeposito)) {
            pstmt.setDouble(1, monto);
            pstmt.setInt(2, destino);
            pstmt.executeUpdate();
        }
        
        conn.commit(); // Confirmar transacción
        return true;
        
    } catch (SQLException e) {
        if (conn != null) {
            try { conn.rollback(); } catch (SQLException ex) {}
        }
        e.printStackTrace();
        return false;
    } finally {
        if (conn != null) {
            try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) {}
        }
    }
}
❌ SIN TRANSACCIÓN
// Transferencia sin transacción - PELIGROSO
public boolean transferirSinTransaccion(int origen, int destino, double monto) {
    try (Connection conn = getConnection()) {
        conn.setAutoCommit(true); // Cada SQL es independiente
        
        // 1. Retirar de cuenta origen
        String sqlRetiro = "UPDATE cuentas SET saldo = saldo - ? WHERE id = ? AND saldo >= ?";
        try (PreparedStatement pstmt = conn.prepareStatement(sqlRetiro)) {
            pstmt.setDouble(1, monto);
            pstmt.setInt(2, origen);
            pstmt.setDouble(3, monto);
            int filas = pstmt.executeUpdate();
            
            if (filas == 0) {
                return false; // Saldo insuficiente
            }
        }
        
        // ⚠️ Si hay un error aquí, el dinero ya se retiró pero no se depositó
        // ⚠️ Resultado: Dinero perdido en el limbo
        
        // 2. Depositar en cuenta destino
        String sqlDeposito = "UPDATE cuentas SET saldo = saldo + ? WHERE id = ?";
        try (PreparedStatement pstmt = conn.prepareStatement(sqlDeposito)) {
            pstmt.setDouble(1, monto);
            pstmt.setInt(2, destino);
            pstmt.executeUpdate();
        }
        
        return true;
        
    } catch (SQLException e) {
        e.printStackTrace();
        return false;
    }
}

Pool de Conexiones

Optimización de Recursos

Pool de Conexiones (HikariCP)
Activas: 0
Idle: 10
Máximo: 20
Aplicación
Request 1
Request 2
Request 3
Request 4
Configuración HikariCP
// Configuración HikariCP
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

public class DatabaseConfig {
    
    private static HikariDataSource dataSource;
    
    static {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/mi_base_datos");
        config.setUsername("root");
        config.setPassword("password123");
        
        // Configuración del pool
        config.setMaximumPoolSize(20);
        config.setMinimumIdle(10);
        config.setConnectionTimeout(30000); // 30 segundos
        config.setIdleTimeout(600000); // 10 minutos
        config.setMaxLifetime(1800000); // 30 minutos
        config.setConnectionTestQuery("SELECT 1");
        
        dataSource = new HikariDataSource(config);
    }
    
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
    
    public static void closeDataSource() {
        if (dataSource != null && !dataSource.isClosed()) {
            dataSource.close();
        }
    }
}
Ventajas del Pool de Conexiones
Rendimiento

Reutilización de conexiones existentes en lugar de crear nuevas

Escalabilidad

Manejo eficiente de múltiples solicitudes concurrentes

Estabilidad

Prevención de agotamiento de conexiones de base de datos

Gestión

Configuración centralizada de parámetros de conexión

Ejercicios Prácticos

Sistema de Gestión de Usuarios

Crea un sistema CRUD completo para gestión de usuarios con:

  • Conexión a base de datos MySQL/PostgreSQL
  • Operaciones CRUD con PreparedStatement
  • Validación de datos y manejo de excepciones
  • Búsqueda y filtrado avanzado
  • Exportación a CSV

Sistema de Ventas

Implementa un sistema de ventas con transacciones:

  • Tablas relacionadas: productos, clientes, ventas, detalles
  • Transacciones para ventas (ACID)
  • Manejo de inventario concurrente
  • Reportes con JOINs complejos
  • Backup automático de datos

Sistema de Análisis de Datos

Desarrolla un sistema para análisis de grandes volúmenes:

  • Conexión pool con HikariCP
  • Procesamiento por lotes (Batch Processing)
  • Consultas optimizadas con índices
  • Manejo de conexiones en aplicaciones web
  • Monitoreo de rendimiento

Editor de Código

Resumen y Recursos

Lo Aprendido en Este Módulo

Conexión a BD
  • DriverManager para establecer conexiones
  • Configuración de URL, usuario y contraseña
  • Manejo de excepciones SQLException
  • Cierre adecuado de recursos
Operaciones CRUD
  • Statement para consultas simples
  • ResultSet para procesar resultados
  • executeQuery() para SELECT
  • executeUpdate() para INSERT/UPDATE/DELETE
PreparedStatement
  • Prevención de SQL Injection
  • Mejor rendimiento por precompilación
  • setXXX() métodos para parámetros
  • Reutilización de consultas
Transacciones
  • setAutoCommit(false)
  • commit() y rollback()
  • Propiedades ACID
  • Manejo de errores en transacciones
Pool de Conexiones
  • Optimización de recursos
  • HikariCP como solución estándar
  • Configuración de parámetros
  • Manejo en aplicaciones web

Descargar Recursos

Evaluación de Conocimientos

Comprueba lo que has aprendido con nuestro quiz interactivo