Java JDBCを使ってストアドプロシージャを簡単に呼び出す方法を徹底解説

JDBC(Java Database Connectivity)とストアドプロシージャは、Javaでデータベースと連携する際に非常に重要な役割を果たします。JDBCは、Javaプログラムからデータベースへの接続と操作を可能にするAPIであり、ストアドプロシージャはデータベース側で事前に定義された一連のSQL命令をまとめたものです。本記事では、JDBCを使ってJavaアプリケーションからストアドプロシージャを呼び出す方法について、基本から応用まで詳しく解説します。

目次

JDBCを使うメリット

JDBCを使用することで、Javaアプリケーションとデータベースの間でデータの送受信が容易になります。以下は、JDBCを活用する主なメリットです。

データベースの独立性

JDBCは、OracleやMySQL、PostgreSQLなど、多くの異なるデータベースと互換性があります。これにより、Javaアプリケーションは特定のデータベースに依存せず、柔軟に運用できます。

標準化されたAPI

JDBCはJavaの標準APIであるため、Java開発者は統一されたインターフェースを使ってデータベース操作を行えます。これにより、学習コストが低く、保守性も向上します。

パフォーマンスの向上

プリコンパイルされたSQL文を使うことで、SQLの再コンパイルを避け、アプリケーションの実行速度を向上させることが可能です。ストアドプロシージャの呼び出しと組み合わせると、さらに効率的なデータ処理が行えます。

ストアドプロシージャとは?

ストアドプロシージャは、データベース内で事前に作成され、保存された一連のSQL命令を実行するプログラムのことです。これにより、複雑なデータベース操作を繰り返し簡単に実行でき、パフォーマンスやセキュリティの向上が期待できます。

ストアドプロシージャの利点

ストアドプロシージャを使用する主な利点は、以下の通りです。

  • 効率的なデータ処理:一度作成したプロシージャを複数回呼び出すことで、データベースとのやり取りを最適化できます。
  • セキュリティの向上:アプリケーション側でSQL文を直接実行せず、プロシージャを通して処理することで、SQLインジェクションなどの攻撃を防ぐことができます。
  • 再利用性:ストアドプロシージャは一度定義すれば、他のアプリケーションや開発者が簡単に呼び出して使用できるため、再利用性が高まります。

主な用途

ストアドプロシージャは、複雑な計算やデータ操作をまとめて行う際に非常に便利です。例えば、売上集計、データのバッチ更新、複数のテーブルを横断するクエリなど、効率的にデータベース処理を行うために頻繁に使用されます。

JDBCでストアドプロシージャを呼び出す基本手順

JavaのJDBCを使用してストアドプロシージャを呼び出すための基本的な手順を以下に説明します。これらのステップを通じて、Javaアプリケーションからデータベースにアクセスし、ストアドプロシージャを実行できます。

1. JDBCドライバのロード

最初のステップは、Javaアプリケーションから特定のデータベースと接続するために必要なJDBCドライバをロードすることです。通常、以下のようなコードでドライバをロードします。

Class.forName("com.mysql.cj.jdbc.Driver");

2. データベースへの接続

次に、DriverManager.getConnectionメソッドを使用してデータベースに接続します。接続URLには、使用するデータベースのタイプやホスト、ポート番号、データベース名を指定します。

Connection conn = DriverManager.getConnection(
    "jdbc:mysql://localhost:3306/mydatabase", "username", "password");

3. CallableStatementオブジェクトの作成

ストアドプロシージャを呼び出すには、CallableStatementオブジェクトを生成します。以下のコードは、my_procedureというストアドプロシージャを呼び出す準備を行います。

CallableStatement stmt = conn.prepareCall("{call my_procedure(?, ?)}");

4. 入出力パラメータの設定と実行

ストアドプロシージャに引数がある場合、setXxxメソッドを使って入力パラメータを設定し、executeメソッドでプロシージャを実行します。

stmt.setInt(1, 10);  // 1番目のパラメータに値を設定
stmt.registerOutParameter(2, Types.INTEGER);  // 2番目の出力パラメータを設定
stmt.execute();  // ストアドプロシージャを実行

