Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Gelegentliche NullPointerException in ResultSetImpl.checkColumnBounds oder ResultSetImpl.getStringInternal

Es ist lange her, dass ich diese Frage gepostet habe, und ich möchte eine Antwort posten, die das genaue Szenario beschreibt, das zu dieser kniffligen NullPointerException geführt hat .

Ich denke, dies kann zukünftigen Lesern helfen, die auf eine so verwirrende Ausnahme stoßen, über den Tellerrand hinauszublicken, da ich fast allen Grund hatte zu vermuten, dass dies ein MySQL-Connector-Bug war, obwohl dies letztendlich nicht der Fall war.

Bei der Untersuchung dieser Ausnahme war ich mir sicher, dass meine Anwendung die DB-Verbindung möglicherweise nicht schließen kann, während sie versucht, Daten daraus zu lesen, da meine DB-Verbindungen nicht von Threads gemeinsam genutzt werden und wenn derselbe Thread die Verbindung geschlossen und dann versucht hat, darauf zuzugreifen es hätte eine andere Ausnahme ausgelöst werden sollen (einige SQLException ). Das war der Hauptgrund, warum ich einen Fehler im MySQL-Connector vermutete.

Es stellte sich heraus, dass zwei Threads auf dieselbe Verbindung zugegriffen haben Letztendlich. Der Grund dafür war schwer herauszufinden, weil einer dieser Threads ein Garbage-Collector-Thread war .

Zurück zu dem Code, den ich gepostet habe:

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();

Das Problem liegt im Abschnitt "Verarbeiten, bei dem andere Datensätze über dieselbe Verbindung aus der DB geladen werden", den ich leider nicht in meine ursprüngliche Frage aufgenommen habe, da ich nicht glaubte, dass das Problem dort lag.

Wenn wir in diesen Abschnitt hineinzoomen, haben wir:

if (sc != null) {
    ...
    someMethod (conn);
    ...
}

Und someMethod sieht so aus:

public void someMethod (Connection conn) 
{
    ...
    SomeOtherClass instance = new SomeOtherClass (conn);
    ...
}

SomeOtherClass sieht so aus (natürlich vereinfache ich hier):

public class SomeOtherClass
{
    Connection conn;

    public SomeOtherClass (Connection conn) 
    {
        this.conn = conn;
    }

    protected void finalize() throws Throwable
    { 
        if (this.conn != null)
            conn.close();
    }

}

SomeOtherClass kann in einigen Szenarien eine eigene DB-Verbindung erstellen, kann aber in anderen Szenarien, wie der hier gezeigten, eine bestehende Verbindung akzeptieren.

Wie Sie sehen, enthält dieser Abschnitt einen Aufruf von someMethod die die offene Verbindung als Argument akzeptiert. someMethod übergibt die Verbindung an eine lokale Instanz von SomeOtherClass . SomeOtherClass hatte ein finalize Methode, die die Verbindung schließt.

Jetzt, nach someMethod gibt zurück, instance berechtigt zur Müllabfuhr. Wenn es Garbage Collection ist, wird es finalize -Methode wird vom Garbage Collector-Thread aufgerufen, der die Verbindung schließt.

Jetzt kommen wir zurück zur for-Schleife, die weiterhin SELECT-Anweisungen über dieselbe Verbindung ausführt, die jederzeit vom Garbage-Collector-Thread geschlossen werden kann.

Wenn der Garbage-Collector-Thread zufällig die Verbindung schließt, während sich der Anwendungs-Thread mitten in einer mysql-Connector-Methode befindet, die darauf angewiesen ist, dass die Verbindung offen ist, wird eine NullPointerException ausgelöst kann vorkommen.

finalize entfernen Methode hat das Problem gelöst.

Wir überschreiben nicht oft finalize Methode in unseren Klassen, was es sehr schwierig machte, den Fehler zu lokalisieren.