У меня есть этот фрагмент кода в серверной части веб-приложения. Иногда он выдает исключение NullPointerException в while (rs.next ()), иногда он выдает SQLException Operation, недопустимый после закрытия ResultSet, также в while (rs.next ()), но иногда он не вызывает никаких ошибок. Кто-нибудь знает, в чем проблема?
public HashSet<LocalDate> getAvailableDates(long listingNumber) throws SQLException{
Statement statement = null;
String getDatesAvail = "SELECT AVAILDATE FROM AVAILABILITY WHERE LISTINGNUM = " +listingNumber +";";
HashSet<LocalDate> avail = new HashSet<LocalDate>();
ResultSet rs = null;
try {
connect(); // Open dbConnection
statement = dbConnection.createStatement();
rs = statement.executeQuery(getDatesAvail);
System.out.println(getDatesAvail);
if (rs == null) {
System.out.println("Result set is null for avail");
}
while(rs.next()) {
avail.add(LocalDate.parse(rs.getString("AVAILDATE"), DateTimeFormatter.ofPattern("yyyy-MM-dd")));
}
} catch (SQLException e) {
System.out.println(e.getMessage());
e.printStackTrace();
throw new SQLException();
} finally {
if (rs != null) {
rs.close();
}
if (statement != null) {
statement.close();
}
if (dbConnection != null) {
dbConnection.close();
}
}
return avail;
}
Мой метод connect ():
private void connect() {
try {
Class.forName(DB_DRIVER);
} catch (ClassNotFoundException e) {
System.out.println(e.getMessage());
}
try {
dbConnection = DriverManager.getConnection( DB_CONNECTION, DB_USER, DB_PASSWORD);
} catch (SQLException e) {
e.printStackTrace();
}
}
Вот трассировка стека для NullPointerException:
java.lang.NullPointerException
at com.mysql.jdbc.ResultSetImpl.next(ResultSetImpl.java:6309)
at car_service.JDBCConnector.getAvailableDates(JDBCConnector.java:1170)
at car_service.JDBCConnector.getListing(JDBCConnector.java:974)
at car_service.ListingController.getListing(ListingController.java:347)
at car_service.ListingController.lambda$6(ListingController.java:94)
at spark.RouteImpl$1.handle(RouteImpl.java:72)
at spark.http.matching.Routes.execute(Routes.java:61)
at spark.http.matching.MatcherFilter.doFilter(MatcherFilter.java:130)
at spark.embeddedserver.jetty.JettyHandler.doHandle(JettyHandler.java:50)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1568)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
at org.eclipse.jetty.server.Server.handle(Server.java:530)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:347)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:256)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:124)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:247)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:140)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131)
at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:382)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:708)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:626)
at java.lang.Thread.run(Unknown Source)
Вот трассировка стека для SQLException:
java.sql.SQLException: Operation not allowed after ResultSet closed
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:965)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:898)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:887)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:861)
at com.mysql.jdbc.ResultSetImpl.checkClosed(ResultSetImpl.java:743)
at com.mysql.jdbc.ResultSetImpl.next(ResultSetImpl.java:6288)
at car_service.JDBCConnector.getAvailableDates(JDBCConnector.java:1170)
at car_service.JDBCConnector.getListing(JDBCConnector.java:974)
at car_service.ListingController.getListing(ListingController.java:347)
at car_service.ListingController.lambda$6(ListingController.java:94)
at spark.RouteImpl$1.handle(RouteImpl.java:72)
at spark.http.matching.Routes.execute(Routes.java:61)
at spark.http.matching.MatcherFilter.doFilter(MatcherFilter.java:130)
at spark.embeddedserver.jetty.JettyHandler.doHandle(JettyHandler.java:50)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1568)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
at org.eclipse.jetty.server.Server.handle(Server.java:530)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:347)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:256)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:124)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:247)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:140)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131)
at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:382)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:708)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:626)
at java.lang.Thread.run(Unknown Source)
@Berger Из документация: объект ResultSet, содержащий данные, созданные данным запросом; никогда не обнуляться
@TimBiegeleisen: Да, я тоже это видел, но комментарий должен был указать на очевидный недостаток (может помочь позже в ситуациях, когда может произойти null)
Не могли бы вы включить соответствующую часть трассировки стека?
Пожалуйста, опубликуйте полную трассировку стека исключений и рассмотрите возможность публикации минимальный воспроизводимый пример, например, учитывая, что dbConnection не является локальным методом, вы вполне можете страдать от состояния гонки из-за неправильного совместного использования соединения с несколькими потоками одновременно.
@TimBiegeleisen Вы предполагаете, что OP использует драйвер JDBC, соответствующий спецификации. Хотя большинство из них поступают в этом отношении, мы не можем быть на 100% уверены, что эта реализация не возвращает здесь null.
@MarkRotteveel Я бы не удивился. Я бы хотел увидеть трассировку стека.
Какую версию MySQL Connector / J вы используете? Вы уверены, что не используете одно и то же соединение JDBC из нескольких потоков? Это могло объяснить периодические ошибки. Возможно, вы захотите сохранить соединение локальным для метода, а не как поле в вашем классе.
Я добавил трассировку стека исключений.
@Berger Если rs был нулевым, ResultSetImpl.next() не мог появиться в трассировке стека.
@MarkRotteveel Я использую MySQL Connector / J 5.1.46. Я не использую многопоточность в своем коде.
Уверены ли вы? Веб-приложения обычно являются многопоточными при обработке нескольких запросов. В любом случае рассмотрите возможность изменения кода, чтобы соединение создавалось и сохранялось локально для метода, а не делиться им в своем классе. В любом случае вам действительно нужно опубликовать минимальный воспроизводимый пример. Здесь слишком много неизвестных, и поведение действительно предполагает, что вы используете одно и то же соединение параллельно.
Я с @MarkRotteveel. Я столкнулся с подобной проблемой почти два десятилетия назад, когда поделился одним объектом подключения db с несколькими потоками. Что ж, это было веб-приложение. В то время я был достаточно наивен, чтобы не понимать потоковую модель веб-сервера. Мой код наугад преподнес много сюрпризов. Хотя в то время я не понимал точной причины, я изменил соединение db на метод local и выжил!
@Lyndt Конечно, вы используете потоки. Это веб-сервер. Посмотрите на трассировку стека. Повсюду нити.
И любой драйвер JDBC, доставивший null, проработает около пяти минут, прежде чем будет исправлен. Никто не проверяет это на ноль.




Ваш код не является потокобезопасным. Вы допустили ошибку, сохранив объект Connection как переменную-член. Это должна быть локальная переменная, открываться и закрываться в методе с лежащим в основе пулом соединений.
Если
rs- этоnull, вы должны остановить свою логику (например,returnиз вашего метода).