5. 出力結果の取得

ストアドプロシージャが出力パラメータを返す場合、getXxxメソッドを使用して結果を取得します。

int result = stmt.getInt(2);

この一連の手順を理解すれば、JDBCを使って効率的にストアドプロシージャを呼び出し、データベース操作を自動化できます。

データベース接続の設定方法

JDBCを使ってストアドプロシージャを呼び出すためには、まずデータベースへの接続設定を正しく行う必要があります。データベース接続は、Javaアプリケーションがデータベースと通信するための最初の重要なステップです。ここでは、データベース接続を確立するための基本的な方法を解説します。

1. JDBC URLの構成

JDBC接続には、データベースのタイプ、ホスト名、ポート番号、データベース名を指定するJDBC URLが必要です。一般的な形式は以下の通りです。

jdbc:subprotocol://hostname:port/databasename

例えば、MySQLデータベースに接続する場合のJDBC URLは次のようになります。

String url = "jdbc:mysql://localhost:3306/mydatabase";

2. 必要なドライバの指定

JDBCを使うためには、対象データベースに対応するJDBCドライバが必要です。通常、Class.forName()を使ってJDBCドライバをロードします。例えば、MySQL用のドライバは以下のように指定します。

Class.forName("com.mysql.cj.jdbc.Driver");

これにより、Javaはデータベース接続に必要なドライバを認識し、接続が可能になります。

3. データベース接続の作成

次に、DriverManager.getConnection()を使用してデータベースに接続します。このメソッドは、JDBC URL、ユーザー名、パスワードを受け取り、データベース接続を確立します。

Connection conn = DriverManager.getConnection(url, "username", "password");

接続が成功すると、Connectionオブジェクトが返され、このオブジェクトを使ってデータベースに対する操作が行えます。

4. エラーハンドリング

データベース接続時にエラーが発生する可能性があるため、接続処理はtry-catchブロックでラップし、例外(SQLException)を適切に処理します。

try {
    Connection conn = DriverManager.getConnection(url, "username", "password");
    System.out.println("接続成功");
} catch (SQLException e) {
    e.printStackTrace();
    System.out.println("接続失敗");
}

5. 接続のクローズ

最後に、データベース接続を使い終わったら必ず閉じる必要があります。これにより、リソースの解放とデータベースへの負担軽減が行われます。

conn.close();

正しく設定されたデータベース接続は、Javaアプリケーションが効率的にデータベースとやり取りするための基盤となります。

CallableStatementの使い方

JDBCでストアドプロシージャを呼び出すためには、CallableStatementという特別なステートメントを使用します。CallableStatementは、Javaからデータベース内のストアドプロシージャや関数を呼び出すために設計されたクラスです。ここでは、CallableStatementの基本的な使い方を詳しく解説します。

1. CallableStatementの準備

ストアドプロシージャを呼び出すには、ConnectionオブジェクトのprepareCall()メソッドを使用してCallableStatementを作成します。このメソッドは、ストアドプロシージャの呼び出し構文を引数として受け取ります。

CallableStatement stmt = conn.prepareCall("{call my_procedure(?, ?)}");

この例では、my_procedureというストアドプロシージャを呼び出しており、2つのパラメータを渡しています。

2. 入力パラメータの設定

ストアドプロシージャにパラメータが必要な場合、CallableStatementを使ってそのパラメータを設定します。パラメータには、数値や文字列など、さまざまなデータ型を使用できます。パラメータの設定は、setXxx()メソッドを使って行います(Xxxはデータ型を表します)。

stmt.setInt(1, 100);  // 1番目のパラメータに数値100を設定
stmt.setString(2, "Sample");  // 2番目のパラメータに文字列を設定

この例では、ストアドプロシージャの1番目の引数に数値、2番目の引数に文字列を設定しています。

3. 出力パラメータの登録

ストアドプロシージャが出力パラメータを持っている場合は、registerOutParameter()を使って、出力パラメータを登録します。出力パラメータは、プロシージャの実行後に結果を受け取るための場所を確保します。

stmt.registerOutParameter(3, Types.INTEGER);  // 3番目のパラメータを出力用に登録

