도서 반납 처리
당신은 Metropolitan Public Library의 관리 시스템을 담당하는 개발자입니다. 지난주에 치명적인 버그가 발견되었는데, 시스템이 도서 반납 중에 장애가 발생하면 대출 기록은 "returned"로 표시되지만, 재고에서는 해당 도서가 여전히 "checked-out" 상태로 남는 문제였습니다. 이로 인해 직원이 수동으로 데이터베이스를 수정할 때까지 수주 동안 도서를 이용할 수 없었습니다.
도서가 반납될 때는 다음 세 가지 작업이 함께 성공하거나 함께 실패해야 합니다.
- 대출 기록을 "returned" 상태로 업데이트합니다.
- 도서의 이용 가능 상태를 "checked-out"에서 "available"로 변경합니다.
- 반납이 늦은 경우 연체료를 기록합니다.
데이터 일관성을 보장하도록 올바른 트랜잭션 제어를 구현하세요.
이 연습은 강의의 일부입니다
Java에서 PostgreSQL 데이터베이스 질의하기
연습 안내
- 26번째 줄에서
autoCommit을false로 설정하세요. - 모든 작업이 성공하면 59번째 줄에서 트랜잭션을 커밋하세요.
- 어떤 작업이든 실패하면 66번째 줄에서 트랜잭션을 롤백하세요.
실습형 인터랙티브 연습
이 예제를 이 샘플 코드를 완성하여 풀어보세요.
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;
}
}
}