在池中关闭JDBC连接


109

我们使用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:使用连接池时,是否应该在最后关闭连接?如果是这样,合并的目的就不会丢失吗?如果不是,那么DataSource如何知道何时释放Connection的特定实例并可以重用?我对此感到有些困惑,任何指针都表示赞赏。

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

谢谢-MS

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);
}

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

Answers:


121

使用连接池时,是否应该在最后关闭连接?如果是这样,合并的目的就不会丢失吗?如果不是,那么DataSource如何知道何时释放Connection的特定实例并可以重用?我对此感到有些困惑,任何指针都表示赞赏。

是的,当然您也需要关闭池化连接。实际上,它是实际连接的包装。它会在幕后将实际连接释放回池中。由池进一步决定实际的连接是实际上是关闭还是重新用于新getConnection()呼叫。因此,无论是否使用连接池,都应始终以相反的顺序在获取它们finallytry块中关闭所有JDBC资源。在Java 7中,可以使用try-with-resourcesstatement 进一步简化此操作。


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

这个例子很可怕。您仅需要DataSource在应用程序启动期间在整个应用程序范围的数据库配置类的某些构造函数/初始化中查找/初始化一次。然后,只需getConnection()在应用程序的整个生命周期中调用一个相同的数据源即可。无需同步或nullchecks。

也可以看看:


多数民众赞成在做什么(初始化一次),不是吗?ds是一个实例变量,而if(ds == null)...是初始化部分。
Manidip Sengupta 2011年

每次都用一种get方法进行检查getConnection()是很奇怪的。只需在c'tor或同一类的初始化块中进行操作,而无需同步/空检查。它只会被调用一次。有关更多提示和启动示例,您可能会发现本文很有用。
BalusC

优秀文章,BalusC。我正在处理的类使用DTO来实现数据层。我同意你的观点,初始化应该在构造函数中。现在,此类有大量方法,每个方法都将conn,stmt和rset作为局部变量,连接位于try块中,最后有1行调用csrClose(conn,stmt,rset),其中所有3已关闭(以相反顺序)。现在,在示例中开发的DTO是数据库表行的镜像。我们有带有联接(和其他子句)的复杂SQL查询,您是否有一篇有关如何为此类结果开发DAO的文章?
Manidip Sengupta 2011年

2
@yat:您必须close()在与获取/创建它们的位置finally相同的try块中调用所有它们。完全不管它是否是池连接。
BalusC 2013年

1
@iJava:该池是由一个完全不知道自己在做什么的业余爱好者编写的。忽略它,然后去一个真正的图书馆。例如HikariCP。
BalusC 2015年

22

池通常返回包装的Connection对象,在该对象中将覆盖close()方法,通常将Connection返回到池。调用close()可以,并且可能仍然需要。

close()方法可能看起来像这样:

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

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


我同意,我们确实有一个记录器,也可以在这里使用它。我需要研究一下如何包装对象,重写其close()方法,但仍保持相同的类名(连接)
Manidip Sengupta 2011-2-8

1
Calling close() is OK and probably still required.,不调用close将会泄漏连接,除非该池实施了一些恢复策略
svarog

0

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

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

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

您可以根据需要拥有任意数量的执行程序实例,但是没有人可以编写自己代表打开和关闭连接的代码。

方便地,这还使您可以从一组代码中记录所有SQL。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.