このコードは、ストアドプロシージャの3番目のパラメータを出力パラメータとして登録しています。

4. ストアドプロシージャの実行

パラメータの設定が完了したら、execute()メソッドを使ってストアドプロシージャを実行します。ストアドプロシージャは通常、データベース上で複数の操作を一度に行います。

stmt.execute();

このコードは、指定されたストアドプロシージャをデータベース上で実行します。

5. 出力パラメータの取得

ストアドプロシージャが出力パラメータを返す場合、getXxx()メソッドを使って値を取得します。Xxxは、取得するデータ型に応じて変更します。

int result = stmt.getInt(3);  // 3番目の出力パラメータを取得

この例では、ストアドプロシージャの3番目のパラメータから整数の結果を取得しています。

6. CallableStatementのクローズ

最後に、CallableStatementも他のリソースと同様にクローズする必要があります。これにより、データベースのリソースが解放されます。

stmt.close();

これで、CallableStatementを使ったストアドプロシージャの呼び出しの基本的な流れが完了します。CallableStatementを適切に使用することで、複雑なデータベース処理を簡潔に実行でき、効率的なデータ操作が可能となります。

ストアドプロシージャの入力パラメータの設定方法

ストアドプロシージャに引数を渡す際、JDBCのCallableStatementを使って入力パラメータを設定します。入力パラメータは、ストアドプロシージャが実行される前にデータベースに送信され、プロシージャ内で使用されます。ここでは、入力パラメータの設定方法について詳しく説明します。

1. 入力パラメータの基本

ストアドプロシージャのパラメータには、入力パラメータと出力パラメータがあります。入力パラメータは、Javaからストアドプロシージャに渡すデータであり、プロシージャ内で処理に使われます。入力パラメータの設定には、CallableStatementsetXxxメソッドを使用します。

2. 入力パラメータのデータ型

CallableStatementでは、SQLのデータ型に対応するJavaのデータ型を使ってパラメータを設定します。例えば、以下のように異なるデータ型に応じたメソッドを使用します。

  • setInt(int parameterIndex, int value) – 整数型のパラメータを設定
  • setString(int parameterIndex, String value) – 文字列型のパラメータを設定
  • setDate(int parameterIndex, Date value) – 日付型のパラメータを設定

3. パラメータの設定例

例えば、次のようなストアドプロシージャを呼び出すとします。

CREATE PROCEDURE update_salary(IN emp_id INT, IN new_salary DECIMAL(10,2))
BEGIN
    UPDATE employees SET salary = new_salary WHERE id = emp_id;
END;

このプロシージャに対して、CallableStatementでパラメータを設定する方法は次の通りです。

CallableStatement stmt = conn.prepareCall("{call update_salary(?, ?)}");

// 1番目の入力パラメータに従業員ID(整数)を設定
stmt.setInt(1, 123);

// 2番目の入力パラメータに新しい給与額(小数点数)を設定
stmt.setBigDecimal(2, new BigDecimal("55000.75"));

このコードでは、従業員IDと新しい給与額をストアドプロシージャに渡しています。

4. パラメータの順序とインデックス

CallableStatementでは、パラメータの順序が重要です。パラメータは、ストアドプロシージャ内の定義順にインデックスで指定されます。例えば、上記の例では、emp_idが1番目のパラメータ、new_salaryが2番目のパラメータとして設定されています。

インデックスは1から始まるため、setXxxメソッドを使って正しい順序でパラメータを指定する必要があります。

5. NULL値の設定

場合によっては、入力パラメータとしてNULLを設定する必要があります。その場合は、setNullメソッドを使用します。例えば、ストアドプロシージャにNULLを渡したい場合、以下のようにします。

stmt.setNull(2, Types.DECIMAL);

この例では、2番目のパラメータにNULLを設定しています。

6. 入力パラメータの確認

すべてのパラメータが正しく設定されているか確認したら、CallableStatementを実行します。適切なパラメータが設定されていないと、ストアドプロシージャの実行時にエラーが発生する可能性があります。


