【问题标题】:Creating Connection Pool In Standalone Java Application在独立 Java 应用程序中创建连接池
【发布时间】:2016-07-15 18:40:43
【问题描述】:

我需要在独立(如非 Web)Java 应用程序中使用连接池。在我工作的地方,我们不允许在没有经过安全层的情况下使用 API,而且这项工作需要尽快完成。下面是我创建此连接池的尝试。

我已经对这段代码进行了单元测试,并在整个应用程序的上下文中对其进行了一百次测试,在所有情况下,测试都以零错误通过,此外,每次运行的性能比以串行方式进行简单连接、检索数据、断开连接;但是,我仍然担心这种方法可能存在我尚未发现的问题。如果有人对以下代码提出任何建议,我将不胜感激。这是我在这个网站上的第一篇文章;如果我在礼仪上犯了任何错误,请告诉我。在发布之前,我确实在这个网站上搜索过这个问题。请参阅下面的代码以获取调用示例。谢谢。 --JR

package mypackage;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * Note: This class is only instantiated once per application run.
 *       Multiple instantiations, as specified in the release notes,
 *       are not supported.      
 */
public class ConnectionManager {

    // Use a blocking queue to store the database connections.
    // The application will only be called once, by a single user,
    // but within the application many threads will require 
    // a connection. 
    private BlockingQueue<Connection> connectionQueue = null;

    // Load the connection queue with a user-defined number of connections.
    // Params contains a map of all non hard-coded variables in the
    // application.
    public ConnectionManager(int howMany, Map<String, Object> params) {
        Database database = new Database();
        connectionQueue = new ArrayBlockingQueue<Connection>(howMany);
        for(int i = 0; i < howMany; i++) {
            connectionQueue.add(database.getConn(params));
        }
    }

    // Return a connection from the queue, waiting up to 15 minutes to do so.
    // 15 minutes is hard-coded because it is the standard time-out for all
    // processes at our agency.  This application must complete in less
    // than fifteen minutes (is currently completing in thirty five seconds).
    public Connection getConnection() {
        Connection conn = null;
        try {
            conn = connectionQueue.poll(15, TimeUnit.MINUTES);
        }
        catch(InterruptedException e) {
            e.printStackTrace();
        }
        catch(SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }

    // Returns a connection to the connection queue.  
    public void returnConnectionToManager(Connection conn) {
        connectionQueue.add(conn);
    }

    // Called on the last line of the application program's dispatcher.
    // Closes all active connections (which will only exist if there
    // was a failure within one of the worker threads).
    public void closeAllConnections() {
        for(Connection conn : connectionQueue) {
            try {
                conn.close();
            }
            catch(SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

调用示例:

...

private ConnectionManager cm;

...

public Table(Map<String, Object> params, String method) {
  ...
  cm = (ConnectionManager) params.get("cm");
}

// Execute a chunk of SQL code without requiring processing of a 
// result set.  Acquires connection from pool via cm.getConnection
// and releases connection via cm.returnConnectionToManager.
// (Database is just a helper class with simple methods for 
// closing prepared statement, result sets, etc.)  
private void execute(String sql) {
  PreparedStatement ps = null;
  Connection conn = null;
  try {
    conn = cm.getConnection();
    ps = conn.prepareStatement(sql);
    ps.execute();
  }
  catch (SQLException e) {
    e.printStackTrace();
  }
  finally {
    database.closePreparedStatement(ps);
    cm.returnConnectionToManager(conn); 
  }
}

【问题讨论】:

  • 将对象存储到单例类或枚举中。最好是枚举。通过这种方式,您的连接池将易于访问并轻松避免多个对象。添加一个方法以在您的应用程序代码完成数据库事务后释放单个连接。
  • Where I work, we are not allowed to use APIs without going through layers of security ..听起来像是 Not Invented Here 的糟糕案例。为什么有些人认为内部代码比经过数千人尝试和测试的开源框架更安全,这总是让我感到惊讶。
  • IMO 而不是编写自己的代码来清除 API 会更好
  • 创建一个发布查询以执行的地方,在这个地方建立连接,执行查询并将连接返回到池中。它将确保您返回连接。如果您需要在单个连接中一个接一个地调用多个查询,请使该方法接受要按顺序执行的 SQL 查询的数组或列表。
  • Ace,我希望我可以选择为此获得一个 API,但要实现这一点几乎需要国会法案。十年后,我什至放弃了尝试。

标签: java jdbc connection pool


【解决方案1】:

您的代码看起来不错,但有一个严重的问题,即您的 API 的客户端需要处理获取和释放连接,其中一个忘记了,并且内存/资源泄漏已准备好。

创建一个发布查询以执行的地方,在这个地方建立连接,执行查询并将连接返回到池中。它将确保您返回连接。如果您需要在单个连接中一个接一个地调用多个查询,请使该方法接受要按顺序执行的 SQL 查询的数组或列表。这个想法是将每个请求封装到数据库,以便您管理所有连接。您可以编写一个接口,该接口具有您需要实现的执行(连接连接),然后您可以让一些接受此类对象的服务给它一个连接,然后将资源释放回连接池。 比如:

interface SqlWork {
 execute(Connection conn);
}

SqlWork myWork = new SqlWork () {
  execute(Connection conn) {
    // do you work with the conn here
  }
}

class SqlExecutionService {
   ConnectionManager cm = ...;

   public void execute(SqlWork sqlWork) {
       Connection conn = null;
       try {
         conn = cm.getConnection();
         sqlWork.execute(conn);
       } catch (Your exceptions here) {
         //serve or rethrow them
       }
       finally 
       {
         if (conn!=null) { 
           cm.returnConnectionToManager(conn); 
         }
       }
   }
}

使用示例:

SqlExecutionService sqlExecService = ...;
sqlExecService.execute(myWork);

【讨论】:

  • 谢谢,Krzysztof。我没有提到的一件事是没有其他人会使用这个代码——他们只被允许运行它。所以我不必担心有人滥用它;只是运行不正确(在这种情况下,任何问题都落在他们身上;这听起来很糟糕,但这正是我工作的方式——每个人都必须保护自己,因为其他人会留意他)。感谢您的代码和您的洞察力。非常感谢。
  • 这似乎是正确的观点 :) 但我向您保证,这种方法将来会节省您的时间,如果它不是自动的,我经常会忘记做某事。
猜你喜欢
  • 2013-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-07
  • 2015-01-25
  • 1970-01-01
相关资源
最近更新 更多