この質問を投稿してから長い時間が経ちました。このトリッキーなNullPointerException
につながった正確なシナリオを説明する回答を投稿したいと思います。 。
これは、このような紛らわしい例外に遭遇した将来の読者が、箱の外で考えるのに役立つと思います。結局のところ、これがmysqlコネクタのバグであると疑う理由はほとんどあったからです。
この例外を調査している間、DB接続がスレッド間で共有されておらず、同じスレッドが接続を閉じてからアクセスしようとした場合、アプリケーションがDB接続からデータを読み取ろうとしているときにDB接続を閉じることができないと確信しました。それなら、別の例外がスローされるべきでした(いくつかのSQLException
)。それが、mysqlコネクタのバグを疑った主な理由でした。
同じ接続にアクセスする2つのスレッドがあったことが判明しました 結局。これを理解するのが難しい理由は、これらのスレッドの1つがガベージコレクタスレッドであったためです。 。
投稿したコードに戻ります:
Connection conn = ... // the connection is open
...
for (String someID : someIDs) {
SomeClass sc = null;
PreparedStatement
stmt = conn.prepareStatement ("SELECT A, B, C, D, E, F, G, H FROM T WHERE A = ?");
stmt.setString (1, "someID");
ResultSet res = stmt.executeQuery ();
if (res.next ()) {
sc = new SomeClass ();
sc.setA (res.getString (1));
sc.setB (res.getString (2));
sc.setC (res.getString (3));
sc.setD (res.getString (4));
sc.setE (res.getString (5));
sc.setF (res.getInt (6));
sc.setG (res.getString (7));
sc.setH (res.getByte (8)); // the exception is thrown here
}
stmt.close ();
conn.commit ();
if (sc != null) {
// do some processing that involves loading other records from the
// DB using the same connection
}
}
conn.close();
問題は、「同じ接続を使用してDBから他のレコードをロードする処理を実行する」セクションにあります。残念ながら、問題があるとは思わなかったため、元の質問には含めませんでした。
そのセクションを拡大すると、次のようになります。
if (sc != null) {
...
someMethod (conn);
...
}
そしてsomeMethod
次のようになります:
public void someMethod (Connection conn)
{
...
SomeOtherClass instance = new SomeOtherClass (conn);
...
}
SomeOtherClass
このように見えます(もちろん、ここでは簡略化しています):
public class SomeOtherClass
{
Connection conn;
public SomeOtherClass (Connection conn)
{
this.conn = conn;
}
protected void finalize() throws Throwable
{
if (this.conn != null)
conn.close();
}
}
SomeOtherClass
一部のシナリオでは独自のDB接続を作成できますが、ここにあるような他のシナリオでは既存の接続を受け入れることができます。
ご覧のとおり、そのセクションにはsomeMethod
の呼び出しが含まれています オープン接続を引数として受け入れます。 someMethod
SomeOtherClass
のローカルインスタンスに接続を渡します 。 SomeOtherClass
finalize
がありました 接続を閉じるメソッド。
さて、someMethod
の後 戻り値、instance
ガベージコレクションの対象になります。ガベージコレクションされると、そのfinalize
メソッドは、接続を閉じるガベージコレクタスレッドによって呼び出されます。
ここで、forループに戻ります。このループは、ガベージコレクタスレッドによっていつでも閉じられる可能性のある同じ接続を使用してSELECTステートメントを実行し続けます。
アプリケーションスレッドが開いている接続に依存するmysqlコネクタメソッドの途中にあるときに、ガベージコレクタスレッドがたまたま接続を閉じた場合、NullPointerException
発生する可能性があります。
finalize
を削除する メソッドが問題を解決しました。
finalize
をオーバーライドすることはあまりありません クラスのメソッドであるため、バグを見つけるのが非常に困難でした。