[Java] SQLExceptionのgetNextExceptionとiteratorの違い

2018年5月7日月曜日

Java JDBC

SQLExceptionを補足した時にチェーンされた例外

SQLExceptionを補足した時にチェーンされた例外をすべて見たかったんだけど、getNextExceptionで戻り値がnullになるまでループするのと、iteratorでループするのでは取得される例外が異なるみたい。

https://docs.oracle.com/javase/jp/8/docs/api/java/sql/SQLException.html
        ...
    } catch (SQLException e) {
        for (Throwable t : e) {
            ...
        }
    }
        ...
    } catch (SQLException e) {
        for (SQLException sqle = e; sqle != null; sqle = sqle.getNextException()) {
            ...
        }
    }
iteratorは単純にgetNextExceptionを反復してくれるのかと思っていたら、そうではなかった。

戻り値の型

まず、getNextExceptionとiteratorでは戻り値の型が違う。
getNextExceptionの戻り値はSQLExceptionで、iteratorの戻り値はIterator<Throwable>になっている。
つまり、iteratorを使うと、SQLExceptionではない例外やエラーが取得される場合がある。

getNextException

setNextExceptionのAPIリファレンスには、以下のように書かれている。

setNextException(SQLException ex)によってこのSQLExceptionオブジェクトにチェーンされた例外を取得します。

これは、まあ想定どおり。

iterator

iteratorのAPIリファレンスには、以下のように書かれている。

チェーンされたSQLExceptionについてのイテレータを返します。イテレータは、各SQLExceptionとその基になる原因(存在する場合)を反復するために使用されます。

基になる原因と言っているので、Throwable.getCauseで取得される例外も返してくるということだろう。

なので

試しにsetNextExceptionとコンストラクタで原因例外を設定して、iteratorでループしてみると、こんな感じになる。
    try {
        SQLException e = new SQLException("SQLException1",
            new Exception("Exception1", new Exception("Exception0")));
        e.setNextException(new SQLException("SQLException2",
            new Exception("Exception2")));
        throw e;
    } catch (SQLException e) {
        for (Throwable t : e) {
            System.out.println("loop...");
            System.out.println(t.getMessage());
        }
    }
loop...
SQLException1
loop...
Exception1
loop...
Exception0
loop...
SQLException2
loop...
Exception2

まず、getCauseで取得される原因例外をたどっていって、それがなくなったら、getNextExceptionで次のSQLExceptionを取得するという順番になっている。

SQLExceptionのgetErrorCodeやgetSQLStatを使いたい場合は、instanceofで分岐するしかないかなあ。