レベルエンター山本大のブログ

面白いプログラミング教育を若い人たちに

BLOCKVROCKリファレンス目次はこちら

Java6でぐっと良くなったSQLの例外

[原文]Better SQLExceptions in Java 6

http://www.javaspecialists.co.za/archive/newsletter.do?issue=138

今まで、JDBCをつかったプログラムでの例外は、
すべてSQLExceptionとして返され、例外のメッセージに含まれる
SQLステータスコードプログラマーが読んで対処する必要がありました。

しかし、喜ばしいことにJDK6 のJDBC4.0 からは、データベースの例外を
JDBCドライバーが解析して、適切な例外を返してくれるようになります。
例外は、SQLExceptionのサブクラスとして返されます。

それらの例外サブクラスは以下の分類に分けられます。

  • SQLNonTransientException : 一時的でない例外
  • SQLTransientException : 一時的な例外
  • SQLRecoverableException : 回復可能な例外

さらに詳細な例外階層は以下です。

SQLException
+---> SQLNonTransientException(一時的でない例外)
| +---> SQLDataException :データ型の例外
| +---> SQLFeatureNotSupportedException :サポートされていない時の例外
| +---> SQLIntegrityConstraintViolationException : 制限違反(外部キーの異常など)
| +---> SQLInvalidAuthorizationException :権限違反
| +---> SQLNonTransientConnectionException :一時的でない接続例外
| +---> SQLSyntaxErrorException : SQLの構文例外
+---> SQLTransientException(一時的な例外)
| +---> SQLTimeoutException : SQLタイムアウト
| +---> SQLTransactionRollbackException : ロールバッグ例外
| +---> SQLTransientConnectionException : 一時的な接続例外
+---> SQLRecoverableException(回復可能な例外)

  • SQLNonTransientException : 一時的でない例外

 これは、構文異常やデータ型の異常、外部キー制約の異常など、
 アプリケーションを修正し直さなければならない例外といえます。

  • SQLTransientException : 一時的な例外

 これは、少し時間が経てば回復することができるかも知れない例外を表しています。
 つまり、アプリケーションのロジックを変更しなくても回復する可能性があります。
 たとえば、SQLTransactionRollbackExceptionは、デッドロックが発生したときに返されます。

Apache Derby(アパッチ ダービー)
このJDK6(JRE6のことではありません)の機能は、Derbyという組込データベースで実装されています。
derby.jar

以下の「DbTest」を実行するには、「derby.jar」ファイルをクラスパス下におくことです。
derby.jarは、「JDK6/db/lib」ディレクトリにあります。
ちなみに、JDBC4.0 ではClass.forName()の実行は必要ありません。

import java.sql.*;

public class DbTest {
  private final static String dburl = "jdbc:derby:tjsnTest";

  public static void main(String[] args) throws SQLException {
    Connection con = getNewConnection();
    Statement s = con.createStatement();

    try {
      s.execute("hello world - this should not work");
    } catch (SQLSyntaxErrorException ex) {
      System.out.println("Permanent problem with syntax");
    }

    s.execute("create table testTable(id int, name varchar(10))");
    try {
      s.execute("insert into testTable values (1, 'Heinz Kabutz')");
    } catch (SQLDataException ex) {
      System.out.println("Permanent problem with the data input");
    }

    System.out.println("Is connection valid? " + con.isValid(10));

    shutdownDB();

    System.out.println("Is connection valid? " + con.isValid(10));

    try {
      s.execute("drop table testTable");
    } catch (SQLTransientConnectionException ex) {
      System.out.println("Temporary problem connecting to db");
    }

    // restarting the database
    con = getNewConnection();
    s = con.createStatement();
    try {
      s.execute("drop table testTable");
    } catch (SQLTransientConnectionException ex) {
      System.out.println("Temporary problem connecting to db");
    }

    try {
      s.executeQuery("SELECT id, name FROM testTable");
    } catch (SQLSyntaxErrorException ex) {
      System.out.println("Permanent syntax problem with query");
    }
  }

  // shutting down the database
  private static void shutdownDB() throws SQLException {
    try {
      DriverManager.getConnection(dburl + ";shutdown=true");
    } catch (SQLTransientConnectionException ex) {
      // this should not happen - but it does ...
      System.out.println("Temporary problem connecting to db");
    }
  }

  private static Connection getNewConnection() throws SQLException {
    return DriverManager.getConnection(dburl + ";create=true");
  }
}

こんな風に実行結果が得られます。

    java -cp %JDK_HOME%/db/lib/derby.jar;. DbTest

    Permanent problem with the data input
    Is connection valid? true
    Temporary problem connecting to db
    Is connection valid? false
    Temporary problem connecting to db
    Permanent syntax problem with query