JDBCを勉強してみた。
最近、javaのormであるActiveObjectsを勉強しています。このActiveObjectsですが、javaからDBへのアクセス部分のライブラリであるため、結局最終的に使用されるのはJDBCです。
そこで、今日は5年ぐらい前に買ったJDBCの本をちゃんと読んでみました。
- 作者: ジョージリース,George Reese,石井史子,福龍興業
- 出版社/メーカー: オライリー・ジャパン
- 発売日: 2001/09
- メディア: 単行本
- クリック: 7回
- この商品を含むブログ (3件) を見る
以下、今日勉強した内容を書いていきます。
今回勉強した内容一覧
java.sql.ResultSet(結果セット)で重要なメソッド
列の値を取得する。
get型( 列の番号 | 列名 ) => 列の値(型はメソッド名の型)
// SELECT文を実行して、結果セットを取得する。 ResultSet rs = stmt.executeQuery("SELECT a, b FROM TABLE"); // a列(1番目の列)を、String型で取得する。 // ※列の番号は1から始まる。 String a = rs.getString( 1 ); // b列の値を、int型で取得する。 int b = rs.getInt( "b" ); // 結果セットを閉じる。 rs.close();
次の行を取得する。
next() => true : 残りの行があった/ false : 残りの行がない
// SELECT文を実行して、結果セットを取得する。 ResultSet rs = stmt.executeQuery("SELECT a, b FROM TABLE"); // 行がなくなるまで繰り返す。 while ( rs.next() ) { // a列とb列の値を出力する。 System.out.println( rs.getString( "a" ) + " : " + rs.getInt( "b" ) ); } // 結果セットを閉じる。 rs.close();
直前に(get型()で)取得した列がNULLかどうか確認する。
wasNull()
// SELECT文を実行して、結果セットを取得する。 ResultSet rs = stmt.executeQuery("SELECT a, b FROM TABLE"); String a = rs.getString( "a" ); // b列を取得する。 int b = rs.getInt( "b" ); // b列がNULLでないことを確認する。 if ( rs.wasNull() ) { // b列がNULLである: // -1におきかえる。 b = -1; }
(Connection|Statement|ResultSet|...)をclose()するべき理由
JDBCはSunによって開発されたAPIですが、JDBCの実装であるJDBCドライバは様々なベンダによって作成されています。そのため、JDBC実装の中には、close()が呼び出されるまでJDBCドライバ内で使用したリソースを解放しないまま保持し続けるようなものもあるそうです。したがって、このようなJDBCドライバの実装の違いに振り回されないためにも、使用し終わったオブジェクトに対しては、利用者側で必ずclose()するべきのようです。
トランザクション・アイソレーション(トランザクションの遮断レベル)
ポイント: トランザクション内で読み取る内容が、他のトランザクションの更新の影響をどれぐらい受けるか?
ダーティ読み取り
他のトランザクションのコミット前の更新値を読み取ってしまいます。そうすると、他のトランザクションがコミットされずにロールバックされた場合、誤った値を読み取ってしまったこととなります。
上図では、トランザクションAのコミット前の「1.UPDATE」での更新値「B」を、トランザクションBの「2.SELECT」が読み取っている。しかし、トランザクションAは「3.ROLLBACK」を行ったため、「B」の読み取りは誤りだったことになります。実際に、ロールバック後には、トランザクションAでの「4.SELECT」で「A」が読み取られます。
繰り返し不可の読み取り
あるトランザクションが、他のトランザクションによるコミットの前と後において、それぞれをコミット前の値とコミット後の値を読み取ります。それにより、他のトランザクションのコミットの前後でトランザクションが読み取るデータが異なる、つまり、繰り返して同じ値を読み取ることができなくなってしまいます。
上図では、トランザクションAのコミット前は、トランザクションBは値「A」を読み取ります。しかし、トランザクションAのコミット後は、トランザクションBは値「B」を読み取るようになります。トランザクションのAのコミットの前後で、トランザクションBは同じ値を読み取ることができないことになります。
JDBCにおけるトランザクション・アイソレーション
java.sql.Connection#setTransactionIsolation(int level)を呼び出すことで、トランザクション・アイソレーションの設定の変更を試みることが可能です。試みることが可能と言っているのは、JDBC実装によって設定が変更されないことがあるためです。
引数のlevelには、java.sql.Connectionで定義されている、定数を指定します。以下、定数を表にまとめました。
ダーティ読み取りが起こる | 繰り返し不可読み取りが起こる | ファントム読み取りが起こる | |
---|---|---|---|
TRANSACTION_READ_COMMITTED | ○ | ○ | ○ |
TRANSACTION_READ_UNCOMMITTED | ○ | ○ | |
TRANSACTION_REPEATABLE_READ | ○ | ||
TRANSACTION_SERIALIZABLE |
実際のデータ(インタフェース)とメタデータの対応関係
JDBCでは、いくつかのデータ(インタフェース)に対して、それに付随するメタデータがそれぞれ別のインタフェース(またはクラス)として定義されています。この実際のデータとメタデータの関係を、表にまとめました。似たような関連性があるにもかかわらず、名前の付け方には規則性がありません。
実際のデータ(インタフェース) | メタデータ(インタフェース または クラス) | |
---|---|---|
結果セット | ResultSet | ResultSetMetaData |
JDBC接続 | Connection | DatabaseMetaData |
JDBCドライバ | Driver | DriverPropertyInfo |
ネーミング・サービス/ディレクトリ・サービス
- ネーミング・サービス -- 「プログラムの要素」と「名前」を対応付ける仕組み
- ディレクトリ・サービス -- ネーミング・サービスの「対応付け」に、「属性」を設定できるようにした仕組み
一般的なディレクトリ・サービス
- NIS(Network Information Service) -- サンマイクロシステムズが開発したディレクトリサービス。
- NIS+ -- NISの後継で、サンマイクロシステムズが開発した。
- ActiveDirectory -- マイクロソフトが開発したディレクトリサービス。
- etc...
JDBCのデータソース
JDBCのデータソース(java.sql.DataSource) = JDBCの接続ファクトリ
- データソースを使用すると、データベースを名前で参照できます。
- データソースは、ディレクトリサービス内に格納できます。
データソースを使用して、データベースを名前で参照する。
// ディレクトリサービスの初期コンテキストを作成する。 Context context = new InitialContext(); // 初期コンテキストから、名前「jdbc/ora」でJDBCのデータソース(JDBCの接続ファクトリ)を取得する。 DataSource dataSource = (DataSource) context.lookup( "jdbc/ora" ); // データソースを使用して、JDBCの接続を作成する。 Connection connection = dataSource.getConnection( "user", "password" );
データソースを、ディレクトリサービス内に格納する。
// MySqlデータソースを作成する。 MysqlDataSource dataSource = new MysqlDataSource(); // データソースにサーバ名を設定する。 dataSource.setServerName( "host_name" ); // データソースにDB名を設定する。 dataSource.setDatabaseName( "db_name" ); // データソースを、ディレクトリサービスのコンテキストに名前「jdbc/mysql」で格納(=バインド)する。 context.bind( "jdbc/mysql", dataSource );
接続プール
多くのデータベースエンジンでは、データベースのオープンやクローズの処理には時間がかかります。そのため、短い時間に多数のユーザが接続するシステムでは、このオープンやクローズの処理の回数が非常に多くなるため、ボトルネックになります
それを避けるために、接続プールが使用されます。以下にようになります。
- 接続プールが、オープンされたDB接続をキャッシュします。
- アプリケーションでは、キャッシュされたDB接続を利用します。
開発者から見ると、通常の接続と接続プールで取得された接続には、ほとんど違いはありません。
行セット
読んだけど、自分の中でまとまってない&使わない気がするので、パスします。