====== 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.