Class.forName で DB アクセスできるようになる理由
Java の研修で DB(だいたいMDBかな) にアクセスするプログラムを作ることになったとき、講師はほぼ 100% 「JDBC Driver を使用するためには Class.forName を使用します」と言うはず。ただ、呪文のごとく。
で、Class.forName の API を見てみる。
forName(String name, boolean initialize, ClassLoader loader)
指定されたクラスローダを使って、指定された文字列名を持つクラスまたはインタフェースに関連付けられた Class オブジェクトを返します
そして疑問が生まれる。「クラスをロードするだけでなんでDBにアクセスできるようになるの?」と。
講師はなぜできるかは説明しない。分かってないってことは無いと思うけど「まだ初心者だから覚えとけばいい」的な感じだろう。
けど、ここは言わせてもらう!
説明すべきだ!
おかげで今になってようやく理解できた。特に必要なかったのもあるけど、ふと気になって調べてみた。
んで、こういう事(間違ってたら恥ずかしいから早く教えてください)。
各ベンダの JDBC Driver は java.sql.Driver を実装している必要があり、その実装クラスは static initializer で java.sql.DriverManager.registerDriver(java.sql.Driver) を使って登録している。
Class.forName -> static initializer -> DriverManager.registerDriver って流れ。あとは DriverManager を経由して各 Driver にアクセスして使用する。
ついでにいくつかの JDBC Driver のソースを確認してみた。
sun
JDBC Driver の登録
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
static initializer
public class JdbcOdbcDriver extends JdbcOdbcObject implements JdbcOdbcDriverInterface { //-------------------------------------------------------------------- // Static method to be executed when the class is loaded. //-------------------------------------------------------------------- static { JdbcOdbcTracer tracer1 = new JdbcOdbcTracer(); if (tracer1.isTracing ()) { tracer1.trace ("JdbcOdbcDriver class loaded"); } JdbcOdbcDriver driver = new JdbcOdbcDriver (); // Attempt to register the driver try { DriverManager.registerDriver (driver); } catch (SQLException ex) { if (tracer1.isTracing ()) { tracer1.trace ("Unable to register driver"); } } }
MySQL
JDBC Driver の登録
Class.forName("com.mysql.jdbc.Driver");
static initializer
public class Driver extends NonRegisteringDriver implements java.sql.Driver { // ~ Static fields/initializers // --------------------------------------------- // // Register ourselves with the DriverManager // static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }
PostgreSQL
JDBC Driver の登録
Class.forName("org.postgresql.Driver");
static initializer
public class Driver implements java.sql.Driver { // make these public so they can be used in setLogLevel below public static final int DEBUG = 2; public static final int INFO = 1; private static final Logger logger = new Logger(); private static boolean logLevelSet = false; static { try { // moved the registerDriver from the constructor to here // because some clients call the driver themselves (I know, as // my early jdbc work did - and that was based on other examples). // Placing it here, means that the driver is registered once only. java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException e) { e.printStackTrace(); } }
HashMap#put のソースを読む
ちょっと疑問があったので調べていたのだが、ソースも公開されている事だしちょっとずつ Java のソースを見ていこうと思う。仕事では JDK1.4 だけど読むのは JDK5.0 にする。今更ながら結構違うのよね。ジェネリックスが登場してソースコードも結構修正されてる。
第一弾は HashMp#put
public V put(K key, V value) { K k = maskNull(key); int hash = hash(k); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { if (e.hash == hash && eq(k, e.key)) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, k, value, i); return null; }
まず、K とか V とかってなんだよ(笑) Object じゃないのかよ。理解不能だがこれは後で調べる。
気になったのが、
return oldValue
put したら一つ前の値が返ってくるんだよ (^^; 今まで実装で困ったことは無いしこれをあてにしたことも無いから実害は無いと思うけど、、、ちょっと微妙だなぁ、、、普通に考えれば今突っ込んだ値を返してメソッドチェーンでつなぐんじゃね?
とりあえずテストコードを書いてみた。
Map map = new HashMap(); for(int i = 0 ; i < 9 ; i++) { System.out.println("put " + i + " -> " + map.put("KEY" , i + "")); }
実行結果
put 0 -> null put 1 -> 0 put 2 -> 1 put 3 -> 2 put 4 -> 3 put 5 -> 4 put 6 -> 5 put 7 -> 6 put 8 -> 7
一つ前の値が返ってきてるいるのが分かる。そして、JavaDoc を見ていて気がついた。
@return previous value associated with specified key
ちゃんと「キーに関連する前の値が返される」と書かれている。日本語訳されている JavaDoc を見てみる
指定されたキーに関連した値
HashMap#put JDK5
いや、誤訳じゃね?
気になったので、JDK6 の API も見てみた。
key に以前に関連付けられていた値
HashMap#put JDK6
直ってる!
安心した。
[rails] migration 分かりずれー
ruby script/generate model モデル名
と
ruby script/generate migration 変更名
と
rake db:migrate
の違いが分かりずらい。順番的には、
- テーブルを追加するために generate model -> rake
- 変更したくなったら generate migration -> rake
- テーブルを追加したくなったら generate model -> rake
を繰り返す感じかな。変更を実際に DB へ反映させるためには rake しないといけないところがポイント。