【问题标题】:Closing JDBC Connections in Pool关闭池中的 JDBC 连接
【发布时间】:2011-06-23 17:40:45
【问题描述】:

我们使用 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();

问题一:在使用Connection Pool时,应该在最后关闭Connection吗?如果是这样,是不是失去了汇集的目的?如果没有,DataSource 如何知道某个特定的 Connection 实例何时被释放并可以重用?我对此有点困惑,任何指针表示赞赏。

问题 2: 以下方法是否接近标准?看起来像是试图从池中获取连接,如果无法建立 DataSource,请使用老式的 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);
}

编辑:我认为我们正在获得池连接,因为我们没有看到堆栈跟踪。

【问题讨论】:

    标签: java mysql jdbc connection-pooling


    【解决方案1】:

    在使用连接池时,是否应该在最后关闭连接?如果是这样,是不是失去了汇集的目的?如果没有,DataSource 如何知道某个特定的 Connection 实例何时被释放并可以重用?我对此有点困惑,任何指针表示赞赏。

    是的,当然您还需要关闭池连接。它实际上是实际连接的包装器。它将在幕后释放实际连接回池。由池进一步决定实际连接是实际上关闭还是重新用于新的getConnection() 调用。因此,无论您是否使用连接池,您都应该始终关闭您已获取的try 块的finally 块中的所有JDBC 资源他们。在 Java 7 中,这可以通过使用 try-with-resources 语句进一步简化。


    以下方法是否接近标准?看起来像是试图从池中获取连接,如果无法建立 DataSource,请使用老式的 DriverManager。我们甚至不确定哪个部分在运行时被执行。重复上面的问题,是否应该关闭这种方法产生的连接?

    这个例子很吓人。您只需要在应用程序启动期间在应用程序范围的数据库配置类的某些构造函数/初始化中查找/初始化一次DataSource。然后,在应用程序的剩余生命周期中,只需在同一个数据源上调用getConnection()。不需要同步也不需要空检查。

    另见:

    【讨论】:

    • 这就是它在做什么(初始化一次),不是吗? ds 是实例变量,if (ds == null) ... 是初始化部分。
    • 在像getConnection() 这样的get 方法中每次 进行检查很奇怪。只需在同一类的 c'tor 或初始化块中执行此操作,无需同步/空检查。它只会被调用一次。如需更多提示和启动示例,您可能会发现 this article 很有用。
    • 优秀的文章,BalusC。我正在处理的类几乎使用 DTO 实现了数据层。我同意你的观点,初始化应该在构造函数中。现在,这个类有很多方法,每个方法都有 conn、stmt 和 rset 作为局部变量,连接在 try 块中,最后有一个 1 行调用 csrClose (conn, stmt, rset),其中所有 3关闭(以相反的顺序)。现在,您在示例中开发的 DTO 是数据库表行的镜像。我们有复杂的带有连接(和其他子句)的 SQL 查询,你有一篇关于如何为这样的结果开发 DAO 的文章吗?
    • @yat:您必须在与您获取/创建它们的位置相同的try 块的finally 块中对所有它们调用close()。这完全不管它是否是池连接。
    • @iJava:这个池是由一个完全不知道自己在做什么的业余爱好者编写的。忽略它,去一个真正的图书馆。例如。 HikariCP。
    【解决方案2】:

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

    close() 方法可能如下所示:

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

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

    【讨论】:

    • 我同意,我们确实有一个记录器,也可以在这里使用。我需要研究一下如何包装一个对象,覆盖它的 close() 方法,但仍然保持相同的类名 (Connection)
    • Calling close() is OK and probably still required.,不调用close会泄露连接,除非池实现了一些恢复策略
    【解决方案3】:

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

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

    然后应用程序的整个其余部分将语句泵入执行程序,而不是从池中获取连接并在各处进行管理(或管理不善)。

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

    方便的是,这还允许您从一组代码中记录所有 SQL。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-09
      • 1970-01-01
      • 2011-07-07
      • 1970-01-01
      • 2016-07-06
      • 2013-04-12
      • 2010-10-05
      • 2011-10-10
      相关资源
      最近更新 更多