Java,ResultSet.close(),PreparedStatement.close() - 用于什么?

时间:2011-02-02 09:44:38

标签: java database memory-leaks connection-leaks

在我的网络应用程序中,我广泛使用数据库。

我有一个抽象的servlet,继承了所有需要数据库连接的servlet。该抽象servlet创建数据库连接,调用抽象方法,该方法必须由继承的servlet覆盖以执行其逻辑,然后关闭连接。我不使用连接池,因为我的应用程序将拥有非常有限的用户和操作。

我的问题是,如果我没有关闭我继承的servlet创建的ResultSetPreparedStatementStatement,那么最糟糕的情况是什么?创建它们的{1}}总是关闭的吗?

5 个答案:

答案 0 :(得分:9)

Statement#close()的javadoc说:

  

注意:当Statement对象关闭时,它的当前ResultSet对象(如果存在)也将关闭。

因此,只要您始终及时关闭语句,您就不必担心关闭ResultSet。

Connection#close()的javadoc没有做出相应的保证,但确实说:

  

立即释放此Connection对象的数据库和JDBC资源,而不是等待它们自动释放。

您可以合理地解释为暗示任何陈述将被关闭。看一下开源的jTDS驱动程序,并窥探一个众所周知且昂贵的商业数据库的驱动程序,我可以看到它们就是这样做的。

答案 1 :(得分:5)

我非常确定关闭连接会关闭相关的语句,结果集和其他相关对象。但是,所有这些都将消耗客户端上的资源,并且可能在数据库服务器上,直到连接关闭。

如果在您的情况下您知道您将很快关闭连接 ,您可能不会冒很大的风险,尽管我认为这不应被视为最佳做法。

但是这仅在您当前的设置中有效。 如果当您的应用程序发生变化时,您可能会遇到问题,因为您没有关闭您的语句和结果集。

虽然您不想使用连接池,但我认为即使用户/操作很少也是一个坏主意,因为打开数据库连接并不便宜。因此,即使在您的上下文中,连接池也可能有助于提高系统响应速度。

关于垃圾收集的说明。在关闭连接之前,未使用的语句或结果集可以进行GCed。 当涉及释放系统资源(如文件或更一般的非Java资源(例如数据库服务器上的游标))时,不应依赖JVM GC。例如,如果您的客户端应用程序打开了很多ResultSet但只使用了分配的堆内存的一小部分,那么当打开的游标将数据库服务器窒息时,GC将永远不会启动。

答案 2 :(得分:1)

AFAIK,由于绑定文件句柄,保存与给定语句关联的结果集所需的资源等,您最终会耗尽数据库服务器上的资源。可能存在智能驱动程序/数据库实现,确保一旦连接关闭,所有相关的资源都会被释放,但这不是规范的一部分,所以最终可能会让你感到厌烦。您的重写类无法关闭它们使用的结果集和语句的原因是什么?

答案 3 :(得分:1)

它的一些废话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;
    }
}

然后例如在Wrapped Connection中(这是包装数据库连接的东西):

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集的包装版本(我在此处未显示)等,并以类似的方式使用它。

我不知道其他人在使用什么,但在IDEA中,您可以为使用(或者我应该说是资源试用)块中的自动关闭内容启用警告:

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

这些使用块最后会自动关闭并且只能用于AutoClosable接口类型的东西

我不知道为什么在IDEA默认情况下这个警告没有打开,但是你去了。

答案 4 :(得分:0)

与数据库的连接并不是您的应用程序唯一的用途。还有其他资源受到威胁。

如果你不自己释放它们,它们会在某个时间点被释放,但如果能够这样做,你应该这样做。