このようにして、CallableStatementを使用してストアドプロシージャに必要な入力パラメータを設定し、Javaからデータベースのプロシージャを操作できます。正確なパラメータ設定により、データベース処理を効率的に行うことができます。

ストアドプロシージャの出力パラメータの取得方法

ストアドプロシージャは、入力パラメータだけでなく、実行結果を出力パラメータとして返すことがあります。出力パラメータは、データベース操作の結果をJavaプログラム内で利用するために重要です。ここでは、JDBCのCallableStatementを使って、出力パラメータを取得する方法を解説します。

1. 出力パラメータの登録

出力パラメータを取得する前に、CallableStatementで出力パラメータを登録する必要があります。registerOutParameter()メソッドを使用して、出力パラメータをプロシージャ実行前に登録します。

例えば、次のようなストアドプロシージャを呼び出すとします。

CREATE PROCEDURE get_employee_salary(IN emp_id INT, OUT salary DECIMAL(10,2))
BEGIN
    SELECT salary INTO salary FROM employees WHERE id = emp_id;
END;

このプロシージャに対して、出力パラメータを登録するコードは以下のようになります。

CallableStatement stmt = conn.prepareCall("{call get_employee_salary(?, ?)}");

// 1番目の入力パラメータに従業員IDを設定
stmt.setInt(1, 123);

// 2番目の出力パラメータを登録
stmt.registerOutParameter(2, Types.DECIMAL);

ここでは、1番目のパラメータは入力用(従業員ID)、2番目のパラメータは出力用(給与額)として登録しています。

2. ストアドプロシージャの実行

出力パラメータの登録が完了したら、execute()メソッドを使用してストアドプロシージャを実行します。このメソッドが実行されると、ストアドプロシージャの出力結果がCallableStatementに格納されます。

stmt.execute();

3. 出力パラメータの取得

ストアドプロシージャが実行された後、getXxx()メソッドを使って出力パラメータを取得します。Xxxは、返されるデータ型に応じたメソッドを使用します。例えば、DECIMAL型の出力パラメータを取得する場合は、getBigDecimal()メソッドを使います。

BigDecimal salary = stmt.getBigDecimal(2);
System.out.println("従業員の給与: " + salary);

このコードでは、2番目の出力パラメータ(従業員の給与額)を取得しています。

4. 複数の出力パラメータの処理

ストアドプロシージャが複数の出力パラメータを返す場合、各出力パラメータをそれぞれregisterOutParameter()で登録し、getXxx()で取得します。例えば、次のようなプロシージャを呼び出す場合を考えます。

CREATE PROCEDURE get_employee_details(IN emp_id INT, OUT name VARCHAR(100), OUT salary DECIMAL(10,2))
BEGIN
    SELECT name, salary INTO name, salary FROM employees WHERE id = emp_id;
END;

このプロシージャを呼び出して出力パラメータを取得するコードは次の通りです。

CallableStatement stmt = conn.prepareCall("{call get_employee_details(?, ?, ?)}");

// 1番目の入力パラメータに従業員IDを設定
stmt.setInt(1, 123);

// 2番目と3番目の出力パラメータを登録
stmt.registerOutParameter(2, Types.VARCHAR);
stmt.registerOutParameter(3, Types.DECIMAL);

// ストアドプロシージャの実行
stmt.execute();

// 出力パラメータの取得
String name = stmt.getString(2);
BigDecimal salary = stmt.getBigDecimal(3);

System.out.println("従業員名: " + name);
System.out.println("従業員の給与: " + salary);

このコードでは、従業員の名前(VARCHAR)と給与(DECIMAL)の両方の出力パラメータを取得しています。

5. エラーハンドリング

出力パラメータの取得中に例外が発生する場合があるため、必ずtry-catchブロックでエラーハンドリングを行うことが重要です。例えば、データ型の不一致やストアドプロシージャ実行中のエラーなどが考えられます。

try {
    BigDecimal salary = stmt.getBigDecimal(2);
} catch (SQLException e) {
    e.printStackTrace();
}

このように、JDBCを使って出力パラメータを登録し、ストアドプロシージャ実行後に結果を取得することで、データベースからの応答をJavaプログラム内で利用できます。これにより、より複雑なデータ処理を効率的に行うことが可能です。

