简体   繁体   English

JDBC连接池设计

[英]JDBC connection pool design

I have developed a JDBC connection pool using synchronized methods like getConnection and returnConnection. 我已经使用诸如getConnection和returnConnection之类的同步方法开发了JDBC连接池。 This works well and it is fast enough for my purposes. 这很好用,并且对于我的目的来说足够快。 Now the problem happens when this connection pool has to be shared in other packages of our application and so other developers will make use it as well. 现在,当必须在我们应用程序的其他程序包中共享此连接池时,就会发生问题,因此其他开发人员也将使用它。 I feel it is a bit confusing as they always need to perform a returnConnection and I am afraid they may forget to do so. 我感到有些困惑,因为他们总是需要执行returnConnection,而且恐怕他们可能会忘记这样做。

Thinking about it I came up with the idea to expose only only method in my connection pool and force the other developers to encapsulate their code and so I handle the getConnection / returnConnection inside the connection pool. 考虑一下,我想到了只在我的连接池中公开方法并强迫其他开发人员封装其代码的想法,因此我在连接池中处理了getConnection / returnConnection。

It would be something like this: 就像这样:

public MyConnectionPool {

private Connection getConnection() {
    //return connection
}

private void returnConnection(Connection connection) {
     //add connection to list
}

public void executeDBTask(DBTaskIF task) {
    Connection connection = getConnection();
    task.execute(connection);
    returnConnection(connection);
}

} }

where: 哪里:

public interface DBTaskIF {
  public execute(Connection connection);
}

with an example of this DBTaskIF: 带有以下DBTaskIF的示例:

connectionPool.executeDBTask( new DBTaskIF() {
public void execute(Connection connection) {

PreparedStatement preStmt = null;
try {
    preStmt = connection.prepareStatement(Queries.A_QUERY);
    preStmt.setString(1, baseName);
    preStmt.executeUpdate();
} finally {
    if(preStmt!=null) {
    try {
        preStmt.close();
    } catch (SQLException e) {
        log.error(e.getStackTrace());
    }
}
}}});

I hope you can get the idea. 希望您能明白。 What I want to know is your opinion about this approach. 我想知道的是您对这种方法的看法。 I want to propose this to the development team and I worry some one comes up saying that this is not standard or OOP or something else... Any comments are much appreciated. 我想向开发团队提出这个建议,但我担心有人会说这不是标准或OOP或其他东西。

I feel it is a bit confusing as they always need to perform a returnConnection and I am afraid they may forget to do so. 我感到有些困惑,因为他们总是需要执行returnConnection,而且恐怕他们可能会忘记这样做。

Thinking about it I came up with the idea to expose only only method in my connection pool and force the other developers to encapsulate their code and so I handle the getConnection returnConnection inside the connection pool. 考虑一下,我想到了只在连接池中公开方法,并迫使其他开发人员封装其代码的想法,因此我在连接池中处理getConnection returnConnection。

I'm concerned with this statement. 我对此声明感到担忧。 APIs should not (never?) assume that whoever uses them will do so in some way that is not enforced contractually by whichever method it exposes. API不应(永远不会?)假设使用它们的人会以某种方式执行此操作,而无论使用哪种方法,API都不会通过合同强制实施这些方法。

And java.sql.Connection is a widely used interface so you'll be making enemies by telling people how to use it with your pool. 而且java.sql.Connection是一个广泛使用的接口,因此您将通过告诉人们如何在池中使用它来使自己成为敌人。

