sql >> Databáze >  >> RDS >> Mysql

Občasná výjimka NullPointer v ResultSetImpl.checkColumnBounds nebo ResultSetImpl.getStringInternal

Už je to dlouho, co jsem tuto otázku zveřejnil, a chci zveřejnit odpověď, která popisuje přesný scénář, který vedl k této záludné NullPointerException .

Myslím, že to může pomoci budoucím čtenářům, kteří se setkají s tak matoucí výjimkou, přemýšlet mimo rámec, protože jsem měl téměř všechny důvody k podezření, že se jedná o chybu mysql konektoru, i když tomu tak nakonec nebylo.

Při zkoumání této výjimky jsem si byl jistý, že moje aplikace nemůže zavírat připojení k databázi, když se z ní pokouší číst data, protože moje připojení DB nejsou sdílena mezi vlákny a pokud stejné vlákno připojení uzavřelo a poté se pokusilo o přístup měla být vyvolána jiná výjimka (některá SQLException ). To byl hlavní důvod, proč jsem měl podezření na chybu mysql konektoru.

Ukázalo se, že existovala dvě vlákna přistupující ke stejnému připojení po všem. Důvod, proč to bylo těžké zjistit, byl ten, že jedno z těchto vláken bylo vlákno pro sběrač odpadu .

Vracím se ke kódu, který jsem zveřejnil:

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

Problém spočívá v sekci „proveďte nějaké zpracování, které zahrnuje načtení dalších záznamů z DB pomocí stejného připojení“, kterou jsem bohužel do své původní otázky nezahrnul, protože jsem si nemyslel, že tam problém je.

Když přiblížíme tuto sekci, máme:

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

A someMethod vypadá takto:

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

SomeOtherClass vypadá takto (samozřejmě zde zjednodušuji):

public class SomeOtherClass
{
    Connection conn;

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

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

}

SomeOtherClass může v některých scénářích vytvořit své vlastní připojení k databázi, ale v jiných scénářích, jako je ten, který máme zde, může přijmout existující připojení.

Jak vidíte, tato sekce obsahuje volání someMethod který akceptuje otevřené spojení jako argument. someMethod předá připojení místní instanci SomeOtherClass . SomeOtherClass měl finalize metoda, která uzavře spojení.

Nyní, po someMethod vrátí, instance se stává způsobilým pro svoz odpadu. Když je odpad shromážděn, jeho finalize metoda je volána vláknem garbage collector, čímž se spojení uzavře.

Nyní se vrátíme ke smyčce for, která pokračuje ve vykonávání příkazů SELECT pomocí stejného připojení, které může být kdykoli uzavřeno garbage collectorem.

Pokud vlákno garbage collector zavře připojení, zatímco aplikační vlákno je uprostřed nějaké metody mysql konektoru, která spoléhá na to, že připojení je otevřené, NullPointerException může dojít.

Odstranění finalize metoda problém vyřešila.

finalize často nepřepisujeme metoda v našich třídách, což velmi ztěžovalo lokalizaci chyby.



  1. Jak nainstalovat MySQL na Debian 8

  2. Načítání dat z databáze MySQL do rozevíracího seznamu html

  3. nejlepší způsob, jak převést a ověřit řetězec data

  4. Více smyček while v rámci smyčky while?