Java,ResultSet.close(),PreparedStatement.close() – 为什么?

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

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

我的问题是,如果我没有关闭我inheritance的servlet创建的ResultSetPreparedStatementStatement ,如果创建它们的Connection总是关闭的话,会发生什么最糟糕的事情?

Statement#close()的javadoc说:

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

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

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

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

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

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

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

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

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

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

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

它有点废话Api – 最后你写了一堆锅炉板代码。

我认为最好的方法是将类包装起来并使其处理,以便处理连接来处理其他内容(因为你可以跟踪在包装调用的路上所做的事情)。

只要你有一个可以生成你在类中委派方法的IDE,那么包装这样的东西是一项微不足道的工作。

我没有意识到需要处理的所有额外的东西,但只是发现有人在这里做,但是我很幸运,因为我们已经包装基本类将所有恼人的exception转换为RuntimeExceptions并提供更高级别的sql操作。

我做了一个小课程来跟踪不同的东西:

 public class CleanupList { private final ArrayList _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 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 trackForDisposal(T statement) { return _cleanupList.track(statement); } .... lots more methods } 

然后,您也可以将相同的列表传递给PreparedStatement / Result集的包装版本(我在此未显示)等,并以类似的方式使用它。

我不知道其他人在使用什么,但是在IDEA中你可以为不能使用(或者我应该说是try-with-resources)块的可自动关闭的东西打开警告:

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

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

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

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

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