Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Sporadyczny wyjątek NullPointerException w ResultSetImpl.checkColumnBounds lub ResultSetImpl.getStringInternal

Minęło dużo czasu, odkąd opublikowałem to pytanie i chcę opublikować odpowiedź, która opisuje dokładny scenariusz, który doprowadził do tego trudnego NullPointerException .

Myślę, że może to pomóc przyszłym czytelnikom, którzy napotkają tak mylący wyjątek, myśleć nieszablonowo, ponieważ miałem prawie wszelkie powody, by podejrzewać, że jest to błąd złącza mysql, mimo że tak nie było.

Podczas badania tego wyjątku byłem pewien, że moja aplikacja nie może zamykać połączenia z bazą danych podczas próby odczytu z niej danych, ponieważ moje połączenia z bazą danych nie są współdzielone przez wątki, a jeśli ten sam wątek zamknął połączenie, a następnie próbował uzyskać dostęp powinien zostać zgłoszony inny wyjątek (niektóre SQLException ). To był główny powód, dla którego podejrzewałem błąd złącza mysql.

Okazało się, że dwa wątki miały dostęp do tego samego połączenia w końcu. Powodem, dla którego trudno było to rozgryźć, było to, że jeden z tych wątków był wątkiem odśmiecacza .

Wracając do kodu, który opublikowałem:

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

Problem tkwi w sekcji „zrób trochę przetwarzania, która polega na załadowaniu innych rekordów z bazy danych przy użyciu tego samego połączenia”, której niestety nie uwzględniłem w moim pierwotnym pytaniu, ponieważ nie sądziłem, że jest w tym problem.

Powiększając tę ​​sekcję, mamy:

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

I someMethod wygląda tak:

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

SomeOtherClass wygląda tak (oczywiście upraszczam):

public class SomeOtherClass
{
    Connection conn;

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

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

}

SomeOtherClass może utworzyć własne połączenie z bazą danych w niektórych scenariuszach, ale może zaakceptować istniejące połączenie w innych scenariuszach, takie jak to, które mamy tutaj.

Jak widać, sekcja That zawiera wywołanie someMethod który akceptuje otwarte połączenie jako argument. someMethod przekazuje połączenie do lokalnego wystąpienia SomeOtherClass . SomeOtherClass miał finalize metoda zamykająca połączenie.

Teraz, po someMethod zwraca, instance kwalifikuje się do zbierania śmieci. Kiedy jest zbierany śmieci, jego finalize Metoda jest wywoływana przez wątek garbage collector, który zamyka połączenie.

Teraz wracamy do pętli for, która kontynuuje wykonywanie instrukcji SELECT przy użyciu tego samego połączenia, które może zostać zamknięte w dowolnym momencie przez wątek garbage collector.

Jeśli wątek garbage collector zamyka połączenie, gdy wątek aplikacji znajduje się w środku jakiejś metody konektora mysql, która opiera się na otwartym połączeniu, NullPointerException może wystąpić.

Usuwanie finalize metoda rozwiązała problem.

Nieczęsto nadpisujemy finalize w naszych klasach, co bardzo utrudniło zlokalizowanie błędu.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak zarządzać rolami użytkowników w bazie danych?

  2. Obejdź limit 61 tabel JOIN w MySQL, zagnieżdżając podzapytania w sobie nawzajem

  3. MYSQL:Tabela numerów sekwencyjnych

  4. Jak zaktualizować pole na podstawie jego aktualnej wartości w MySQL?

  5. Optymalizacja wyszukiwania MySQL za pomocą polubień i symboli wieloznacznych