====== 04 - JDBC ======
**JDBC** (*Java Database Connectivity*) es la API estándar de Java para conectarse a bases de datos y ejecutar SQL. Cada motor (MariaDB, PostgreSQL...) proporciona un **driver** que la implementa.
===== 1. Conexión =====
La configuración se externaliza en un fichero de propiedades, no en el código:
db.url=jdbc:mariadb://localhost:3306/library
db.user=library_user
db.password=secret
public final class DbConnection {
private static final Properties PROPERTIES = loadProperties();
public static Connection get() throws SQLException {
return DriverManager.getConnection(
PROPERTIES.getProperty("db.url"),
PROPERTIES.getProperty("db.user"),
PROPERTIES.getProperty("db.password"));
}
}
''DriverManager.getConnection'' abre una conexión con el driver del classpath.
===== 2. PreparedStatement =====
String sql = "SELECT id, title, price FROM book WHERE publisher_id = ?";
try (Connection connection = DbConnection.get();
PreparedStatement statement = connection.prepareStatement(sql)) {
statement.setLong(1, publisherId);
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
long id = resultSet.getLong("id");
String title = resultSet.getString("title");
BigDecimal price = resultSet.getBigDecimal("price");
}
}
}
* El marcador ''?'' se sustituye con ''setLong'', ''setString''...
* El **parametrizado** evita la **inyección SQL**: el valor nunca se interpreta como parte de la sentencia. Concatenar SQL con datos del usuario es un error grave.
* ''try-with-resources'' cierra ''Connection'', ''PreparedStatement'' y ''ResultSet'' aunque ocurra una excepción.
===== 3. Tipos de sentencias =====
^ Método ^ Uso ^
| ''executeQuery()'' | ''SELECT''; devuelve ''ResultSet'' |
| ''executeUpdate()'' | ''INSERT''/''UPDATE''/''DELETE''; devuelve filas afectadas |
| ''execute()'' | Sentencias generales |
Para recuperar el ''id'' generado por un ''INSERT'':
try (PreparedStatement statement = connection.prepareStatement(sql,
Statement.RETURN_GENERATED_KEYS)) {
statement.setString(1, book.title());
statement.executeUpdate();
try (ResultSet keys = statement.getGeneratedKeys()) {
if (keys.next()) {
long id = keys.getLong(1);
}
}
}
===== 4. Transacciones =====
Por defecto cada sentencia se confirma al ejecutarse (**autocommit**). Cuando varias operaciones deben ser **atómicas** — todas o ninguna — se desactiva y se gestiona explícitamente:
Connection connection = DbConnection.get();
try {
connection.setAutoCommit(false);
// insertar pedido, actualizar stock...
connection.commit();
} catch (SQLException exception) {
connection.rollback();
throw exception;
} finally {
connection.setAutoCommit(true);
connection.close();
}
Si algo falla a mitad de camino, ''rollback()'' deshace todo lo pendiente.
===== 5. SQLException =====
''SQLException'' informa del problema: ''getErrorCode()'' y ''getSQLState()'' permiten distinguir, por ejemplo, una violación de clave única de un fallo de conexión. En la capa de persistencia se traduce a excepciones propias de la aplicación.
Una conexión abre y cierra por operación o por bloque lógico. Las conexiones son un recurso del servidor: mantenerlas abiertas innecesariamente agota la base de datos.