エラーハンドリングとデバッグのポイント

JDBCを使ってストアドプロシージャを呼び出す際、エラーが発生することは避けられません。特に、データベース接続、SQL構文、ストアドプロシージャの実行時など、さまざまな段階でエラーが生じる可能性があります。ここでは、エラーハンドリングとデバッグのポイントについて解説します。

1. 例外処理の基本

JDBC操作中にエラーが発生すると、SQLExceptionがスローされます。この例外には、エラーメッセージ、SQLエラーコード、およびエラーが発生したSQLステートが含まれています。エラーの詳細を取得するためには、SQLExceptionのメソッドを活用します。

try {
    CallableStatement stmt = conn.prepareCall("{call my_procedure(?)}");
    stmt.setInt(1, 123);
    stmt.execute();
} catch (SQLException e) {
    System.out.println("エラーメッセージ: " + e.getMessage());
    System.out.println("SQLエラーコード: " + e.getErrorCode());
    System.out.println("SQLステート: " + e.getSQLState());
}

上記のコードでは、SQL例外の詳細を出力することで、問題の原因を特定できます。

2. SQL構文エラーの特定

ストアドプロシージャの呼び出しやSQLクエリに誤りがあると、SQL構文エラーが発生します。この場合、データベースのログやSQLExceptionのエラーメッセージを確認し、どの部分に問題があるかを特定します。エラーメッセージはSQL文の誤りを指摘してくれるため、エラーメッセージを精査することが重要です。

例えば、SQL文にタイポがあった場合、次のようなエラーメッセージが表示されることがあります。

エラーメッセージ: You have an error in your SQL syntax near 'FROM employee'

3. パラメータの不一致によるエラー

ストアドプロシージャのパラメータの数やデータ型が一致しない場合もエラーが発生します。例えば、CallableStatementに渡したパラメータが、ストアドプロシージャで期待されている型と一致しないと、エラーがスローされます。このようなエラーは、事前にストアドプロシージャのパラメータリストを確認し、正確なデータ型を指定することで回避できます。

// 例: INT型のパラメータを期待しているのに、String型を渡してしまう
stmt.setString(1, "123");  // これはエラーを引き起こす可能性がある

この場合、以下のように正しいデータ型を設定する必要があります。

stmt.setInt(1, 123);

4. トランザクション管理とロールバック

ストアドプロシージャが複数のデータベース操作を行う場合、トランザクション管理を適切に行うことが重要です。トランザクションが失敗した場合、エラー時にロールバックを行い、データベースの一貫性を保つようにします。

try {
    conn.setAutoCommit(false);  // トランザクションを手動で管理
    CallableStatement stmt = conn.prepareCall("{call my_procedure(?)}");
    stmt.setInt(1, 123);
    stmt.execute();
    conn.commit();  // 正常に完了した場合、コミットする
} catch (SQLException e) {
    conn.rollback();  // エラーが発生した場合、ロールバックして変更を取り消す
    e.printStackTrace();
}

このコードでは、トランザクション内でストアドプロシージャを実行し、エラーが発生した場合にはロールバックを行ってデータベースの状態を保護しています。

5. デバッグのためのログ出力

デバッグを効率的に行うために、重要な操作やエラーをログに記録することをお勧めします。JavaのLoggerクラスを使って、アプリケーション内で発生する問題を追跡できるようにします。

import java.util.logging.Logger;

Logger logger = Logger.getLogger("MyLog");

try {
    CallableStatement stmt = conn.prepareCall("{call my_procedure(?)}");
    stmt.setInt(1, 123);
    stmt.execute();
} catch (SQLException e) {
    logger.severe("エラー発生: " + e.getMessage());
}

この方法を使うことで、エラーの詳細を記録し、後で確認できるようになります。

6. データベースとの接続切断エラー

データベース接続の途中でネットワークエラーやデータベースサーバーのダウンが発生すると、接続切断エラーが発生します。接続が切れた場合、再接続を試みる、もしくは適切なエラーメッセージを表示するようにする必要があります。


