My Photo

« MySQLメモ012:ユーザー管理 その2 | Main | 100円ショップでスタイラス買った »

May 09, 2012

MySQLメモ013:INSERT後にAUTO_INCREMENTなカラムの値を取得する方法

Java+MySQLなWEBアプリにおいて、AUTO_INCREMENTなカラムを持つテーブルにINSERTでレコードを挿入してから、その新規レコードのAUTO_INCREMENTなカラムの値を取得する方法について、ちょっとてこずったのでメモ。 環境は以下。
Java:1.6.0_31
MySQL:5.5.19

本やネットで調べると LAST_INSERT_ID() というMySQLの関数を使う方法もあるようだが、ここはせっかくなのでMySQLのJDBCドライバ(MySQL Connector/J)が提供するAPIを使ってみた。

サンプルコード
final String sql = "INSERT INTO orders (customer_name, address, payment) VALUES (?, ?, ?)";

DataSource dataSource = (DataSource)context.lookup("java:comp/env/jdbc/mysql");
Connection conn = dataSource.getConnection();
PreparedStatement statement = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
statement.setString(1, customerName);
statement.setString(2, address);
statement.setInt(3, payment);
statement.executeUpdate();

// 上記の処理で登録したデータのAUTO_INCREMENTで生成されたIDを取得する
PreparedStatement stmt2
    = (PreparedStatement)((DelegatingPreparedStatement) statement)
        .getInnermostDelegate();
ResultSet rs = ((com.mysql.jdbc.PreparedStatement)stmt2).getGeneratedKeys();
if (rs.next()) {
	id = rs.getInt(1);
} else {
	throw new SQLException("failure: retrieve new id");
}

上記のコードのWEBアプリでは、JNDIで DataSource を取得し DataSource.getConnection() で java.sql.Connection を取得して変数connに入れている。
そのconnで作成した PreparedStaement を作成し statement に代入(5行目)する。ここで使用する prepareStatement() は引数にSQLとフラグを取るもので、フラグには Statement.RETURN_GENERATED_KEYS をセットする。

java.sql 
インタフェース Connection
PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException
自動生成キーを取得する機能を持つデフォルトの PreparedStatement オブジェクトを生成します。
パラメータ:
sql - 1 つ以上の '?' IN パラメータプレースホルダーを含めることができる SQL 文
autoGeneratedKeys - 自動生成キーを返すかどうかを示すフラグ。Statement.RETURN_GENERATED_KEYS または Statement.NO_GENERATED_KEYS 
戻り値:
プリコンパイルされた SQL 文を含む新しい PreparedStatement オブジェクト。自動生成キーを返す機能を持つ 
例外: 
SQLException - データベースアクセスエラーが発生した場合、このメソッドがクローズされた接続に対して呼び出された場合、または指定されたパラメータが自動生成キーを返すかどうかを示す Statement 定数でない場合 
SQLFeatureNotSupportedException - JDBC ドライバが定数 Statement.RETURN_GENERATED_KEYS を指定したこのメソッドをサポートしない場合

作成した PreparedStaement は com.mysql.jdbc.PreparedStatement ではないらしい。statement を com.mysql.jdbc.PreparedStatement でキャストしようとすると例外が発生する。
そこで、 statement を org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement にキャストし、getInnermostDelegate() で内部に保持されていた PreparedStatement を取得し stmt2 に代入する(12-15行目)。この取得した PreparedStatement は com.mysql.jdbc.PreparedStatement らしいので getGeneratedKeys() で ResultSet を取得し AUTO_INCREMENT なカラムの値を取得する(15,17行目)。

org.apache.commons.dbcp 
クラス DelegatingPreparedStatement
public PreparedStatement getInnermostDelegate()
内部に保持する PreparedStatement が DelegatingPreparedStatement でない場合にはその PreparedStatement を返し、それ以外の場合には再帰的に getDelegate() をコールします。 
従ってこのメソッドは DelegatingPreparedStatement ではない根本の処理の委託先となる PreparedStatement を返し、 DelegatingPreparedStatement の連鎖の中に処理の委託先が見つからない場合には null を返します。 
このメソッドはネストした DelegatingPreparedStatement から 本来の PreparedStatement を取得したい場合に有用です。 

JNDIを使用せず下記のように Connection を取得した場合は DelegatingPreparedStatement#getInnermostDelegate() は不要で、直接キャストして getGeneratedKeys() を使えばよい。

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/java_sample_db", "java", "password");

参考ページ:MySQL :: MySQL 5.1 リファレンスマニュアル :: 24.4.5.1 JDBC の基本コンセプト

« MySQLメモ012:ユーザー管理 その2 | Main | 100円ショップでスタイラス買った »

Java」カテゴリの記事

MySQL」カテゴリの記事

Comments

The comments to this entry are closed.

TrackBack

« MySQLメモ012:ユーザー管理 その2 | Main | 100円ショップでスタイラス買った »

June 2021
Sun Mon Tue Wed Thu Fri Sat
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30      
無料ブログはココログ

日本blog村

  • にほんブログ村 IT技術ブログへ
  • にほんブログ村 アニメブログへ
  • にほんブログ村 サッカーブログ アルビレックス新潟へ

好きな音楽家

メモ

XI-Prof