Java, ResultSet.close (), PreparedStatement.закрыть() - зачем?

в моем веб-приложении я широко использую базу данных.

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

мой вопрос, что самое худшее, что может случиться, если не закрыть ResultSets,PreparedStatements и Statements, которые создают мои наследующие сервлеты, если Connections, которые их создают, всегда закрыты?

5 ответов


javadoc для оператор#close () говорит:

Примечание: когда объект оператора закрыт, его текущий объект ResultSet, если он существует, также закрыт.

поэтому вам не нужно беспокоиться о закрытии наборов результатов, если вы всегда своевременно закрываете заявления.

javadoc для соединение#close () не дает соответствующей гарантии, но говорит:

релизы база данных этого объекта подключения и ресурсы JDBC немедленно вместо ожидания их автоматического освобождения.

который вы могли бы разумно истолковать как подразумевающий, что любые утверждения будут закрыты. Глядя на драйвер jtds с открытым исходным кодом и заглядывая в драйвер для известной и дорогой коммерческой базы данных, я вижу, что они делают именно это.


Я уверен, что закрытие соединения закроет связанные операторы, наборы результатов и другие связанные объекты. Однако все это будет потреблять ресурсы как на клиенте, так и на сервере базы данных, пока соединение не будет закрыто.

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

однако это допустимо только в текущей обстановке. если когда ваше приложение изменится, вы можете столкнуться с проблемами, потому что вы не закрыли свои заявления и наборы результатов.

хотя вы не хотите использовать пул соединений, я думаю, что это плохая идея даже с несколькими пользователями/операциями, поскольку открытие соединения с базой данных не дешево. Таким образом, даже в вашем пуле контекстных соединений может помочь получить вашу систему более отзывчивой.

просто заметка о сборе мусора. Перед закрытием соединения неиспользуемые операторы или ResultSets мая быть GCed. но когда дело доходит до освобождения системных ресурсов, таких как файлы или, как правило, не java-ресурсы (такие как курсоры на сервере баз данных), на JVM GC не следует полагаться. Например, если ваше клиентское приложение открывает много наборов результатов, но использует только небольшую часть выделенной памяти кучи, GC никогда не будет работать, пока сервер базы данных передохли открытыми курсорами.


AFAIK, вы исчерпаете ресурсы на сервере базы данных из-за привязанных дескрипторов файлов, ресурсов, необходимых для хранения результирующего набора, связанного с данным оператором и т. д. Там могут быть интеллектуальные реализации драйверов / баз данных, которые гарантируют, что как только соединение будет закрыто, все связанные ресурсы будут освобождены, но это не является частью спецификации, поэтому в конечном итоге может прийти и укусить вас в долгосрочной перспективе. Любая причина, по которой ваши переопределяющие классы не могут закрыть результирующие наборы и операторы, которые они используют?


его немного дерьма Api-заканчивается тем, что вы пишете нагрузку кода котельной плиты.

Я считаю, что лучший способ пойти-это обернуть классы и сделать так, чтобы утилизация соединения утилизировала другие вещи (так как вы можете отслеживать, что делается на выходе из обернутых вызовов).

пока у вас есть IDE, которая может генерировать методы делегирования в классе, это тривиальная работа, чтобы обернуть такие вещи.

Я не понял все дополнительные вещи необходимо утилизировать, но просто заметил, что кто-то делает это здесь, однако мне повезло, поскольку мы уже оборачиваем основные классы, чтобы превратить все раздражающие исключения в RuntimeExceptions и предоставить еще несколько операций sql высокого уровня.

Я сделал небольшой класс для отслеживания разных штуки:

public class CleanupList
{
    private final ArrayList<AutoCloseable> _disposables;

    public CleanupList()
    {
        _disposables = new ArrayList<>();
    }

    public void cleanup()
    {
        for(AutoCloseable closeable : _disposables){
            //it sucks that they put an exception on this interface
            //if anyone actually throws an exception in a close method then there's something seriously wrong going on
            //they should have copied the c# more closely imo as it has nicer syntax aswell
            try
            {
                closeable.close();
            }
            catch (Exception e)
            {
                throw new RuntimeException(e);
            }
        }

        _disposables.clear();
    }

    public <T extends AutoCloseable> T track(T statement)
    {
        _disposables.add(statement);
        return statement;
    }
}

а затем, например, в обернутом соединении (которое является чем-то, что обертывает соединение с базой данных):

public class WrappedConnection implements AutoCloseable
{
    private final CleanupList _cleanupList;
    private Connection _connection;

    public WrappedConnection(Connection connection)
    {
        _connection = connection;
        _cleanupList = new CleanupList();
    }

    public void close()
    {
        try
        {
            _connection.close();
            _cleanupList.cleanup();
        }
        catch (SQLException e)
        {
            throw new RuntimeException(e);
        }
    }

    public PreparedStatement prepareStatement(String sql)
    {
        try
        {
            return trackForDisposal(_connection.prepareStatement(sql));
        } 
        catch (SQLException e)
        {
            throw new RuntimeException(e);
        }
    }

    private <T extends AutoCloseable> T trackForDisposal(T statement)
    {
        return _cleanupList.track(statement);
    }

.... lots more methods
}

вы можете также передать то же самое перечислите в обернутые версии PreparedStatement / Result sets (которые я здесь не показывал) и т. д. и используйте его аналогичным образом.

Я не знаю, что другие люди используют, но в идее вы можете включить предупреждение для автоматического закрытия материала, который не находится в блоке использования (или я должен сказать try-with-resources):

try(SomethingThatNeedsClosing somethingThatNeedsClosing = new SomethingThatNeedsClosing()){
    //do stuff
}

эти блоки с использованием вы пытаетесь, наконец, закрыть автоматически и может использоваться только с вещами типа автоклавного интерфейса

Я не знаю, почему это предупреждение не включается по умолчанию в IDEA, но вы идете.


подключение к базе данных-это не единственное, что связывает ваше приложение. На карту поставлены другие ресурсы.

Они будут освобождены в какой-то момент времени, если вы не отпустите их сами, но если вы можете это сделать, вы должны.