エラーハンドリングとデバッグの適切な方法を知っておくことで、JDBCを使ったストアドプロシージャの呼び出しがスムーズに行えるようになります。エラーの種類を理解し、それに応じた処理を行うことで、アプリケーションの信頼性と安定性が向上します。

応用例:複数のストアドプロシージャを呼び出す

JDBCを使用して、複数のストアドプロシージャを呼び出すことは、より高度なデータベース操作が必要な場合に非常に有効です。特に、複数のテーブルにまたがるデータ処理や、連続したデータ更新を行う場合に役立ちます。ここでは、複数のストアドプロシージャを効率的に呼び出す方法と、その応用例について解説します。

1. ストアドプロシージャの連続呼び出し

まず、複数のストアドプロシージャを連続して呼び出す基本的な方法について説明します。以下の例では、2つのストアドプロシージャを順番に呼び出して、最初のプロシージャでデータを取得し、次にそのデータを使って2番目のプロシージャを実行します。

try {
    // 最初のストアドプロシージャ呼び出し
    CallableStatement stmt1 = conn.prepareCall("{call get_employee_details(?, ?, ?)}");
    stmt1.setInt(1, 123);  // 従業員IDを設定
    stmt1.registerOutParameter(2, Types.VARCHAR);  // 名前の出力パラメータ
    stmt1.registerOutParameter(3, Types.DECIMAL);  // 給与の出力パラメータ
    stmt1.execute();  // 最初のストアドプロシージャを実行

    // 出力パラメータから結果を取得
    String name = stmt1.getString(2);
    BigDecimal salary = stmt1.getBigDecimal(3);

    System.out.println("従業員名: " + name);
    System.out.println("給与: " + salary);

    // 2番目のストアドプロシージャ呼び出し(取得したデータを利用)
    CallableStatement stmt2 = conn.prepareCall("{call update_salary(?, ?)}");
    stmt2.setInt(1, 123);  // 同じ従業員ID
    stmt2.setBigDecimal(2, salary.add(new BigDecimal("5000")));  // 給与を5000増加
    stmt2.execute();  // 2番目のストアドプロシージャを実行

    System.out.println("給与が更新されました。");

} catch (SQLException e) {
    e.printStackTrace();
}

この例では、まず従業員の名前と給与を取得するストアドプロシージャを呼び出し、その後取得した給与に基づいて給与を更新する別のストアドプロシージャを呼び出しています。

2. トランザクション内での複数プロシージャ呼び出し

複数のストアドプロシージャを呼び出す際、すべての操作が正常に完了することを保証するために、トランザクション管理を行うことが重要です。すべてのストアドプロシージャが成功した場合のみコミットし、どれかが失敗した場合はロールバックすることで、データの一貫性を保ちます。

try {
    conn.setAutoCommit(false);  // トランザクションを開始

    // 最初のストアドプロシージャ
    CallableStatement stmt1 = conn.prepareCall("{call get_employee_details(?, ?, ?)}");
    stmt1.setInt(1, 123);
    stmt1.registerOutParameter(2, Types.VARCHAR);
    stmt1.registerOutParameter(3, Types.DECIMAL);
    stmt1.execute();

    String name = stmt1.getString(2);
    BigDecimal salary = stmt1.getBigDecimal(3);

    // 2番目のストアドプロシージャ
    CallableStatement stmt2 = conn.prepareCall("{call update_salary(?, ?)}");
    stmt2.setInt(1, 123);
    stmt2.setBigDecimal(2, salary.add(new BigDecimal("5000")));
    stmt2.execute();

    conn.commit();  // すべてが成功したらコミット
    System.out.println("トランザクションが正常にコミットされました。");

} catch (SQLException e) {
    conn.rollback();  // 何かが失敗した場合はロールバック
    System.out.println("エラーが発生し、トランザクションがロールバックされました。");
    e.printStackTrace();
}

このコードでは、2つのストアドプロシージャが連続して呼び出され、どちらかが失敗した場合にはロールバックされるようになっています。これにより、データの一貫性が保たれ、複数の操作が確実に行われます。

3. 複数プロシージャを使った応用例

