JDBC: guía de estudio
JDBC significa Java Database Connectivity. Es la API estándar de Java para conectarse a bases de datos relacionales, ejecutar sentencias…
JDBC: guía de estudio
JDBC significa Java Database Connectivity. Es la API estándar de Java para conectarse a bases de datos relacionales, ejecutar sentencias SQL, procesar resultados y manejar errores relacionados con el acceso a datos.
La idea central de JDBC es permitir que una aplicación Java pueda comunicarse con distintas bases de datos usando una API común. Java no necesita conocer internamente cómo funciona PostgreSQL, MySQL, Oracle o SQL Server. Para eso existen los drivers JDBC, que son las implementaciones específicas para cada base de datos.
JDBC se puede pensar como una capa intermedia entre el código Java y la base de datos. La aplicación Java usa interfaces y clases de JDBC, y el driver se encarga de traducir esas operaciones al protocolo específico de la base de datos.
Un flujo típico con JDBC sería:
- Registrar o cargar el driver JDBC.
- Obtener una conexión con la base de datos.
- Crear una sentencia SQL.
- Ejecutar la sentencia.
- Leer el resultado.
- Manejar errores si ocurren.
- Cerrar los recursos utilizados.
Aunque hoy muchas aplicaciones usan frameworks que simplifican este trabajo, JDBC sigue siendo una base importante para entender cómo Java accede a una base de datos relacional.
JDBC API
La JDBC API es el conjunto de clases e interfaces que Java ofrece para trabajar con bases de datos relacionales.
No es una base de datos. Tampoco es un driver. Es una API estándar. Define una forma común de conectarse, ejecutar SQL y procesar resultados.
Las clases e interfaces principales de JDBC se encuentran en el paquete java.sql. También existe el paquete javax.sql, donde aparece DataSource, muy usado en aplicaciones empresariales.
Algunos elementos importantes de la JDBC API son:
Connection representa una conexión activa con la base de datos.
Statement permite ejecutar una sentencia SQL simple.
PreparedStatement permite ejecutar una sentencia SQL parametrizada.
CallableStatement permite ejecutar stored procedures.
ResultSet representa el resultado devuelto por una consulta.
SQLException representa errores relacionados con JDBC.
DriverManager permite obtener conexiones usando drivers registrados.
Una forma correcta de explicarlo en un examen sería:
JDBC API es la API estándar de Java para acceder a bases de datos relacionales. Define clases e interfaces para abrir conexiones, ejecutar SQL, procesar resultados y manejar errores. La aplicación trabaja contra esta API, mientras que cada base de datos necesita un driver JDBC específico.
JDBC Driver
Un driver JDBC es una librería que permite que Java se comunique con una base de datos específica.
JDBC define la API común, pero cada base de datos tiene su propia forma de recibir conexiones, ejecutar consultas y devolver resultados. El driver JDBC se encarga de adaptar la API de Java al protocolo de esa base de datos.
Por ejemplo, si una aplicación Java se conecta a PostgreSQL, necesita el driver JDBC de PostgreSQL. Si se conecta a MySQL, necesita el driver JDBC de MySQL. Si se conecta a Oracle, necesita el driver JDBC de Oracle.
La aplicación no debería depender directamente de los detalles internos de cada base. La aplicación usa JDBC, y el driver resuelve la comunicación concreta.
Una forma simple de decirlo sería:
El driver JDBC es la implementación específica que permite que una aplicación Java se conecte a una base de datos determinada. JDBC define la API general, y el driver implementa la comunicación concreta con PostgreSQL, MySQL, Oracle, SQL Server u otra base.
Driver Registration
Driver Registration significa registrar el driver JDBC para que Java pueda usarlo al momento de crear una conexión.
En versiones antiguas de Java era común cargar manualmente la clase del driver. Por ejemplo:
Class.forName("org.postgresql.Driver");
Ese código cargaba la clase del driver de PostgreSQL y permitía que DriverManager pudiera usarlo para crear conexiones.
Después se podía pedir una conexión de esta forma:
String url = "jdbc:postgresql://localhost:5432/mydb";
String user = "myuser";
String password = "mypassword";
Connection connection = DriverManager.getConnection(url, user, password);
En aplicaciones modernas, muchas veces el registro del driver es automático. Si el driver está agregado como dependencia del proyecto, Java puede detectarlo mediante mecanismos internos de carga de servicios.
Para el examen, lo importante no es memorizar solo el Class.forName, sino entender la idea: antes de conectarse, Java necesita tener disponible un driver que sepa cómo hablar con esa base de datos.
Driver Registration es el proceso mediante el cual un driver JDBC queda disponible para que Java pueda usarlo al crear conexiones. Antes era común hacerlo manualmente con Class.forName. En versiones modernas, el driver suele registrarse automáticamente si está presente en el classpath.
Connection
Connection representa una conexión activa entre la aplicación Java y la base de datos.
A través de una conexión se pueden crear sentencias SQL, ejecutar consultas, modificar datos y manejar transacciones.
Ejemplo básico:
String url = "jdbc:postgresql://localhost:5432/mydb";
String user = "myuser";
String password = "mypassword";
Connection connection = DriverManager.getConnection(url, user, password);
En este ejemplo, DriverManager usa la URL JDBC para identificar qué driver debe utilizar. La URL comienza con jdbc, luego indica el tipo de base de datos y finalmente la ubicación de la base.
Una Connection no es simplemente una variable más. Es un recurso externo. Representa una comunicación abierta con la base de datos. Por eso debe cerrarse correctamente cuando ya no se usa.
Ejemplo:
connection.close();
Si las conexiones no se cierran, la aplicación puede quedarse sin conexiones disponibles. Eso puede provocar errores, lentitud o bloqueos.
Connection es el objeto de JDBC que representa una conexión activa con la base de datos. Permite crear sentencias SQL, ejecutar operaciones y manejar transacciones. Como es un recurso externo, debe cerrarse correctamente cuando ya no se utiliza.
Statement
Statement permite ejecutar sentencias SQL directamente desde Java.
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT id, name FROM users");
Statement puede servir para consultas simples, pero tiene una desventaja importante cuando se usan datos externos o valores ingresados por usuarios. Si se arma una consulta concatenando texto, puede aparecer un problema de seguridad conocido como SQL Injection.
Ejemplo incorrecto:
String name = request.getParameter("name");
String sql = "SELECT * FROM users WHERE name = '" + name + "'";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
El problema es que el valor de name queda pegado directamente dentro del SQL. Si ese valor viene manipulado, podría modificar el sentido de la consulta.
Por eso, cuando hay parámetros, normalmente se prefiere PreparedStatement.
PreparedStatement
PreparedStatement permite ejecutar SQL parametrizado.
En lugar de concatenar valores dentro del SQL, se usan placeholders representados con signos de pregunta.
Ejemplo:
String sql = "SELECT id, name, email FROM users WHERE id = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setLong(1, 10L);
ResultSet resultSet = preparedStatement.executeQuery();
El signo de pregunta representa un parámetro. Luego, con setLong, setString, setBoolean u otros métodos, se asigna el valor correspondiente.
La posición de los parámetros empieza en 1, no en 0.
PreparedStatement tiene varias ventajas.
Primero, ayuda a prevenir SQL Injection porque separa la estructura de la consulta de los valores.
Segundo, hace el código más claro.
Tercero, puede permitir optimizaciones cuando la misma consulta se ejecuta muchas veces con distintos valores.
Ejemplo con más de un parámetro:
String sql = "SELECT id, name FROM users WHERE active = ? AND age > ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setBoolean(1, true);
preparedStatement.setInt(2, 18);
ResultSet resultSet = preparedStatement.executeQuery();
PreparedStatement es una sentencia SQL precompilada o parametrizada. Permite usar placeholders en la consulta y asignar valores de forma segura. Es preferible a Statement cuando hay parámetros, porque evita concatenar valores dentro del SQL y ayuda a prevenir SQL Injection.
ResultSet
ResultSet representa el resultado de una consulta SQL.
Cuando se ejecuta un SELECT, la base de datos devuelve filas y columnas. JDBC permite recorrer ese resultado mediante un ResultSet.
Ejemplo:
String sql = "SELECT id, name, email FROM users";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
Long id = resultSet.getLong("id");
String name = resultSet.getString("name");
String email = resultSet.getString("email");
System.out.println(id + " - " + name + " - " + email);
}
El método next mueve el cursor a la siguiente fila.
Si existe una fila disponible, devuelve true.
Si no hay más filas, devuelve false.
Por eso se usa normalmente dentro de un while.
Para leer valores de las columnas, se usan métodos como getString, getLong, getInt, getBoolean, getDate o getTimestamp.
Los valores pueden obtenerse usando el nombre de la columna:
String name = resultSet.getString("name");
O usando la posición de la columna:
String name = resultSet.getString(2);
En general, usar el nombre de la columna suele ser más claro.
ResultSet es el objeto de JDBC que representa las filas devueltas por una consulta SELECT. Se recorre usando next y permite obtener los valores de cada columna mediante métodos como getString, getInt, getLong o getBoolean.
SQL Data Types
SQL Data Types son los tipos de datos que usa una base de datos para definir qué clase de información puede guardar cada columna.
Cuando se crea una tabla, cada columna tiene un tipo de dato. Ese tipo indica si la columna va a guardar texto, números, fechas, valores booleanos, datos binarios u otro tipo de información.
Por ejemplo, una tabla de usuarios podría tener columnas como id, name, email, active y created_at. Cada una de esas columnas necesita un tipo SQL.
Un ejemplo simple de tabla sería:
CREATE TABLE users (
id BIGINT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(150),
active BOOLEAN,
created_at TIMESTAMP
);
En ese ejemplo, id usa BIGINT porque representa un número grande. name y email usan VARCHAR porque guardan texto. active usa BOOLEAN porque representa verdadero o falso. created_at usa TIMESTAMP porque guarda fecha y hora.
Algunos tipos SQL comunes son:
VARCHAR se usa para texto de longitud variable.
CHAR se usa para texto de longitud fija.
TEXT se usa para textos largos.
INTEGER se usa para números enteros.
BIGINT se usa para números enteros grandes.
DECIMAL o NUMERIC se usan para valores numéricos exactos, por ejemplo dinero.
BOOLEAN se usa para valores verdadero o falso.
DATE se usa para fechas.
TIME se usa para horas.
TIMESTAMP se usa para fecha y hora.
BLOB se usa para datos binarios grandes.
CLOB se usa para texto grande.
El punto importante es que Java y SQL no usan exactamente los mismos tipos. Java tiene String, Integer, Long, Boolean, BigDecimal, LocalDate o LocalDateTime. SQL tiene VARCHAR, INTEGER, BIGINT, BOOLEAN, DECIMAL, DATE o TIMESTAMP.
Por eso aparece el concepto de mapping.
SQL Data Types son los tipos de datos definidos por la base de datos para representar la información de las columnas. Algunos ejemplos son VARCHAR para texto, INTEGER o BIGINT para números enteros, DECIMAL para números exactos, BOOLEAN para valores lógicos, DATE para fechas y TIMESTAMP para fecha y hora.
Mapping
Mapping significa convertir o relacionar un tipo de dato de la base de datos con un tipo de dato de Java.
Cuando una aplicación Java lee datos desde una base de datos, esos datos vienen con tipos SQL. Pero el programa Java necesita trabajar con tipos Java.
Por ejemplo, una columna VARCHAR normalmente se lee como String. Una columna BIGINT puede leerse como Long. Una columna BOOLEAN puede leerse como Boolean. Una columna TIMESTAMP puede convertirse en LocalDateTime o Timestamp.
Ejemplo:
SELECT id, name, active, created_at FROM users;
Esa consulta puede devolver una fila como esta:
id = 10
name = "Ana"
active = true
created_at = "2026-05-24 15:30:00"
En Java, esos valores podrían leerse así:
Long id = resultSet.getLong("id");
String name = resultSet.getString("name");
Boolean active = resultSet.getBoolean("active");
Timestamp createdAt = resultSet.getTimestamp("created_at");
El mapping aparece justamente en esa conversión.
El programador debe saber qué método usar para leer cada columna. Si la columna es texto, se puede usar getString. Si la columna es numérica, se puede usar getInt, getLong o getBigDecimal, según corresponda. Si la columna es booleana, se puede usar getBoolean. Si la columna representa fecha u hora, se pueden usar getDate, getTime o getTimestamp.
Ejemplo más completo:
while (resultSet.next()) {
Long id = resultSet.getLong("id");
String name = resultSet.getString("name");
Boolean active = resultSet.getBoolean("active");
BigDecimal salary = resultSet.getBigDecimal("salary");
Timestamp createdAt = resultSet.getTimestamp("created_at");
}
Un error común sería leer un dato con un tipo incorrecto. Por ejemplo, intentar leer una columna de texto como número puede provocar errores o resultados incorrectos.
También hay que tener cuidado con los valores NULL de la base de datos. Si una columna permite NULL, el valor puede no existir. En algunos casos, después de leer un valor se puede usar wasNull para verificar si el último valor leído era NULL.
Ejemplo:
int age = resultSet.getInt("age");
if (resultSet.wasNull()) {
System.out.println("La edad era NULL en la base de datos");
}
Esto es importante porque los tipos primitivos de Java, como int o boolean, no pueden representar null. En cambio, tipos como Integer o Boolean sí pueden hacerlo.
Mapping en JDBC es la conversión entre los tipos SQL de la base de datos y los tipos Java usados por la aplicación. Por ejemplo, VARCHAR suele mapearse a String, BIGINT a Long, BOOLEAN a Boolean, DECIMAL a BigDecimal y TIMESTAMP a Timestamp o LocalDateTime.
Transactions
Una transacción es un conjunto de operaciones que deben ejecutarse como una unidad lógica.
Esto significa que todas las operaciones de la transacción deben completarse correctamente, o ninguna debería quedar aplicada.
El ejemplo clásico es una transferencia bancaria. Si una operación resta dinero de una cuenta y otra operación suma dinero a otra cuenta, no puede ejecutarse solo una parte. Si se resta dinero de la primera cuenta pero falla la suma en la segunda, el sistema queda inconsistente.
La transacción permite evitar ese problema.
Una transacción puede terminar de dos formas principales: commit o rollback.
Commit significa confirmar los cambios. Cuando se hace commit, las operaciones realizadas dentro de la transacción quedan guardadas en la base de datos.
Rollback significa deshacer los cambios. Si ocurre un error, se puede hacer rollback para volver al estado anterior.
En JDBC, por defecto muchas conexiones trabajan con auto-commit activado. Eso significa que cada sentencia SQL se confirma automáticamente apenas se ejecuta.
Por ejemplo, si se ejecuta un UPDATE, la base confirma ese cambio inmediatamente.
Para manejar una transacción manualmente en JDBC, se desactiva el auto-commit.
Ejemplo:
connection.setAutoCommit(false);
Luego se ejecutan varias operaciones y, si todo sale bien, se hace commit.
connection.commit();
Si ocurre un error, se hace rollback.
connection.rollback();
Ejemplo completo:
try {
connection.setAutoCommit(false);
String debitSql = "UPDATE accounts SET balance = balance - ? WHERE id = ?";
PreparedStatement debitStatement = connection.prepareStatement(debitSql);
debitStatement.setBigDecimal(1, amount);
debitStatement.setLong(2, fromAccountId);
debitStatement.executeUpdate();
String creditSql = "UPDATE accounts SET balance = balance + ? WHERE id = ?";
PreparedStatement creditStatement = connection.prepareStatement(creditSql);
creditStatement.setBigDecimal(1, amount);
creditStatement.setLong(2, toAccountId);
creditStatement.executeUpdate();
connection.commit();
} catch (SQLException exception) {
connection.rollback();
throw exception;
}
En este ejemplo, la transacción incluye dos operaciones: restar saldo de una cuenta y sumar saldo a otra. Si ambas operaciones funcionan, se confirma la transacción. Si alguna falla, se deshacen los cambios.
Las transacciones suelen explicarse con el concepto ACID.
Atomicity significa que la transacción se ejecuta completa o no se ejecuta nada.
Consistency significa que la base de datos pasa de un estado válido a otro estado válido.
Isolation significa que las transacciones concurrentes no deberían interferir incorrectamente entre sí.
Durability significa que, una vez confirmado un cambio, debe permanecer guardado aunque luego ocurra una falla.
Una transacción es una unidad lógica de trabajo que agrupa una o más operaciones sobre la base de datos. En JDBC se puede manejar desactivando auto-commit con setAutoCommit(false), confirmando los cambios con commit y deshaciéndolos con rollback si ocurre un error.
Resource Allocation
Resource Allocation en JDBC se refiere a la administración de los recursos que se usan al trabajar con la base de datos.
Cuando una aplicación usa JDBC, no está trabajando solamente con objetos comunes de Java. También está usando recursos externos, como conexiones de red, cursores de base de datos, memoria del motor de base de datos y objetos asociados a la ejecución de consultas.
Los recursos principales son Connection, Statement, PreparedStatement y ResultSet.
Una Connection representa una conexión abierta con la base de datos.
Un Statement o PreparedStatement representa una sentencia SQL preparada para ejecutarse.
Un ResultSet representa el resultado abierto de una consulta.
Todos esos recursos deben cerrarse correctamente cuando ya no se necesitan.
Si no se cierran, pueden aparecer problemas. Por ejemplo, la aplicación puede quedarse sin conexiones disponibles, el pool de conexiones puede saturarse, la base puede mantener cursores abiertos innecesariamente o la aplicación puede consumir más memoria de la necesaria.
Una forma antigua de manejar recursos era cerrarlos manualmente en un bloque finally.
Ejemplo:
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = DriverManager.getConnection(url, user, password);
statement = connection.prepareStatement("SELECT id, name FROM users");
resultSet = statement.executeQuery();
while (resultSet.next()) {
System.out.println(resultSet.getString("name"));
}
} finally {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
}
El problema es que este código es largo y fácil de escribir mal.
La forma moderna en Java es usar try-with-resources. Este mecanismo cierra automáticamente los recursos al terminar el bloque.
Ejemplo:
String sql = "SELECT id, name FROM users";
try (
Connection connection = DriverManager.getConnection(url, user, password);
PreparedStatement statement = connection.prepareStatement(sql);
ResultSet resultSet = statement.executeQuery()
) {
while (resultSet.next()) {
System.out.println(resultSet.getString("name"));
}
}
Para que un objeto pueda usarse en try-with-resources, debe implementar AutoCloseable. Los objetos principales de JDBC lo implementan, por eso pueden cerrarse automáticamente.
Resource Allocation en JDBC es la gestión de recursos como conexiones, sentencias y resultados. Estos recursos deben cerrarse correctamente para evitar fugas de memoria, conexiones abiertas innecesarias o saturación del sistema. En Java moderno se suele usar try-with-resources para cerrarlos automáticamente.
JDBC Driver Types
Los JDBC Driver Types son una clasificación histórica de los distintos tipos de drivers JDBC.
Existen cuatro tipos principales: Type 1, Type 2, Type 3 y Type 4.
El Type 1 se conoce como JDBC-ODBC Bridge. Era un puente entre JDBC y ODBC. La aplicación Java usaba JDBC, pero por debajo se conectaba mediante ODBC. Este tipo de driver está obsoleto y ya no se usa en aplicaciones modernas.
El Type 2 se conoce como Native API Driver. Este tipo de driver usa librerías nativas del proveedor de la base de datos. Eso significa que requiere componentes instalados en la máquina donde corre la aplicación. Tiene la desventaja de depender del sistema operativo y de librerías externas.
El Type 3 se conoce como Network Protocol Driver. En este caso, el driver JDBC no se comunica directamente con la base de datos, sino con un servidor intermedio o middleware. Ese servidor intermedio traduce la comunicación hacia la base de datos. Puede servir en arquitecturas empresariales, pero agrega una capa adicional.
El Type 4 se conoce como Thin Driver o Pure Java Driver. Es un driver escrito completamente en Java que se comunica directamente con la base de datos usando el protocolo propio de esa base. Es el tipo más común en aplicaciones modernas.
Por ejemplo, muchos drivers actuales de PostgreSQL, MySQL o SQL Server funcionan como drivers Type 4.
La idea más importante para recordar es que el Type 4 es el más usado actualmente porque es portable, no necesita librerías nativas y se conecta directamente con la base.
JDBC define cuatro tipos históricos de drivers. Type 1 es el puente JDBC-ODBC y está obsoleto. Type 2 usa librerías nativas. Type 3 usa un servidor intermedio. Type 4 es un driver puro Java que se comunica directamente con la base de datos y es el más usado actualmente.
Binary Object
Binary Object hace referencia a datos binarios almacenados en la base de datos.
En una base relacional no siempre se guardan solamente textos, números o fechas. También pueden guardarse archivos o contenido binario, como imágenes, documentos PDF, audios, videos o archivos comprimidos.
Para ese tipo de información se usa normalmente BLOB.
BLOB significa Binary Large Object. Es un tipo de dato pensado para almacenar datos binarios grandes.
Por ejemplo, una tabla podría guardar archivos de esta manera:
CREATE TABLE files (
id BIGINT PRIMARY KEY,
file_name VARCHAR(255),
content BLOB
);
En JDBC, un BLOB puede manejarse usando byte arrays, streams o la interfaz Blob.
Ejemplo usando byte[]:
String sql = "INSERT INTO files (id, file_name, content) VALUES (?, ?, ?)";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setLong(1, 1L);
statement.setString(2, "document.pdf");
statement.setBytes(3, fileBytes);
statement.executeUpdate();
También puede leerse un archivo binario desde la base:
String sql = "SELECT content FROM files WHERE id = ?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setLong(1, 1L);
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
byte[] fileBytes = resultSet.getBytes("content");
}
Además de BLOB existe CLOB.
CLOB significa Character Large Object. Se usa para almacenar texto grande, no datos binarios. Por ejemplo, documentos largos, contenido HTML, XML o texto extenso.
La diferencia principal es que BLOB se usa para datos binarios y CLOB para grandes cantidades de texto.
Un Binary Object en JDBC suele referirse a un BLOB, que es un Binary Large Object. Se usa para almacenar datos binarios grandes, como imágenes, PDFs o archivos. JDBC permite escribirlos y leerlos usando byte[], Blob o streams. Para texto grande existe CLOB, que representa Character Large Object.
JDBC Exception Handling
JDBC Exception Handling se refiere al manejo de errores que pueden ocurrir al trabajar con JDBC.
La excepción principal de JDBC es SQLException.
SQLException es una checked exception. Eso significa que Java obliga a capturarla con catch o declararla con throws.
Los errores pueden aparecer por muchos motivos: problemas de conexión, credenciales incorrectas, SQL mal escrito, tablas inexistentes, columnas inexistentes, violación de constraints, claves duplicadas, timeouts, deadlocks o problemas de permisos.
Ejemplo básico:
try {
String sql = "SELECT id, name FROM users";
PreparedStatement statement = connection.prepareStatement(sql);
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
System.out.println(resultSet.getString("name"));
}
} catch (SQLException exception) {
System.out.println("Error al ejecutar la consulta");
System.out.println(exception.getMessage());
}
SQLException contiene información útil sobre el error.
Por ejemplo:
catch (SQLException exception) {
System.out.println("Message: " + exception.getMessage());
System.out.println("SQLState: " + exception.getSQLState());
System.out.println("ErrorCode: " + exception.getErrorCode());
}
getMessage devuelve una descripción del error.
getSQLState devuelve un código estándar asociado al tipo de error.
getErrorCode devuelve un código específico del proveedor de la base de datos.
También puede haber una cadena de excepciones. En algunos casos, una SQLException puede estar relacionada con otra excepción interna.
Ejemplo:
SQLException nextException = exception.getNextException();
En JDBC puro, el manejo de excepciones es importante porque también puede afectar las transacciones. Si ocurre un error dentro de una transacción, normalmente se debe hacer rollback.
Ejemplo:
try {
connection.setAutoCommit(false);
PreparedStatement statement = connection.prepareStatement(
"UPDATE users SET active = ? WHERE id = ?"
);
statement.setBoolean(1, false);
statement.setLong(2, 10L);
statement.executeUpdate();
connection.commit();
} catch (SQLException exception) {
connection.rollback();
throw exception;
}
En este caso, si ocurre un error antes del commit, se ejecuta rollback para evitar que la base quede en un estado incorrecto.
JDBC maneja errores principalmente mediante SQLException. Esta excepción puede representar problemas de conexión, errores de SQL, violaciones de constraints, timeouts o problemas de permisos. SQLException permite obtener información como el mensaje, SQLState y código de error. Cuando el error ocurre dentro de una transacción, normalmente se debe hacer rollback.
Ampliación y orden conceptual de JDBC
Para completar el estudio de JDBC, conviene entender algunos temas que amplían la visión básica de conexión, ejecución de consultas y lectura de resultados.
JDBC no se limita solamente a abrir una conexión y ejecutar una consulta. También incluye decisiones sobre cómo obtener conexiones, cómo reutilizarlas, cómo ejecutar operaciones de forma eficiente, cómo manejar transacciones concurrentes, cómo inspeccionar metadata y cómo evitar problemas de seguridad.
Uno de los conceptos más importantes para completar esta visión es DataSource.
DataSource es una alternativa más flexible a DriverManager para obtener conexiones a la base de datos. DriverManager permite crear una conexión directamente usando una URL JDBC, usuario y contraseña. Eso sirve para ejemplos simples, pruebas o programas chicos. Pero en aplicaciones más reales suele preferirse DataSource.
La ventaja de DataSource es que puede integrarse con un pool de conexiones.
Un pool de conexiones es un conjunto de conexiones abiertas y reutilizables. Abrir una conexión nueva contra una base de datos puede ser costoso, porque implica comunicación de red, autenticación y creación de recursos en el motor de base de datos. Por eso, en lugar de abrir y cerrar conexiones constantemente, una aplicación puede tomar una conexión disponible del pool, usarla y devolverla al pool cuando termina.
Esto mejora el rendimiento y evita crear conexiones nuevas para cada operación.
Otro concepto importante es batch processing.
Batch processing permite agrupar varias operaciones SQL y ejecutarlas juntas. Es útil cuando se necesitan hacer muchas inserciones, actualizaciones o eliminaciones similares.
En vez de enviar una operación a la base de datos por cada registro, se acumulan varias operaciones en un lote y luego se ejecutan juntas.
Código de ejemplo:
String sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setLong(1, 1L);
statement.setString(2, "Ana");
statement.setString(3, "ana@mail.com");
statement.addBatch();
statement.setLong(1, 2L);
statement.setString(2, "Luis");
statement.setString(3, "luis@mail.com");
statement.addBatch();
int[] results = statement.executeBatch();
addBatch agrega una operación al lote.
executeBatch ejecuta todas las operaciones acumuladas.
Este mecanismo puede mejorar el rendimiento cuando se trabaja con grandes volúmenes de datos.
También es importante conocer CallableStatement.
CallableStatement se usa para ejecutar stored procedures.
Código de ejemplo:
CallableStatement statement = connection.prepareCall("{call update_user_status(?, ?)}");
statement.setLong(1, 10L);
statement.setBoolean(2, false);
statement.execute();
}
En transacciones, además de commit y rollback, conviene conocer el concepto de isolation level.
El nivel de aislamiento define cómo se comportan las transacciones cuando varias operaciones se ejecutan al mismo tiempo.
Este tema es importante porque en una base de datos real puede haber muchos usuarios o procesos modificando información de manera concurrente. Si no hay control, una transacción podría leer datos inconsistentes.
Un problema posible es dirty read. Ocurre cuando una transacción lee datos modificados por otra transacción que todavía no hizo commit. Si esa otra transacción luego hace rollback, la primera transacción leyó datos que nunca quedaron confirmados.
Otro problema es non-repeatable read. Ocurre cuando una transacción lee dos veces la misma fila y obtiene valores distintos porque otra transacción modificó esa fila entre ambas lecturas.
También existe phantom read. Ocurre cuando una transacción repite una consulta y aparecen o desaparecen filas porque otra transacción insertó o eliminó datos que cumplen la condición de búsqueda.
JDBC permite configurar el nivel de aislamiento de una conexión.
Código de ejemplo:
connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
Algunos niveles comunes son READ_COMMITTED, REPEATABLE_READ y SERIALIZABLE.
READ_COMMITTED evita leer datos no confirmados.
REPEATABLE_READ busca que una misma fila leída dentro de una transacción no cambie durante esa transacción.
SERIALIZABLE es el nivel más estricto, porque intenta que las transacciones se comporten como si se ejecutaran una después de la otra.
Cuanto mayor es el aislamiento, mayor consistencia puede lograrse, pero también puede haber más bloqueos o menor rendimiento.
Otro concepto relacionado con transacciones es savepoint.
Un savepoint es un punto intermedio dentro de una transacción. Permite volver a ese punto si ocurre un problema, sin cancelar necesariamente toda la transacción.
Código de ejemplo:
connection.setAutoCommit(false);
Savepoint savepoint = connection.setSavepoint("before_update");
// operaciones SQL
connection.rollback(savepoint);
Con rollback común se revierte toda la transacción. Con rollback hacia un savepoint se puede deshacer solo lo ocurrido después de ese punto.
Síntesis final para estudiar JDBC
JDBC significa Java Database Connectivity y forma parte de la plataforma Java como una API estándar para acceder a bases de datos relacionales.
Más precisamente, JDBC define un conjunto de clases, interfaces y reglas de uso que permiten que una aplicación Java pueda ejecutar operaciones SQL contra una base de datos. No describe cómo funciona internamente una base de datos, no define cómo se almacenan los datos en disco y no reemplaza al motor de base de datos. Su objetivo es estandarizar, desde Java, la forma de conectarse, enviar sentencias SQL, recibir resultados, manejar transacciones y tratar errores.
La idea principal es separar la aplicación Java del detalle particular de cada proveedor de base de datos. El código Java trabaja contra la API JDBC, mientras que la comunicación concreta con PostgreSQL, MySQL, Oracle, SQL Server u otro motor depende del driver JDBC correspondiente.
Por eso, JDBC se apoya en dos partes importantes.
Por un lado, está la API que Java define: Connection, Statement, PreparedStatement, CallableStatement, ResultSet, SQLException, DriverManager, DataSource y otras interfaces o clases relacionadas.
Por otro lado, está el driver JDBC, que es la implementación específica que permite comunicarse con una base de datos concreta. Sin el driver, la aplicación puede conocer la API, pero no tendría cómo hablar realmente con el motor elegido.
El flujo general de JDBC puede entenderse de esta manera: primero se dispone de un driver compatible, luego se obtiene una conexión, después se prepara una sentencia SQL, se ejecuta la operación, se procesan los resultados si existen, se manejan transacciones cuando corresponde, se liberan los recursos utilizados y se capturan posibles errores.
Connection representa la conexión activa con la base de datos. Statement, PreparedStatement y CallableStatement representan distintas formas de ejecutar SQL. ResultSet permite leer los datos devueltos por una consulta. El mapping permite convertir valores SQL a tipos Java. Las transacciones permiten confirmar o revertir un conjunto de operaciones. El manejo de recursos evita dejar conexiones, sentencias o resultados abiertos. SQLException permite tratar errores propios del acceso a datos.
También es importante entender que JDBC no debe estudiarse como una lista aislada de clases. Tiene sentido cuando se lo ve como un modelo completo de acceso relacional desde Java. Ese modelo incluye conexión, ejecución de SQL, lectura de resultados, conversión de tipos, control transaccional, administración de recursos y manejo de excepciones.
Una forma sólida de responder en una evaluación sería:
JDBC es la API estándar de Java para acceder a bases de datos relacionales mediante SQL. Define clases e interfaces que permiten obtener conexiones, ejecutar sentencias, procesar resultados, manejar transacciones, liberar recursos y capturar errores. La comunicación concreta con cada base de datos depende de un driver JDBC específico. Por eso, JDBC no explica el funcionamiento interno de una base de datos, sino que define el modelo que Java utiliza para operar contra bases relacionales desde una aplicación.
Con esta idea, los conceptos principales quedan conectados: el driver permite la comunicación con una base concreta, Connection representa el vínculo activo, PreparedStatement permite ejecutar SQL parametrizado, ResultSet permite leer resultados, los tipos SQL se mapean a tipos Java, las transacciones se controlan con commit y rollback, los recursos deben cerrarse correctamente y los errores se manejan mediante SQLException.
Ese es el núcleo de JDBC: una especificación y API de Java para organizar el acceso a datos relacionales de forma estándar, independientemente del motor de base de datos utilizado.
메타데이터
- post_id
- e4b7dcd972ab
- slug
- jdbc-guía-de-estudio-e4b7dcd972ab
- url
- https://medium.com/@barbieri.santiago/jdbc-gu%C3%ADa-de-estudio-e4b7dcd972ab
- canonical_url
- https://medium.com/@barbieri.santiago/jdbc-gu%C3%ADa-de-estudio-e4b7dcd972ab
- author_url
- https://medium.com/@barbieri.santiago
- status
- ok
- fetched_at
- 2026-06-17 18:03:35