JDBCを使ってデータベースのデータをコピーするはじめにデータベースを操作していると、データベース間でデータをコピーする作業が必要になることがよくあります。これは単純な作業のように思えますが、単なるコピーだけでは済まないことも少なくありません。たとえば、データの一部だけを移動したい場合や、コピーの際にデータを変更したい場合があります。本稿では、私が「データムーバー」(DataMover)と呼んでいる簡単なアプリケーションを使って、基本的なデータベースのコピー(データベース間のテーブルの移動)を実行する一連のクラスを紹介します。このサンプルは、データベース全体をコピーする方法を示す叩き台にすぎませんが、コードに手を加えることにより、必要に応じて個別のテーブルまたは特定のテーブルの行や列をコピーするなど、望みどおりのデータコピー/変換操作を実行できることが分かるでしょう。 本稿では、JDBC(Java Database Connectivity)の扱いに不慣れな方のために、入門編として、次のようなJDBCの基本的な使い方を説明します。
さらに、データベースにクエリを発行し、データベースの構造に関する情報を収集する方法についても説明します。これにより、DataMoverユーティリティを使ってデータベースに関する情報を収集し、データベースをコピーできるようになります。DataMoverは、次の4段階の手順に沿って処理を実行します。
DataMoverアプリケーションをプログラムする詳しい方法を説明する前に、このアプリケーションを使ってデータベースをコピーする方法を説明します。 DataMoverを使ってデータベースをコピーする本稿のダウンロードサンプルには、DataMoverUtilityという簡単なユーティリティが含まれています。このユーティリティは設定ファイルを読み取り、コピー元データベースのデータをコピー先データベースにコピーします。 警告
ここに書かれているように、このユーティリティはコピー先データベースにある既存のデータを上書きします。言い換えると、コピー先データベースにテーブルをコピーするときに、同じ名前のテーブルがあると削除されます。
ユーティリティを実行するには、まず設定ファイルを作成する必要があります。このファイルには、4行のデータを記述します。たとえば、DataMoverをテストするために、私は次のような設定ファイルを使いました。 sourceDriver=com.mysql.jdbc.Driver sourceURL=jdbc:mysql://127.0.0.1/test?user=root targetDriver=com.mysql.jdbc.Driver targetURL=jdbc:mysql://127.0.0.1/test2?user=root この設定ファイルでは、MySQLデータベース間でデータをコピーすることを指定しています。データベースは両方ともローカルコンピュータ(127.0.0.1)上にあるという前提です。JDBCのデータベースごとに、ドライバと接続URLを指定する必要があります。 DataMoverユーティリティを実行するときに、この設定ファイルを最初のパラメータとして指定します。たとえば次のように指定します。 java DataMoverUtility c:export.txt MySQLをJavaで使用するJDBCアプリケーションをコンパイルして使用する前に、使用するデータベースのドライバをインストールする必要があります。通常は、ドライバのJARファイルをプロジェクトのクラスパスに追加するだけです。 本稿では、MySQLを使用するという前提で話を進めます。ただし、ここで示すコードはほとんどのデータベースと非常に高い互換性があります。MySQLに固有と考えられる部分は、すべて「MySQL.java」クラスファイルにまとめられています。他のデータベースを使う場合は、単純に MySQLでは、Connector/Jドライバを使います。これはwww.mysql.orgからダウンロードできます。パッケージにJARファイルが含まれているので、それをクラスパスに追加します。これでMySQLデータベースに接続する準備は完了です。 DataMoverの構造 DataMoverは、主要な2つのパッケージから成ります。com.heatonresearch.datamoverという名前のパッケージには、 com.heatonresearch.datamover.dbという名前のパッケージには、下位レベルのすべてのデータベースクラスが含まれています。本稿では、 MySQLに接続するどのような操作を行うにも、まずデータベースに接続する必要があります。JDBC経由でデータベースに接続するには、2つの情報を知っておく必要があります。JDBCドライバ名と、データベースURLです。 JDBCドライバ名は、単なるクラス名です。MySQLでは、クラスは以前にインストールしたMySQL JARファイルによって提供されます。データベースURLでは、データベースの名前、場所、および使用するユーザーを指定します。データベースへの接続を開くには、 try { Class.forName(driver).newInstance(); connection = DriverManager.getConnection(url); } catch (InstantiationException e) { throw new DatabaseException(e); } catch (IllegalAccessException e) { throw new DatabaseException(e); } catch (ClassNotFoundException e) { throw new DatabaseException(e); } catch (SQLException e) { throw new DatabaseException(e); } コードの最初の2行で、ドライバとURLを処理しています。残りの行では、データベースを開くときに起こりうるいろいろな例外を処理しています。いずれかの例外が発生した場合は、その例外を テーブルおよびカタログ情報にアクセスするデータベースをコピーするには、データベースに関するカタログ情報を取得する必要があります。具体的には、データベースのテーブルのリストと、各テーブル内のすべての列のリストが必要になります。この情報に基づいて、DataMoverはデータのコピーに必要なSQL文を作成します。
Collection<String> result = new ArrayList<String>();
次に、テーブルのリストを保持する ResultSet rs = null; try { DatabaseMetaData dbm = connection.getMetaData();
String types[] = { "TABLE" };
rs = dbm.getTables(null, null, "", types);
この場合の while (rs.next()) { String str = rs.getString("TABLE_NAME"); result.add(str); } } catch (SQLException e) { throw (new DatabaseException(e)); } finally { if( rs!=null ) { try { rs.close(); } catch (SQLException e) { } } } 最後に、
return result;
テーブルのリストを取得した後は、それらを処理してテーブル内の列のリストを取得します。それにより、適切な データベースカタログ情報(テーブルと列)を取得したら、必要なSQL文を生成することができます。 CREATE TABLE文を生成する データベースカタログ情報から
StringBuffer result = new StringBuffer();
次に、 try { StringBuffer sql = new StringBuffer(); sql.append("SELECT * FROM "); sql.append(table); ResultSet rs = executeQuery(sql.toString()); ResultSetMetaData md = rs.getMetaData(); 先に result.append("CREATE TABLE "); result.append(table); result.append(" ( "); すべての列に対してループ処理を行い、列の名前を for (int i = 1; i <= md.getColumnCount(); i++) { if (i != 1) result.append(’,’); result.append(md.getColumnName(i)); result.append(’ ’); 列の型を取得し、それを文の後ろに追加します。 String type = processType(md.getColumnTypeName(i), md.getPrecision(i)); result.append(type); 型の後に精度を指定する必要があります。精度が65535を超える場合は、BLOB(Binary Large Object)またはText型なので精度は不要です。そうでない場合は、精度と桁数を指定します。 if (md.getPrecision(i) < 65535) { result.append(’(’); result.append(md.getPrecision(i)); if (md.getScale(i) > 0) { result.append(’,’); result.append(md.getScale(i)); } result.append(") "); } else result.append(’ ’); 型が数値で、種類が符号なしの場合は、 if (this.isNumeric(md.getColumnType(i))) { if (!md.isSigned(i)) result.append("UNSIGNED "); } また、データ型がNULL値を受け付けるかどうかも指定します。 if (md.isNullable(i) == ResultSetMetaData.columnNoNulls) result.append("NOT NULL "); else result.append("NULL "); if (md.isAutoIncrement(i)) result.append(" auto_increment"); } さらに、主キーを指定する必要があります。これは DatabaseMetaData dbm = connection.getMetaData(); ResultSet primary = dbm.getPrimaryKeys( null, null, table); boolean first = true; while (primary.next()) { if (first) { first = false; result.append(’,’); result.append("PRIMARY KEY("); } else result.append(","); result.append(primary.getString ("COLUMN_NAME")); } if (!first) result.append(’)’); 終わりのかっこを付けて
result.append(" ); ");
}
エラーが発生した場合は、DataMoverが catch (SQLException e) { throw (new DatabaseException(e)); } 最後に、
return result.toString();
著者注 CREATE TABLE文を実行する前に、既存のコピー先データベースのテーブルを削除する必要があります。さもないと、generateCreateメソッドはエラーをスローします。Databaseクラスには、指定されたテーブル名に基づいてDROP TABLE文を生成するgenerateDropメソッドが含まれています。データをコピーする コピー先データベースにテーブルを作成したら、そこにデータをコピーします。それには、 まず、 StringBuffer selectSQL = new StringBuffer(); StringBuffer insertSQL = new StringBuffer(); StringBuffer values = new StringBuffer(); DataMoverは列を取得し、それぞれの状態を表示します。
Collection<String> columns =
source.getColumns(table);
System.out.println("Begin copy: " + table);
次に、 selectSQL.append("SELECT "); insertSQL.append("INSERT INTO "); insertSQL.append(table); insertSQL.append("("); さらに、すべての列に対してループ処理を行い、 boolean first = true; for (String column : columns) { if (!first) { selectSQL.append(","); insertSQL.append(","); values.append(","); } else first = false; 各文に列名を追加し、
selectSQL.append(column);
insertSQL.append(column);
values.append("?");
}
次に、 selectSQL.append(" FROM "); selectSQL.append(table); insertSQL.append(") VALUES ("); insertSQL.append(values); insertSQL.append(")"); ここまでで // now copy PreparedStatement statement = null; ResultSet rs = null; try { statement = target.prepareStatement( insertSQL.toString()); rs = source.executeQuery(selectSQL.toString()); サンプルでは、状態レポート用に行数をカウントしています。
int rows = 0;
すべてのレコードに対してループ処理を行い、
while (rs.next())
{
rows++;
それぞれの for (int i = 1; i <= columns.size(); i++) { statement.setString(i, rs.getString(i)); } その後、 statement.execute(); } 最後に、状態情報を表示して終了します。 System.out.println("Copied " + rows + " rows."); System.out.println(""); } 最初に述べたように、このサンプルはデータベース間でデータをコピーする簡単なユーティリティですが、私はこれを、より大きなデータベースユーティリティを作成するための土台としてよく利用しています。このユーティリティには、もっと複雑なデータベースコピーアプリケーションを作成するために必要な多くの関数が含まれていることがわかるでしょう。 著者紹介Jeff Heaton(Jeff Heaton)
ライター、大学教員、コンサルタントとして活動中。4冊の著作があり、論文誌および雑誌で20を超える記事を発表。また、個人のWebサイトを管理し、人工知能とスパイダー/ボットプログラミングをはじめとする話題について情報発信を行っている。メールの宛先はjheaton@heatonresearch.com。
関連記事 最新トップニュース
|
【ケータイ USA】新しい iPod は来週火曜に発表されるだろう(9月6日 13:00)
Apple が『iPod』関連の発表を準備中、内容は如何に(9月4日 12:40)
なぜ勝った? 世界No.1シェアをつかんだ“Windows”(9月5日 11:00)
【ケータイ USA】イギリスの団体が iPhone の広告における Apple の虚偽に言及(8月30日 13:00)
ソフトバンクモバイル、8月の純増数は約16万件――携帯電話契約数に関する速報(9月5日 14:40)
私の周りは“geek out”している人ばかり(9月5日)
|