応用例として、次のシナリオを考えてみましょう。例えば、オンラインショップで、注文処理に関連する複数の操作をストアドプロシージャで実行します。

  1. 在庫確認プロシージャ: 商品の在庫を確認する。
  2. 注文作成プロシージャ: 注文を作成し、ユーザー情報と商品の情報を記録する。
  3. 在庫更新プロシージャ: 注文が確定した後、在庫を減らす。

このような場合、複数のストアドプロシージャを連続して呼び出し、トランザクション内で処理することで、すべての操作が正常に実行されることを保証します。

try {
    conn.setAutoCommit(false);  // トランザクション開始

    // 在庫確認プロシージャ
    CallableStatement checkStock = conn.prepareCall("{call check_stock(?, ?)}");
    checkStock.setInt(1, productId);
    checkStock.registerOutParameter(2, Types.INTEGER);
    checkStock.execute();

    int stock = checkStock.getInt(2);
    if (stock > 0) {
        // 注文作成プロシージャ
        CallableStatement createOrder = conn.prepareCall("{call create_order(?, ?, ?)}");
        createOrder.setInt(1, userId);
        createOrder.setInt(2, productId);
        createOrder.setInt(3, quantity);
        createOrder.execute();

        // 在庫更新プロシージャ
        CallableStatement updateStock = conn.prepareCall("{call update_stock(?, ?)}");
        updateStock.setInt(1, productId);
        updateStock.setInt(2, quantity);
        updateStock.execute();

        conn.commit();  // 正常に処理された場合はコミット
        System.out.println("注文処理が完了しました。");
    } else {
        System.out.println("在庫が不足しています。");
    }

} catch (SQLException e) {
    conn.rollback();  // 失敗した場合はロールバック
    System.out.println("エラーが発生し、トランザクションがロールバックされました。");
    e.printStackTrace();
}

この例では、在庫確認、注文作成、在庫更新の3つの操作を連続して行い、トランザクションを使用して安全に処理を行っています。


このように、複数のストアドプロシージャを連続して呼び出すことで、複雑なデータベース操作を効率的に行うことができます。特に、トランザクションを活用することで、データの整合性と信頼性を保ちながら処理を進めることが可能です。

演習問題:JDBCを用いたストアドプロシージャの実装

ここでは、JDBCを使用してストアドプロシージャを呼び出す練習問題を通じて、これまで学んだ内容を確認していきます。ストアドプロシージャの基本的な呼び出しから、入力・出力パラメータの設定、複数のプロシージャを連続で実行する操作を含む実践的な問題です。コード例を参考にしながら、自分でコードを書いて試してみてください。

1. 基本問題:ストアドプロシージャの呼び出し

次のSQLを使って、データベース内にストアドプロシージャを作成してください。

CREATE PROCEDURE get_employee_name(IN emp_id INT, OUT emp_name VARCHAR(100))
BEGIN
    SELECT name INTO emp_name FROM employees WHERE id = emp_id;
END;

このストアドプロシージャをJavaのJDBCを使って呼び出し、従業員の名前を取得するプログラムを書いてください。

タスク:

  1. JDBCでデータベースに接続する。
  2. CallableStatementを使ってストアドプロシージャを呼び出す。
  3. 入力パラメータとして従業員IDを渡し、出力パラメータとして名前を取得する。

ヒント:

  • stmt.setInt(1, emp_id)で入力パラメータを設定。
  • stmt.registerOutParameter(2, Types.VARCHAR)で出力パラメータを登録。

実装例

CallableStatement stmt = conn.prepareCall("{call get_employee_name(?, ?)}");

// 従業員IDを入力
stmt.setInt(1, 101);

// 出力パラメータの登録
stmt.registerOutParameter(2, Types.VARCHAR);

// ストアドプロシージャを実行
stmt.execute();

// 結果を取得
String employeeName = stmt.getString(2);
System.out.println("従業員の名前: " + employeeName);

2. 応用問題:複数パラメータのストアドプロシージャ

次に、複数のパラメータを持つストアドプロシージャを呼び出してみましょう。次のSQLを使用して、新しいプロシージャを作成してください。

