8

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

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

我的问题是,如果我不关闭我的继承 servlet 创建的ResultSets、PreparedStatements 和Statements,如果Connection创建它们的 s 始终关闭,那么最糟糕的情况是什么?

4

5 回答 5

11

Statement#close()的 javadoc说:

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

所以你不用担心关闭ResultSets,只要你总是及时关闭Statements即可。

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

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

您可以合理地解释为暗示任何陈述都将被关闭。查看开源 jTDS 驱动程序,并查看一个著名且昂贵的商业数据库的驱动程序,我可以看到他们正是这样做的。

于 2011-02-02T10:27:38.120 回答
5

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

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

但是,这仅在您当前的设置中有效。如果您的应用程序何时更改,您可能会因为没有关闭语句和结果集而面临问题。

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

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

于 2011-02-02T09:54:53.010 回答
1

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

于 2011-02-02T09:53:55.227 回答
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 中,您可以为不在 using(或者我应该说 try-with-resources)块中的可自动关闭的东西打开警告:

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

这些 using 块让您尝试最终自动关闭,并且只能与 AutoClosable 接口类型的东西一起使用

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

于 2014-11-05T09:31:01.117 回答
0

与数据库的连接并不是您的应用程序唯一需要占用的东西。还有其他资源处于危险之中。

如果您不自己释放它们,它们将在某个时间点被释放,但如果可以这样做,您应该这样做。

于 2011-02-02T09:55:42.117 回答