Een boek inleveren
Je bent ontwikkelaar voor het beheersysteem van de Metropolitan Public Library. Vorige week werd er een kritieke bug ontdekt: wanneer het systeem crashte tijdens het inleveren van een boek, werd de lening als "returned" gemarkeerd, maar bleef het boek in de inventaris op "checked-out" staan. Daardoor was het boek wekenlang niet beschikbaar totdat medewerkers de database handmatig corrigeerden.
Wanneer een boek wordt ingeleverd, moeten drie bewerkingen samen slagen of samen falen:
- Werk het leningrecord bij naar de status "returned".
- Verander de beschikbaarheid van het boek van "checked-out" naar "available".
- Registreer een boete als het boek te laat is ingeleverd.
Jouw taak is om correcte transactiebesturing te implementeren om gegevensconsistentie te waarborgen.
Deze oefening maakt deel uit van de cursus
Query's uitvoeren op een PostgreSQL-database in Java
Oefeninstructies
- Zet
autoCommitopfalsehelemaal aan het begin op regel 26. - Commit de transactie als alle bewerkingen slagen op regel 59.
- Als een bewerking faalt, rol de transactie terug op regel 66.
Praktische interactieve oefening
Probeer deze oefening eens door deze voorbeeldcode in te vullen.
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;
}
}
}