Taller: Operaciones CRUD#
Objetivos#
Comprender cómo conectar una aplicación Java a una base de datos utilizando JDBC.
Realizar operaciones CRUD (Crear, Leer, Actualizar, Eliminar) desde Java.
Conocer las diferencias entre los métodos
executeUpdate()
,execute()
, yprepareStatement()
.
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, comoINSERT
,UPDATE
oDELETE
. 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 unResultSet
o un entero que indica cuántas filas fueron afectadas). Se usa cuando no sabes de antemano si tu consulta es unaSELECT
o una operación que modifica la base de datos.boolean hasResultSet = statement.execute("SELECT * FROM users");
Retorna
true
si la consulta genera unResultSet
, ofalse
si fue una operación comoINSERT
,UPDATE
oDELETE
.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#
Uso de
PreparedStatement
: Siempre usaPreparedStatement
en lugar deStatement
para evitar inyecciones SQL, especialmente cuando trabajas con datos ingresados por el usuario.Cerrar Conexiones y Recursos: Usa bloques
try-with-resources
para asegurarte de que los recursos comoConnection
,PreparedStatement
yResultSet
se cierren automáticamente, incluso si ocurre una excepción.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#
Show 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.
Show 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
.
Show 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.
Show 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.
Show 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.
Show 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.