Исключения в while (rs.next ())

У меня есть этот фрагмент кода в серверной части веб-приложения. Иногда он выдает исключение 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)

Если rs - это null, вы должны остановить свою логику (например, return из вашего метода).

Arnaud 22.05.2018 16:49

@Berger Из документация: объект ResultSet, содержащий данные, созданные данным запросом; никогда не обнуляться

Tim Biegeleisen 22.05.2018 16:51

@TimBiegeleisen: Да, я тоже это видел, но комментарий должен был указать на очевидный недостаток (может помочь позже в ситуациях, когда может произойти null)

Arnaud 22.05.2018 16:53

Не могли бы вы включить соответствующую часть трассировки стека?

Tim Biegeleisen 22.05.2018 16:54

Пожалуйста, опубликуйте полную трассировку стека исключений и рассмотрите возможность публикации минимальный воспроизводимый пример, например, учитывая, что dbConnection не является локальным методом, вы вполне можете страдать от состояния гонки из-за неправильного совместного использования соединения с несколькими потоками одновременно.

Mark Rotteveel 22.05.2018 17:04

@TimBiegeleisen Вы предполагаете, что OP использует драйвер JDBC, соответствующий спецификации. Хотя большинство из них поступают в этом отношении, мы не можем быть на 100% уверены, что эта реализация не возвращает здесь null.

Mark Rotteveel 22.05.2018 17:05

@MarkRotteveel Я бы не удивился. Я бы хотел увидеть трассировку стека.

Tim Biegeleisen 22.05.2018 17:06

Какую версию MySQL Connector / J вы используете? Вы уверены, что не используете одно и то же соединение JDBC из нескольких потоков? Это могло объяснить периодические ошибки. Возможно, вы захотите сохранить соединение локальным для метода, а не как поле в вашем классе.

Mark Rotteveel 22.05.2018 17:19

Я добавил трассировку стека исключений.

Lyndt 22.05.2018 17:20

@Berger Если rs был нулевым, ResultSetImpl.next() не мог появиться в трассировке стека.

user207421 22.05.2018 17:25

@MarkRotteveel Я использую MySQL Connector / J 5.1.46. Я не использую многопоточность в своем коде.

Lyndt 22.05.2018 17:26

Уверены ли вы? Веб-приложения обычно являются многопоточными при обработке нескольких запросов. В любом случае рассмотрите возможность изменения кода, чтобы соединение создавалось и сохранялось локально для метода, а не делиться им в своем классе. В любом случае вам действительно нужно опубликовать минимальный воспроизводимый пример. Здесь слишком много неизвестных, и поведение действительно предполагает, что вы используете одно и то же соединение параллельно.

Mark Rotteveel 22.05.2018 17:27

Я с @MarkRotteveel. Я столкнулся с подобной проблемой почти два десятилетия назад, когда поделился одним объектом подключения db с несколькими потоками. Что ж, это было веб-приложение. В то время я был достаточно наивен, чтобы не понимать потоковую модель веб-сервера. Мой код наугад преподнес много сюрпризов. Хотя в то время я не понимал точной причины, я изменил соединение db на метод local и выжил!

RaviH 22.05.2018 17:28

@Lyndt Конечно, вы используете потоки. Это веб-сервер. Посмотрите на трассировку стека. Повсюду нити.

user207421 22.05.2018 17:29

И любой драйвер JDBC, доставивший null, проработает около пяти минут, прежде чем будет исправлен. Никто не проверяет это на ноль.

user207421 22.05.2018 22:29
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
15
221
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Другие вопросы по теме