Instead, you should assume that your Connection instances will be used correctly, ie, that they will be closed (connection.close() in a finally block) once their use is over (see, for instance, Connecting with DataSource Objects ): 相反,您应该假定将正确使用Connection实例, 即,一旦使用结束 (例如,参见与数据源对象 连接),它们将被关闭(在finally块中为connection.close() ):

   Connection con;
   PreparedStatement stmt;
   try {
        con = pool.getConnection();
        con.setAutoCommit(false);
        stmt = con.prepareStatement(...);
        stmt.setFloat(1, ...);
        stmt.setString(2, ...);
        stmt.executeUpdate();

        con.commit();
        stmt.close();
    } catch (SQLException e) {
        con.rollback();
    } finally {
        try {
           if(con!=null) 
               con.close();
           if(stmt!=null) {
               stmt.close();
        } catch (SQLException e) {
             ...
        } finally {

        }
    }

And the Connection implementation of your pool should be recycled when closed. 关闭时,应该回收池的Connection实现。

I second @lreeder's comment that you're really reinventing the wheel here and that most connection pools already available are definitely fast enough for most purposes, and underwent many fine tweakings over time. 我第二个@lreeder的评论是,您实际上是在重新发明轮子,并且大多数现有连接池对于大多数用途而言绝对足够快,并且随着时间的推移进行了许多细微调整。 This also applies to embedded databases. 这也适用于嵌入式数据库。

Disclaimer; 免责声明; this is just my opinion, but I have written custom connection pools before. 这只是我的意见,但是我之前已经编写了自定义连接池。 I find Java code where you have to create inner class impls a little clunky. 我发现必须在其中创建内部类的Java代码有点笨拙。 However in Java8 lambda or Scala anonymous functions this would be a clean design. 但是,在Java8 lambda或Scala匿名函数中,这将是一个干净的设计。 I probably would just expose returnConnection() as a public method and allow callers to use it directly. 我可能只是将returnConnection()公开为一个公共方法,并允许调用者直接使用它。

Third option: use a utility class that takes care of most of the administration. 第三种选择:使用负责大多数管理工作的实用程序类。
Not only forgetting to close a Connection can cause trouble, but also forgetting to close a Statement or a Resultset can cause trouble. 忘记关闭连接不仅会引起麻烦,而且忘记关闭语句或结果集也会引起麻烦。 This is similar to using various IO streams in a method: at some point you make an extra utility class in which you register all opened IO streams so that if an error occurs, you can call close in the utility class and be sure that all opened IO streams are closed. 这类似于在方法中使用各种IO流:在某个时候,您要创建一个额外的实用程序类,在其中注册所有打开的IO流,以便在发生错误时可以在该实用程序类中调用close并确保所有打开的IO流已关闭。
Such a utility class will not cover all use cases but there is always the option to write another one for other (more complex) use cases. 这样的实用程序类不会涵盖所有用例,但始终可以选择为其他(更复杂的)用例编写另一个用例。 As long as they keep the same kind of contract, using them should just make things easier (and will not feel forced). 只要它们保持相同的合同,使用它们就应该使事情变得容易(不会感到被迫)。

Wrapping or proxying a Connection to change the behavior of close to return the Connection to the pool is in general how connection pools prevent connections from actually being closed. 包装或代理连接以更改close行为,以将连接返回到池通常是连接池阻止连接实际关闭的方式。 But if a connection pool is not used, the application is usually written in a different manner: a connection (or two) is created (at startup) and used wherever a query is executed and the connection is only closed when it is known that a connection is not needed for a while (at shutdown). 但是,如果不使用连接池,则通常以不同的方式编写应用程序:(在启动时)创建一个(或两个)连接,并在执行查询的任何地方使用该连接,并且仅当知道暂时不需要连接(关闭时)。 In contrast, when a pool is used, the connection is "closed" as soon as possible so that other processes can re-use the connection. 相反,使用池时,连接将尽快“关闭”,以便其他进程可以重新使用该连接。 This together with the option to use a utility class, made me decide to NOT wrap or proxy a connection, but instead let the utility class actually return the connection to the pool if a pool was used (ie not call connection.close() but call pool.release(connection) ). 这与使用实用程序类的选项一起,使我决定不包装或代理连接,但是如果使用了池,则让实用程序类将连接实际返回到池(即不调用connection.close()而是调用pool.release(connection) )。 Usage example of such a utility class is here , the utlity class itself is here . 此类实用程序类的用法示例在此处 ,实用程序类本身在此处

Proxying causes small delays which is why for example BoneCP decided to wrap Connection and Datasource (wrapping causes very little overhead). 代理会导致较小的延迟,这就是为什么例如BoneCP决定包装Connection和Datasource的原因(包装导致很少的开销)。 The Datasource interface changes with each Java version (at least from 1.6 to 1.7) which means the code will not compile with older/newer versions of Java. 数据源接口随每个Java版本(至少从1.6到1.7)而变化,这意味着该代码将无法使用Java的旧/新版本进行编译。 This made me decide to proxy the Datasource because it is easier to maintain, but it is not easy to setup (see the various proxy helper classes here ). 这使我决定代理数据源,因为它易于维护,但设置起来却不容易(请参见此处的各种代理帮助器类)。 Proxying also has the drawback of making stack-traces harder to read (which makes debugging harder) and sometimes makes exceptions disappear (I have seen this happen in JBoss where the underlying object threw a runtime exception from the constructor). 代理还有一个缺点,就是使堆栈跟踪更难以阅读(这使得调试更加困难),并且有时使异常消失(我已经在JBoss中发生过这种情况,其中底层对象从构造函数中抛出了运行时异常)。

tl;dr If you make your own specialized pool, also deliver a utility class which makes it easy to use the pool and takes care of most of the administration that is required (like closing used resources) so that it is unlikely to be forgotten. tl; dr如果您创建自己的专用池,则还应提供一个实用程序类,该实用程序类使使用该池变得容易,并负责大部分必需的管理工作(例如关闭已使用的资源),因此不太可能将其遗忘。 If a utility class is not an option, wrapping or proxying is the standard way to go. 如果没有实用程序类,则包装或代理是标准的处理方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM