Devolver un libro
Eres desarrollador del sistema de gestión de la Biblioteca Pública Metropolitana. La semana pasada se descubrió un error crítico: cuando el sistema se bloqueaba durante la devolución de un libro, el préstamo se marcaba como "returned", pero el libro seguía como "checked-out" en el inventario. Esto dejaba el libro no disponible durante semanas hasta que el personal corregía la base de datos manualmente.
Cuando se devuelve un libro, tres operaciones deben tener éxito juntas o fallar juntas:
- Actualizar el registro del préstamo al estado "returned".
- Cambiar la disponibilidad del libro de "checked-out" a "available".
- Registrar una multa si el libro se devuelve con retraso.
Tu tarea es implementar un control de transacciones adecuado para garantizar la consistencia de los datos.
Este ejercicio forma parte del curso
Consultas a una base de datos PostgreSQL en Java
Instrucciones del ejercicio
- Establece
autoCommitenfalseal principio en la línea 26. - Confirma la transacción si todas las operaciones tienen éxito en la línea 59.
- Si alguna operación falla, revierte la transacción en la línea 66.
Ejercicio interactivo práctico
Prueba este ejercicio y completa el código de muestra.
public class BookReturnProcessor {
public static void main(String[] args) {
int loanId = 1;
int bookId = 5;
LocalDate dueDate = LocalDate.now().minusDays(2);
try {
boolean success = processBookReturn(loanId, bookId, dueDate);
if (success) {
System.out.println("Book return processed successfully.");
} else {
System.out.println("Book return processing failed.");
}
} catch (SQLException e) {
System.err.println("Database error: " + e.getMessage());
}
}
public static boolean processBookReturn(int loanId, int bookId, LocalDate dueDate) throws SQLException {
Connection conn = null;
try {
HikariDataSource ds = HikariSetup.createDataSource();
conn = ds.getConnection();
// Start transaction by setting autoCommit to false
conn.____(____);
String updateLoanSQL = "UPDATE loans SET status = 'returned', return_date = ? WHERE loan_id = ?";
try (PreparedStatement pstmt = conn.prepareStatement(updateLoanSQL)) {
pstmt.setDate(1, java.sql.Date.valueOf(LocalDate.now()));
pstmt.setInt(2, loanId);
pstmt.executeUpdate();
}
String updateBookSQL = "UPDATE books SET status = 'available' WHERE book_id = ?";
try (PreparedStatement pstmt = conn.prepareStatement(updateBookSQL)) {
pstmt.setInt(1, bookId);
pstmt.executeUpdate();
}
LocalDate today = LocalDate.now();
if (today.isAfter(dueDate)) {
long daysLate = ChronoUnit.DAYS.between(dueDate, today);
double fineAmount = daysLate * 0.50;
String insertFineSQL = "INSERT INTO fines (loan_id, amount, reason, date_assessed) VALUES (?, ?, ?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(insertFineSQL)) {
pstmt.setInt(1, loanId);
pstmt.setDouble(2, fineAmount);
pstmt.setString(3, "Book returned " + daysLate + " days late");
pstmt.setDate(4, java.sql.Date.valueOf(today));
pstmt.executeUpdate();
}
System.out.println("Fine created: $" + fineAmount + " for loan " + loanId);
}
// Commit the transaction
conn.____();
return true;
} catch (SQLException e) {
// Roll back the transaction if an error occurs
if (conn != null) {
conn.____();
}
System.err.println("Error processing return: " + e.getMessage());
return false;
}
}
}