CREATE PROCEDURE update_employee_salary(IN emp_id INT, IN new_salary DECIMAL(10,2))
BEGIN
    UPDATE employees SET salary = new_salary WHERE id = emp_id;
END;

タスク:

  1. JDBCでデータベースに接続する。
  2. CallableStatementを使って、このストアドプロシージャを呼び出し、従業員の給与を更新するプログラムを書いてください。
  3. 複数の入力パラメータを設定し、プロシージャを実行する。

ヒント:

  • 2つのsetXxxメソッドを使って、従業員IDと新しい給与額を設定します。

実装例

CallableStatement stmt = conn.prepareCall("{call update_employee_salary(?, ?)}");

// 従業員IDを設定
stmt.setInt(1, 101);

// 新しい給与を設定
stmt.setBigDecimal(2, new BigDecimal("75000.00"));

// ストアドプロシージャを実行
stmt.execute();

System.out.println("従業員の給与が更新されました。");

3. 実践問題:複数のストアドプロシージャを組み合わせる

今度は、複数のストアドプロシージャを連続して呼び出してみましょう。次のSQLを使用して2つのストアドプロシージャを作成してください。

  1. 従業員の詳細を取得するプロシージャ
   CREATE PROCEDURE get_employee_details(IN emp_id INT, OUT emp_name VARCHAR(100), OUT emp_salary DECIMAL(10,2))
   BEGIN
       SELECT name, salary INTO emp_name, emp_salary FROM employees WHERE id = emp_id;
   END;
  1. 従業員の給与を更新するプロシージャ
   CREATE PROCEDURE update_employee_salary(IN emp_id INT, IN new_salary DECIMAL(10,2))
   BEGIN
       UPDATE employees SET salary = new_salary WHERE id = emp_id;
   END;

タスク:

  1. get_employee_detailsで従業員の名前と現在の給与を取得。
  2. その後、update_employee_salaryを使って、給与を5000円増額する。
  3. トランザクションを用いて、どちらかの処理が失敗した場合はロールバックするようにプログラムを実装。

ヒント:

  • conn.setAutoCommit(false)でトランザクションを開始し、エラーがあればconn.rollback()を使用します。

実装例

try {
    conn.setAutoCommit(false);  // トランザクションの開始

    // 1つ目のストアドプロシージャで従業員の詳細を取得
    CallableStatement stmt1 = conn.prepareCall("{call get_employee_details(?, ?, ?)}");
    stmt1.setInt(1, 101);
    stmt1.registerOutParameter(2, Types.VARCHAR);
    stmt1.registerOutParameter(3, Types.DECIMAL);
    stmt1.execute();

    String name = stmt1.getString(2);
    BigDecimal salary = stmt1.getBigDecimal(3);

    System.out.println("従業員名: " + name);
    System.out.println("現在の給与: " + salary);

    // 2つ目のストアドプロシージャで給与を5000円増額
    CallableStatement stmt2 = conn.prepareCall("{call update_employee_salary(?, ?)}");
    stmt2.setInt(1, 101);
    stmt2.setBigDecimal(2, salary.add(new BigDecimal("5000.00")));
    stmt2.execute();

    conn.commit();  // トランザクションのコミット
    System.out.println("従業員の給与が更新されました。");

} catch (SQLException e) {
    conn.rollback();  // エラーがあればロールバック
    System.out.println("エラーが発生し、トランザクションがロールバックされました。");
    e.printStackTrace();
}

これらの演習問題を通じて、JDBCを使用したストアドプロシージャの呼び出し方法や、入力・出力パラメータの扱い方、トランザクション管理の重要性について理解が深まります。自分で手を動かして実装することで、より実践的なスキルを習得してください。

まとめ

本記事では、JDBCを使用してストアドプロシージャを呼び出す方法について、基本から応用まで詳しく解説しました。データベースへの接続設定、CallableStatementによるパラメータの設定、出力パラメータの取得、エラーハンドリング、そして複数のストアドプロシージャを扱う応用例などを通じて、JDBCの強力な機能を学びました。これにより、Javaプログラムとデータベース間の効率的な通信を実現し、複雑なデータ操作を簡素化する方法を理解できたはずです。

コメント

コメントする

目次