Прежде всего, не закрывайте это как дубликат What's NullPointerException
и как его исправить. Я знаю, что такое NullPointerException
, и я знаю, как его решить в моем собственном коде, но не тогда, когда он генерируется mysql-connector-java-5.1.36-bin.jar, который я не имеют контроля над.
Мы столкнулись с исключением при выполнении общего запроса БД на базе 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();
res.getByte(8)
вызывает NullPointerException
со следующим стеком вызовов:
com.mysql.jdbc.ResultSetImpl.checkColumnBounds(ResultSetImpl.java:763) com.mysql.jdbc.ResultSetImpl.getStringInternal(ResultSetImpl.java:5251) com.mysql.jdbc.ResultSetImpl.getString(ResultSetImpl.java:5173) com.mysql.jdbc.ResultSetImpl.getByte(ResultSetImpl.java:1650) org.apache.tomcat.dbcp.dbcp2.DelegatingResultSet.getByte(DelegatingResultSet.java:206) org.apache.tomcat.dbcp.dbcp2.DelegatingResultSet.getByte(DelegatingResultSet.java:206)
Я искал исходный код соответствующей версии mysql-коннектора и нашел это (взято из здесь):
756 protected final void checkColumnBounds(int columnIndex) throws SQLException {
757 synchronized (checkClosed().getConnectionMutex()) {
758 if ((columnIndex < 1)) {
759 throw SQLError.createSQLException(
760 Messages.getString("ResultSet.Column_Index_out_of_range_low",
761 new Object[] { Integer.valueOf(columnIndex), Integer.valueOf(this.fields.length) }), SQLError.SQL_STATE_ILLEGAL_ARGUMENT,
762 getExceptionInterceptor());
763 } else if ((columnIndex > this.fields.length)) {
764 throw SQLError.createSQLException(
765 Messages.getString("ResultSet.Column_Index_out_of_range_high",
766 new Object[] { Integer.valueOf(columnIndex), Integer.valueOf(this.fields.length) }), SQLError.SQL_STATE_ILLEGAL_ARGUMENT,
767 getExceptionInterceptor());
768 }
769
770 if (this.profileSql || this.useUsageAdvisor) {
771 this.columnUsed[columnIndex - 1] = true;
772 }
773 }
774 }
Как вы можете видеть, исключение происходит в этой строке:
} else if ((columnIndex > this.fields.length)) {
что означает this.fields
, как-то стало null
.
Самое близкое, что я мог найти, это этот вопрос, на который нет ответа.
Я подозреваю, что проблема не в запросе, который я опубликовал. Возможно, что-то пошло не так с экземпляром Connection
из-за некоторых других операторов, которые мы запускаем в одном и том же соединении. Все, что я могу сказать, это то, что мы закрываем каждое утверждение сразу после его выполнения и считываем данные из ResultSet
.
EDIT (1/19/2017):
Я не смог воссоздать ошибку в моей среде разработки. Я думал, что это может быть некоторая ошибка mysql-connect, вызванная, когда одно и то же соединение используется в течение длительного времени. Я ограничил вышеуказанный цикл загрузкой не более 6 элементов за раз. Кроме того, мы обновили версию mysql-коннектора до 5.1.40.
Мы по-прежнему видим NullPointerException
в ResultSetImpl
, но на этот раз в другом месте.
Трассировка стека:
com.mysql.jdbc.ResultSetImpl.getStringInternal(ResultSetImpl.java:5294) com.mysql.jdbc.ResultSetImpl.getString(ResultSetImpl.java:5151) org.apache.tomcat.dbcp.dbcp2.DelegatingResultSet.getString(DelegatingResultSet.java:198) org.apache.tomcat.dbcp.dbcp2.DelegatingResultSet.getString(DelegatingResultSet.java:198)
который на этот раз исключает исключение из-за одного из наших вызовов res.getString()
(я не знаю, какой из них).
Я нашел источник mysql-connector-java-5.1.40-bin.jar здесь и соответствующий код:
5292 // Handles timezone conversion and zero-date behavior
5293
5294 if (checkDateTypes && !this.connection.getNoDatetimeStringSync()) {
5295 switch (metadata.getSQLType()) {
что означает, что this.connection
равно null. this.connection
- это переменная экземпляра типа MySQLConnection
, которая инициализируется в конструкторах ResultSetImpl
и устанавливается только в null при вызове public void realClose(boolean calledExplicitly)
(она вызывается public void close()
), которая, согласно документации в источнике, вызывается при вызове ResultSet.close()
). Мы, конечно, не закрываем ResultSet
до чтения всех данных из него.
Любые идеи о том, как действовать?