JDBCのカタログとかスキーマについて調べる(Oracle、SQL Server、MySQL)
JDBCには便利な機能があって、テーブルや列のメタ情報を取得できる(java.sql.DatabaseMetaDataのgetTablesとかgetColumnsとか)。
で、これらのメソッドの引数にはカタログとかスキーマを渡す必要がある。
これって何なの?というのを調べてみたい。
ちなみに、スキーマはどのDBMSでもだいたいスキーマだけど、カタログの方はDBMSによって違う。何なのかはDatabaseMetaData#getCatalogTermsにより調べられる。
手元にはOracle(12c)、SQL Server(2012)、MySQL(5.6)があるので、こいつらについて調べてみる。
Oracleのカタログとスキーマ
catelog term | (なし) |
現在のカタログの取得 | (なし) |
現在のカタログの変更 | (なし) |
現在のスキーマの取得 | Connection#getSchema ※Oracle JDBC Driver 12c以降 SQL: SELECT SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') FROM DUAL |
現在のスキーマの変更 | Connection#setSchema(スキーマ名) ※Oracle JDBC Driver 12c以降 SQL: ALAER SESSION SET CURRENT_SCHEMA=スキーマ名 |
Oracleではカタログにあたるものは無いようだ。
java.sql.ConnectionにはgetSchemaとかsetSchemaメソッドがあって、これらでスキーマを取得したり変更できたりするが、これはJava 1.7で追加されたもの。
古いJDBCドライバは対応していないようで、SQLを投げる必要がある。
SQLServerのカタログとスキーマ
catelog term | database |
現在のカタログの取得 | Connection.getCatalog SQL: SELECT DB_NAME() |
現在のカタログの変更 | Connection.setCatalog(カタログ名) SQL: USE カタログ名 |
現在のスキーマの取得 | SQL: SELECT default_schema_name FROM sys.database_principals WHERE name = ユーザ名 |
現在のスキーマの変更 | (なし) |
SQL Serverにおけるカタログはデータベースのことだ。
スキーマについては、ユーザごとに既定のスキーマが定義されている。
既定のスキーマはデータベースごとに設定するので、上のSQLでは一意にならないんじゃない?と思ったが、現在使用しているデータベースのが返されるようだ。
これは、USE ~のように接続中に手軽に変えるものではなく、ALTER USER文を使う必要がある。
なお、Connection.getSchemaやsetSchemaを呼んだらjava.sql.SQLFeatureNotSupportedExceptionという例外が出てしまった。
MySQLのカタログとスキーマ
catelog term | database |
現在のカタログの取得 | Connection.getCatalog SQL: select database() |
現在のカタログの変更 | Connection.setCatalog(カタログ名) SQL: use カタログ名 |
現在のスキーマの取得 | (なし) |
現在のスキーマの変更 | (なし) |
MySQLにおけるカタログも、データベースのことだ。
スキーマにあたるものは無い。
なお、Connection.setCatalogを呼ばずに直接USE文を投げると、Connection.getCatalogに反映されないようだ。
という訳で、カタログとかスキーマについて調べてみたので、今度は実際にDatabaseMetaData.getTablesで何が取れるのかを調べてみたい。
OracleでgetTables
Oracleではあるデータベースへの接続から別のデータベースは見られないはず。
データベースは1つだけ、スキーマはSCH1とSCH2の2つを用意し、それぞれにテーブルTESTを用意した。
ログインユーザはSCH1。
現在のスキーマ | getTablesの引数のスキーマ | 取得できたテーブル |
---|---|---|
SCH1 | null | SCH1.TEST SCH2.TEST |
SCH1 | SCH1.TEST | |
SCH2 | SCH2.TEST | |
SCH2 | null | SCH1.TEST SCH2.TEST |
SCH1 | SCH1.TEST | |
SCH2 | SCH2.TEST |
nullを指定した場合は全スキーマのテーブルが返ってくるということだ。
SQL ServerでgetTables
データベースDB1、DB2を用意し、それぞれにスキーマSCH1、SHC2を用意し、すべてにテーブルTESTを用意した。
データベースとスキーマの両方があるのでめんどいなあ。
現在のカタログ | 現在のスキーマ | getTablesの引数のカタログ | getTablesの引数のスキーマ | 取得できたテーブル |
---|---|---|---|---|
DB1 | SCH1 | null | null | DB1.SCH1.TEST DB1.SCH2.TEST |
DB1 | null | DB1.SCH1.TEST DB1.SCH2.TEST |
||
DB2 | null | DB2.SCH1.TEST DB2.SCH2.TEST |
||
DB1 | SCH1 | DB1.SCH1.TEST | ||
DB1 | SCH2 | DB1.SCH2.TEST |
全パターン書くのはめんどいので、代表的なパターンのみを書いた。
カタログ名を省略すると、Connection.getCatalogの値が補完されるようだ。
Javadocを見ると、両方「null means that the catalog/schema name should not be used to narrow the search」なんだけどなあ。
MySQLでgetTables
データベースDB1、DB2を用意し、それぞれにテーブルTESTを用意した。
現在のカタログ | getTablesの引数のカタログ | 取得できたテーブル |
---|---|---|
null | null | (なし) |
DB1 | DB1.TEST | |
DB2 | DB2.TEST | |
DB1 | null | DB1.TEST |
DB1 | DB1.TEST | |
DB2 | DB2.TEST | |
DB2 | null | DB2.TEST |
DB1 | DB1.TEST | |
DB2 | DB2.TEST |
カタログ名を省略すると、Connection.getCatalogの値が補完されるようだ。
あと、MySQLではテーブル名に「xxx.<テーブル名>」と指定しても、「xxx.」が無視されて検索されるようだ。
まとめ
- カタログとかスキーマはDBMSによって違うので、ちゃんと調べて使わないと。
- DatabaseMetaData#getTablesでは、カタログを省略すると現在のカタログ、スキーマを省略すると全スキーマが対象になるようだ(とりあえずOracle、SQL Server、MySQLの話。他のDBMSは違うかも)
どのDBMSでもちゃんと動くツールを作るのは大変だろうなあ…。
OS | Windos 7 |
Oracle | 12c JDBC Driver 12.1.0.2 JDBC Driver 11.2.0.4 |
SQL Server | 2012 JDBC Driver 4.1 |
MySQL | 5.6 JDBC Driver 5.1.34 |