Измина много време, откакто публикувах този въпрос и искам да публикувам отговор, който описва точния сценарий, довел до това сложно NullPointerException
.
Мисля, че това може да помогне на бъдещите читатели, които се сблъскат с такова объркващо изключение, да мислят извън кутията, тъй като имах почти всички основания да подозирам, че това е грешка в mysql конектора, въпреки че в крайна сметка не беше.
Докато разследвах това изключение, бях сигурен, че приложението ми не може да затваря връзката с DB, докато се опитва да чете данни от нея, тъй като моите DB връзки не се споделят между нишки и ако същата нишка затвори връзката и след това се опита да получи достъп би трябвало да бъде изведено различно изключение (някакво SQLException
). Това беше основната причина да подозирам грешка в конектора на mysql.
Оказа се, че има две нишки за достъп до една и съща връзка след всичко. Причината, поради която това беше трудно да се разбере, беше, че една от тези нишки беше нишка за събиране на боклук .
Връщайки се към кода, който публикувах:
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
метод в нашите класове, което направи много трудно намирането на грешката.