Taller: Operaciones CRUD#

Objetivos#

  1. Comprender cómo conectar una aplicación Java a una base de datos utilizando JDBC.

  2. Realizar operaciones CRUD (Crear, Leer, Actualizar, Eliminar) desde Java.

  3. Conocer las diferencias entre los métodos executeUpdate(), execute(), y prepareStatement().

Requisitos Previos#

  • Conocimiento básico de SQL.

  • Java SDK y MySQL (o cualquier gestor de bases de datos relacional).

1. Introducción a JDBC#

¿Qué es JDBC?#

JDBC es una API que permite a las aplicaciones Java interactuar con bases de datos relacionales. A través de JDBC, podemos ejecutar consultas SQL directamente desde código Java para manipular datos almacenados en bases de datos.

Componentes Clave:#

  • DriverManager: Gestiona la conexión a la base de datos.

  • Connection: Representa una conexión activa a la base de datos.

  • Statement: Se utiliza para ejecutar consultas SQL.

  • ResultSet: Almacena el resultado de una consulta SQL.

  • PreparedStatement: Una subclase de Statement que permite consultas precompiladas y más seguras.

Diferencias entre executeUpdate(), execute(), y prepareStatement()#

  • executeUpdate(): Este método se utiliza para ejecutar consultas que modifican la base de datos, como INSERT, UPDATE o DELETE. Retorna un entero que indica el número de filas afectadas por la consulta.

    int rowsAffected = statement.executeUpdate("UPDATE users SET age = 30 WHERE id = 1");
    
  • execute(): Este método se utiliza para ejecutar cualquier tipo de consulta SQL. Puede devolver diferentes tipos de resultados, dependiendo de la consulta (puede ser un ResultSet o un entero que indica cuántas filas fueron afectadas). Se usa cuando no sabes de antemano si tu consulta es una SELECT o una operación que modifica la base de datos.

    boolean hasResultSet = statement.execute("SELECT * FROM users");
    

    Retorna true si la consulta genera un ResultSet, o false si fue una operación como INSERT, UPDATE o DELETE.

  • prepareStatement(): Se utiliza para crear consultas SQL precompiladas y parametrizadas, que permiten mayor seguridad (evitando inyecciones SQL) y optimización. Las consultas precompiladas son más eficientes en consultas repetitivas.

    PreparedStatement pstmt = connection.prepareStatement("""INSERT INTO users (name, age) 
                                                             VALUES (?, ?)""");
    pstmt.setString(1, "Juan");
    pstmt.setInt(2, 25);
    pstmt.executeUpdate();
    

Implementación en Java#

Para utilizar los comandos de java.sql es necesario incorporar la dependencia de Maven com.mysql:mysql-connector-j. Al ser un notebook esta dependencia se carga con la siguiente casilla, así que es obligatorio ejecutarla.

// Necesario para que el kernel de jupyter cargue la dependencia mysql
// %maven com.mysql:mysql-connector-j:9.1.0
%%loadFromPOM
<!-- https://mvnrepository.com/artifact/org.json/json -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>9.1.0</version>
</dependency>

Conexión#

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DatabaseConnection {
    private static final String URL = "jdbc:mysql://localhost:3306/Universidad";
    private static final String USER = "dba";            // puede ser root
    private static final String PASSWORD = "$123456789"; //  contraseña del usuario

    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(URL, USER, PASSWORD);
    }

}
// Probando la conexión
new DatabaseConnection().getConnection();
com.mysql.cj.jdbc.ConnectionImpl@11c6758

Creación de la Tabla#

import java.sql.*;

