始める無料で始める

図書クラブの土曜日

メトロポリタン公立図書館では「図書クラブの土曜日」という大人気イベントが開催されています。毎回30〜40人の会員が午前10時に来館し、その月の選書を借りていきます。現在のシステムは各ローンを個別に処理しており、自動コミットが有効になっているため、次の2つの深刻な問題が生じています。

  1. データの不整合:先月、ある会員が4冊を同時に借りようとした際にシステムがクラッシュしました。2冊は貸出済みとして記録されましたが、残りの2冊は記録されず、在庫に不一致が生じて利用者や職員を困らせる結果となりました。
  2. パフォーマンスのボトルネック:各ローンを別々のデータベーストランザクションとして処理するため、15〜20分もかかってしまいます。これにより長い行列ができ、図書館スタッフも頭を悩ませています。

ここでは、トランザクション制御を組み合わせたバッチ処理を実装しましょう。

この演習はコースの一部です

Java で PostgreSQL データベースにクエリを実行する

コースを見る

演習の手順

  • 32行目autoCommitfalse に設定し、手動トランザクション制御を有効にしてください。
  • 43行目で複数のローンをバッチに追加してください。
  • 49行目ですべての処理が正常に完了した場合にトランザクションをコミットしてください。

実践的なインタラクティブ演習

このサンプルコードを完成させて、この演習に挑戦してみましょう。

public class BatchLoanProcessor {
    private static final String DB_URL = "jdbc:postgresql://localhost:5432/library_db";
    private static final String USERNAME = "postgres";
    private static final String PASSWORD = "postgres";
    
    public static void main(String[] args) {
        Object[][] loanData = {
            {1, 1, LocalDate.now(), LocalDate.now().plusDays(14), "borrowed"},
            {2, 2, LocalDate.now(), LocalDate.now().plusDays(14), "borrowed"},
            {3, 3, LocalDate.now(), LocalDate.now().plusDays(7), "borrowed"},
            {4, 1, LocalDate.now(), LocalDate.now().plusDays(14), "borrowed"}
        };
        
        try {
            boolean success = processBatchLoans(loanData);
            if (success) {
                System.out.println("All loans were processed successfully.");
            } else {
                System.out.println("Loan processing failed. Transaction was rolled back.");
            }
        } catch (SQLException e) {
            System.err.println("Database error: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    public static boolean processBatchLoans(Object[][] loanData) throws SQLException {
        String insertSQL = "INSERT INTO loans (book_id, member_id, loan_date, due_date, status) VALUES (?, ?, ?, ?, ?)";
        
        try (Connection conn = DriverManager.getConnection(DB_URL, USERNAME, PASSWORD)) {
            // Set autoCommit to false
            conn.____(____);
            
            try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) {
                for (Object[] loan : loanData) {
                    pstmt.setInt(1, (Integer) loan[0]);  
                    pstmt.setInt(2, (Integer) loan[1]);  
                    pstmt.setDate(3, java.sql.Date.valueOf((LocalDate) loan[2]));  
                    pstmt.setDate(4, java.sql.Date.valueOf((LocalDate) loan[3]));  
                    pstmt.setString(5, (String) loan[4]);  
                    
                    // Add to batch
                    pstmt.____();
                }
                
                int[] results = pstmt.executeBatch();
                                
                // Commit the transaction if all operations are successful
                conn.____();
                
                return true;
            } catch (SQLException e) {
                conn.rollback();
                
                System.err.println("Error during batch processing: " + e.getMessage());
                return false;
            }
        }
    }
}
コードを編集して実行