在连接池中关闭JDBC连接

126

我们使用JDBC的标准代码部分为...

Connection conn = getConnection(...);
Statement  stmt = conn.conn.createStatement (ResultSet.TYPE_SCROLL_INSENSITIVE,
                                                ResultSet.CONCUR_READ_ONLY);
ResultSet  rset = stmt.executeQuery (sqlQuery);

// do stuff with rset

rset.close(); stmt.close(); conn.close();

问题1:在使用连接池时,我们是否应该在最后关闭连接?如果是这样,那么连接池的目的不就失去了吗?如果不关闭,数据源如何知道什么时候可以重用特定的连接实例?我对此有些困惑,希望能得到一些指导。

问题2:下面的方法是否接近于标准?看起来像是尝试从连接池中获取连接,如果无法建立数据源,则使用老式的DriverManager。我们甚至不确定哪一部分会在运行时执行。重复上面的问题,我们是否应该关闭从这样的方法得到的连接?

synchronized public Connection getConnection (boolean pooledConnection)
                                                        throws SQLException {
        if (pooledConnection) {
                if (ds == null) {
                        try {
                                Context envCtx = (Context)
                                        new InitialContext().lookup("java:comp/env");
                                ds = (DataSource) envCtx.lookup("jdbc/NamedInTomcat");
                                return ds.getConnection();
                        } catch (NamingException e) {
                                e.printStackTrace();
                }}
                return (ds == null) ? getConnection (false) : ds.getConnection();
        }
        return DriverManager.getConnection(
                "jdbc:mysql://"+ipaddy+":"+dbPort +"/" + dbName, uName, pWord);
}

编辑:我认为我们得到的是池化连接,因为我们没有看到堆栈跟踪。

3个回答

139
当使用连接池时,最后应该关闭连接吗?如果是,那么池的目的不就失去了吗?如果不是,数据源如何知道特定的连接实例何时被释放并可以被重用?我对此有些困惑,请指点一下。 是的,当然也需要关闭池化连接。它实际上是实际连接的包装器。它将在底层将实际连接释放回池中。由池决定实际连接是否会真正关闭或者被重用于新的getConnection()调用。因此,无论您是否使用连接池,都应该始终在获取它们的try块的finally块中按相反顺序关闭所有JDBC资源。在Java 7中,可以通过使用try-with-resources语句进一步简化这个过程。
以下方法是否接近标准?看起来像是从池中获取连接的尝试,如果无法建立DataSource,则使用老式的DriverManager。我们甚至不确定哪个部分在运行时执行。重复上面的问题,应该关闭这种方法返回的连接吗?
这个例子非常可怕。您只需要在应用程序启动期间的某个构造函数/应用程序范围的DB配置类的初始化中查找/初始化DataSource一次。然后在应用程序的其余生命周期中只需调用同一个数据源上的getConnection()。不需要同步或空检查。
另请参见:
- 在多线程系统中使用静态java.sql.Connection实例是否安全? - 我正在使用JDBC连接池吗?

尽管这个答案似乎已经被接受并投票支持,但我看到三个答案之间存在不同的观点。我理解我们不应该关闭连接(从连接池中获取),而是关闭其他资源(语句,结果集)。我应该还是不应该这样做? - yathirigan
3
你必须在同一个try块中获取/创建它们的地方的finally块中调用close()方法来关闭它们所有。这与连接是否为池化连接完全无关。 - BalusC
我已经阅读了你的回答。现在我尝试开始使用hsqldb,我需要一个连接池。我只找到了一个适用于这个数据库的连接池示例。https://sridharrao85.wordpress.com/2011/07/20/sample-connection-pool-implementation/ 然而,据我所知,如果我们从池中关闭连接,那么池将无法工作。我认为应该使用pool.releaseConnection(Connection connection)。你能发表一下评论吗? - user2022068
1
@iJava:那个连接池是由一个业余爱好者编写的,他完全不知道自己在做什么。不要使用它,而要选择一个真正的库。例如HikariCP。 - BalusC
谢谢您抽出时间查看。我会尝试使用HikariCP和commons-dbcp。 - user2022068
显示剩余5条评论

26

连接池通常会返回一个包装的Connection对象,其中close()方法被覆盖,通常会将Connection返回给池。调用close()是可以的,而且可能仍然需要。

一个close()方法可能会像这样:

public void close() throws SQLException {
  pool.returnConnection(this);
}

对于你的第二个问题,你可以添加一个日志记录器来显示底部块是否运行。我想你只希望数据库连接的配置以某种方式运行。我们仅使用一个池来访问数据库。无论哪种方式,关闭连接都非常重要以防止泄漏。


我同意,我们确实有一个记录器,这也可以在这里使用。我需要研究一下如何包装一个对象,覆盖它的close()方法,但仍然保持相同的类名(Connection)。 - Manidip Sengupta
1
调用close()是可以的,而且可能仍然需要。不调用close将会泄漏连接,除非池实现了某些恢复策略。 - svarog

0

实际上,连接管理的最佳方法是不将它们分配给任何代码。

创建一个SQLExecutor类,它是唯一打开和关闭连接的位置。

整个应用程序将语句传递到执行器中,而不是从池中获取连接并在各个地方进行管理(或错误管理)。

您可以拥有任意数量的执行器实例,但没有人应编写打开和关闭连接的代码。

方便的是,这还可以让您从单个代码集合记录所有SQL。


这似乎是一个合理的想法,至少对于某些应用程序来说是如此,但它与此处提出的问题只有些许关联。您可能是在特别回答问题2,但即使在这种情况下,您所规定的方法也并不排除完全使用连接,只是在SQLExectutor类之外。因此,您可以考虑在SQLExecutor类的上下文中提出问题。 - McKay G

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接