//Creación de la tabla
public class Table {
    public static void Create() {
        String query = """
            CREATE TABLE IF NOT EXISTS usuarios (
                id INT AUTO_INCREMENT PRIMARY KEY,
                nombre VARCHAR(100) NOT NULL,
                edad int NOT NULL,
                fecha_nacimiento DATE NOT NULL
            );
            """;
        try (Connection connection = DatabaseConnection.getConnection();
             Statement stmt = connection.createStatement()) {
          
              // Usando el método executeUpdate
              int result = stmt.executeUpdate(query);
              System.out.println("executeUpdate: Se han afectado %d filas".formatted(result));

              // Usando el método execute
              boolean result1 = stmt.execute(query);
              System.out.println("execute: El resultado es de tipo resultSet %b".formatted(result1));
            
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void Delete() {
        String query = "DROP TABLE IF EXISTS usuarios;";
        try (Connection connection = DatabaseConnection.getConnection();
             Statement stmt = connection.createStatement()) {
          
                int result = stmt.executeUpdate(query);
                System.out.println("executeUpdate: Se han afectado %d filas".formatted(result));
            
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Delete();
        Create();
    }
  }
// ELiminando la tabla, si existe
new Table().Delete();
executeUpdate: Se han afectado 0 filas
// Creando la tabla
new Table().Create();
executeUpdate: Se han afectado 0 filas
execute: El resultado es de tipo resultSet false

Población de la Tabla#

//Poblar la tabla
import java.sql.*;

public class PoblateTable {

  public static void Poblate(){
    
    String query = """
        INSERT INTO usuarios (nombre, edad, fecha_nacimiento) VALUES 
                            ('Jhon Wayne', 35, "2000-12-18"),
                            ('Francisco Gomez', '28', "1990-05-28");
        """;
    try(Connection connection = DatabaseConnection.getConnection();
        Statement stmt = connection.createStatement();) {
          int result = stmt.executeUpdate(query);
          System.out.printf("Se actualizaron %d registros.".formatted(result));
    } catch (Exception e) {
      // TODO: handle exception
    }
  }

  public static void main(String[] args) {
    Poblate();
  }
}
new PoblateTable().Poblate();
Se actualizaron 2 registros.

2. Operaciones CRUD#

Para ejecutar los siguiente códigos deben primero agrega la clase DatabaseConnection.

a) Insertar Registros (INSERT)#

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class InsertRecord {
    public static void insertRecord(String name, int age, String fecha_nacimiento) {
        String query = "INSERT INTO usuarios (nombre, edad, fecha_nacimiento) VALUES (?, ?, ?)";
        try (Connection connection = DatabaseConnection.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(query)) {

            preparedStatement.setString(1, name);
            preparedStatement.setInt(2, age);
            preparedStatement.setString(3, fecha_nacimiento);

            int rowsInserted = preparedStatement.executeUpdate();
            System.out.println("Registros insertados: " + rowsInserted);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        insertRecord("Juan", 25, "2021-10-05");
    }
}
new InsertRecord().insertRecord("Juan", 25, "2021-10-05");
Registros insertados: 1

b) Consultar Registros (SELECT)#

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class SelectRecords {
    public static void selectAllRecords() {
        String query = "SELECT * FROM usuarios";
        try (Connection connection = DatabaseConnection.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(query)) {

            ResultSet resultSet = preparedStatement.executeQuery();
            
            String resultado = "%-7s%-20s%-10s%-22s%n".formatted("ID", "Nombre", "Edad", "Fecha de Nacimiento");
            
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String nombre = resultSet.getString("nombre");
                int edad = resultSet.getInt("edad");
                String fecha_nacimiento = resultSet.getString("fecha_nacimiento");
                resultado = resultado.concat("%-7d%-20s%-10d%-22s%n".formatted(id, nombre, edad, fecha_nacimiento));
            }
            System.out.println(resultado);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        selectAllRecords();
    }
}
new SelectRecords().main(null);
ID     Nombre              Edad      Fecha de Nacimiento   
1      Jhon Wayne          35        2000-12-18            
2      Francisco Gomez     28        1990-05-28            
3      Juan                25        2021-10-05            

c) Actualizar Registros (UPDATE)#

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class UpdateRecord {
    public static void updateRecord(int id, String newName, int newAge, String fecha_nacimiento) {
        // El comando WHERE id=? filtra los elementos por id, si se quiere por
        // otro campo es cambiar id por el de interés. No es necesario pasar todos los campos
        String query = "UPDATE usuarios SET nombre=?, edad=?, fecha_nacimiento=? WHERE id=?";
        try (Connection connection = DatabaseConnection.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(query)) {

            preparedStatement.setString(1, newName);
            preparedStatement.setInt(2, newAge);
            preparedStatement.setString(3, fecha_nacimiento);
            preparedStatement.setInt(4, id);

            int rowsUpdated = preparedStatement.executeUpdate();
            System.out.println("Registros actualizados: %d".formatted(rowsUpdated));
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        updateRecord(1, "Carlos", 30, "2001-02-24");
    }
}
new UpdateRecord().updateRecord(1, "Se", 30, "2001-05-12");
new SelectRecords().main(null);
Registros actualizados: 1
ID     Nombre              Edad      Fecha de Nacimiento   
1      Se                  30        2001-05-12            
2      Francisco Gomez     28        1990-05-28            
3      Juan                25        2021-10-05            

d) Eliminar Registros (DELETE)#

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class DeleteRecord {
    public static void deleteRecord(int id) {
        String query = "DELETE FROM usuarios WHERE id = ?";
        try (Connection connection = DatabaseConnection.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(query)) {

            preparedStatement.setInt(1, id);

            int rowsDeleted = preparedStatement.executeUpdate();
            System.out.println("Registros eliminados: %d".formatted(rowsDeleted));
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        deleteRecord(1);
    }
}
new DeleteRecord().deleteRecord(1);
Registros eliminados: 1

3. Mejores Prácticas#

  1. Uso de PreparedStatement: Siempre usa PreparedStatement en lugar de Statement para evitar inyecciones SQL, especialmente cuando trabajas con datos ingresados por el usuario.

  2. Cerrar Conexiones y Recursos: Usa bloques try-with-resources para asegurarte de que los recursos como Connection, PreparedStatement y ResultSet se cierren automáticamente, incluso si ocurre una excepción.

  3. Colocar todos los métodos que interactúen con la base de datos en el mismo paquete o clase.

4. Ejercicio Práctico: Base de Datos de Licores Colombianos#

Objetivo:#

Crear una base de datos para almacenar información de licores colombianos y realizar operaciones CRUD utilizando JDBC desde una aplicación Java.

1. Creación de la Base de Datos y Tablas en MySQL#

Primero, debes crear una base de datos llamada licores_colombianos con una tabla llamada licores. Cada licor debe tener un ID, nombre, tipo (ron, aguardiente, cerveza, etc.), grado de alcohol, y precio.

SQL para la creación de la base de datos:#

-- Crear la base de datos
CREATE DATABASE licores_colombianos;

-- Usar la base de datos creada
USE licores_colombianos;

-- Crear la tabla 'licores'
CREATE TABLE licores (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    tipo VARCHAR(50) NOT NULL,
    grado_alcohol FLOAT NOT NULL,
    precio DECIMAL(10, 2) NOT NULL
);

Explicación del SQL:#

  • Base de datos licores_colombianos: Es la base de datos donde almacenaremos toda la información de los licores.

  • Tabla licores:

    • id: Es el identificador único de cada licor, que se genera automáticamente con cada inserción.

    • nombre: Almacena el nombre del licor (ejemplo: «Aguardiente Antioqueño»).

    • tipo: Almacena el tipo de licor (ejemplo: «Aguardiente», «Ron»).

    • grado_alcohol: Almacena el porcentaje de alcohol por volumen (ejemplo: 29.5%).

    • precio: Almacena el precio del licor en la moneda local (COP).

2. Conector y Creación de la Tabla#

Adaptar los códigos de la sección de Implementación utilizando la tabla descrita y creada en la sección SQL para la creación de la base de datos. La creación de la base de datos es el único comando que debe ejecutarse en Workbrench o terminal, de resto los demás comandos pueden ejecutarse desde java.

3. Insertar Datos de Ejemplo#

Insertamos algunos datos iniciales en la tabla licores para comenzar a trabajar.

-- Insertar algunos registros en la tabla 'licores'
INSERT INTO licores (nombre, tipo, grado_alcohol, precio) 
VALUES ('Aguardiente Antioqueño', 'Aguardiente', 29.5, 25000),
       ('Ron Medellín', 'Ron', 37.5, 55000),
       ('Club Colombia', 'Cerveza', 4.7, 3000),
       ('Old Parr', 'Whisky', 40.0, 120000),
       ('Tequila 1800', 'Tequila', 38.0, 75000);

4. Operaciones CRUD para un Licorería#

Solución#

Hide code cell content
import java.sql.*;

public class DatabaseConnection {
    private static final String URL = "jdbc:mysql://localhost:3306/licores_colombianos";
    private static final String USER = "dba"; // Cambia 'root' por tu usuario de MySQL
    private static final String PASSWORD = "$123456789"; // Cambia 'yourpassword' por tu contraseña de MySQL

    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(URL, USER, PASSWORD);
    }
}

//Creación de la tabla
public class Table {
    public static void Create() {
        String query = """
            CREATE TABLE IF NOT EXISTS licores (
                id INT AUTO_INCREMENT PRIMARY KEY,
                nombre VARCHAR(100) NOT NULL,
                tipo VARCHAR(50) NOT NULL,
                grado_alcohol FLOAT NOT NULL,
                precio DECIMAL(10, 2) NOT NULL
            );
            """;
        try (Connection connection = DatabaseConnection.getConnection();
             Statement stmt = connection.createStatement()) {
          
              // Usando el método execute
              boolean result = stmt.execute(query);
              System.out.println("execute: El resultado es de tipo resultSet %b".formatted(result));
            
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void Delete() {
        String query = "DROP TABLE IF EXISTS licores;";
        try (Connection connection = DatabaseConnection.getConnection();
             Statement stmt = connection.createStatement()) {
          
                int result = stmt.executeUpdate(query);
                System.out.println("executeUpdate: Se han afectado %d filas".formatted(result));
            
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void Poblar(){
    
      String query = """
          INSERT INTO licores (nombre, tipo, grado_alcohol, precio) 
          VALUES ('Aguardiente Antioqueño', 'Aguardiente', 29.5, 25000),
                ('Ron Medellín', 'Ron', 37.5, 55000),
                ('Club Colombia', 'Cerveza', 4.7, 3000),
                ('Old Parr', 'Whisky', 40.0, 120000),
                ('Tequila 1800', 'Tequila', 38.0, 75000);
          """;
      try(Connection connection = DatabaseConnection.getConnection();
          Statement stmt = connection.createStatement();) {
            int result = stmt.executeUpdate(query);
            System.out.println("%d rows where updated.".formatted(result));
      } catch (Exception e) {
        // TODO: handle exception
      }
    }

    public static void main(String[] args) {
        Delete();
        Create();
        Poblar();
    }
  }

new Table().main(null);
executeUpdate: Se han afectado 0 filas
execute: El resultado es de tipo resultSet false
5 rows where updated.
Hide code cell content
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class CRUD_Licores {
    public static void Insertar(String nombre, String tipo, float gradoAlcohol, double precio) {
        String query = "INSERT INTO licores (nombre, tipo, grado_alcohol, precio) VALUES (?, ?, ?, ?)";
        try (Connection connection = DatabaseConnection.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(query)) {

            preparedStatement.setString(1, nombre);
            preparedStatement.setString(2, tipo);
            preparedStatement.setFloat(3, gradoAlcohol);
            preparedStatement.setDouble(4, precio);

            int rowsInserted = preparedStatement.executeUpdate();
            System.out.println("Licor insertado correctamente %d".formatted(rowsInserted));
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void Mostrar() {
        String query = "SELECT * FROM licores";
        try (Connection connection = DatabaseConnection.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(query)) {

            ResultSet resultSet = preparedStatement.executeQuery();
            
            String resultado = "%-7s%-30s%-20s%-22s%-15s%n".formatted("ID", "Nombre", "Tipo", "Grado de Alcohol", "Precio");
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String nombre = resultSet.getString("nombre");
                String tipo = resultSet.getString("tipo");
                float gradoAlcohol = resultSet.getFloat("grado_alcohol");
                double precio = resultSet.getDouble("precio");

                resultado = resultado.concat("%-7d%-30s%-20s%-22.2f%-15.2f%n".formatted(id, nombre, tipo, gradoAlcohol, precio));
                                
            }
            System.out.println(resultado);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void Actualizar(int id, double nuevoPrecio) {
        String query = "UPDATE licores SET precio = ? WHERE id = ?";
        try (Connection connection = DatabaseConnection.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(query)) {

            preparedStatement.setDouble(1, nuevoPrecio);
            preparedStatement.setInt(2, id);

            int rowsUpdated = preparedStatement.executeUpdate();
            System.out.println("Licor con id %d actualizado correctamente".formatted(rowsUpdated));
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void Eliminar(int id) {
        String query = "DELETE FROM licores WHERE id = ?";
        try (Connection connection = DatabaseConnection.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(query)) {

            preparedStatement.setInt(1, id);

            int rowsDeleted = preparedStatement.executeUpdate();
            System.out.println("Licor con id %d eliminado correctamente.".formatted(rowsDeleted));
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        
    }
}

a) Insertar un nuevo licor#

Este método permite agregar un nuevo licor a la tabla licores.

Hide code cell content
new CRUD_Licores().Insertar("Aguardiente Cristal", "Aguardiente", 29.0f, 24000);
Licor insertado correctamente 1

b) Consultar los licores#

Este método consulta y muestra todos los licores almacenados en la base de datos.

Hide code cell content
new CRUD_Licores().Mostrar();
ID     Nombre                        Tipo                Grado de Alcohol      Precio         
1      Aguardiente Antioqueño        Aguardiente         29.50                 25000.00       
2      Ron Medellín                  Ron                 37.50                 55000.00       
3      Club Colombia                 Cerveza             4.70                  3000.00        
4      Old Parr                      Whisky              40.00                 120000.00      
5      Tequila 1800                  Tequila             38.00                 75000.00       
6      Aguardiente Cristal           Aguardiente         29.00                 24000.00       

c) Actualizar información de un licor#

El siguiente código actualiza el precio de un licor específico.

Hide code cell content
new CRUD_Licores().Actualizar(1, 26000);
new CRUD_Licores().Mostrar();
Licor con id 1 actualizado correctamente
ID     Nombre                        Tipo                Grado de Alcohol      Precio         
1      Aguardiente Antioqueño        Aguardiente         29.50                 26000.00       
2      Ron Medellín                  Ron                 37.50                 55000.00       
3      Club Colombia                 Cerveza             4.70                  3000.00        
4      Old Parr                      Whisky              40.00                 120000.00      
5      Tequila 1800                  Tequila             38.00                 75000.00       
6      Aguardiente Cristal           Aguardiente         29.00                 24000.00       

d) Eliminar un licor#

Este código elimina un registro de la tabla licores por su ID.

Hide code cell content
new CRUD_Licores().Eliminar(3);
new CRUD_Licores().Mostrar();
Licor con id 1 eliminado correctamente.
ID     Nombre                        Tipo                Grado de Alcohol      Precio         
1      Aguardiente Antioqueño        Aguardiente         29.50                 26000.00       
2      Ron Medellín                  Ron                 37.50                 55000.00       
4      Old Parr                      Whisky              40.00                 120000.00      
5      Tequila 1800                  Tequila             38.00                 75000.00       
6      Aguardiente Cristal           Aguardiente         29.00                 24000.00       

Ejercicio#

Taller

Realizar todo el taller en un notebook de java o un proyecto para un tema de intereses: anime, peliculas, carros, motos, etc. La solución debe mostrar como se pueden realizar las operaciones de crear, leer, actualizar y eliminar, también conocidas como operaciones CRUD.