【问题标题】:Connection Pooling: To Close or Not to Close Connections?连接池:关闭还是不关闭连接?
【发布时间】:2016-11-29 10:58:49
【问题描述】:

我在我的 JSF Java 应用程序中使用了 dbcp 的 BasicDataSource。由于基本约定是在使用后关闭连接,因此我在 catch 中这样做 - 最后在我的代码中。但是,应用程序因错误而停止,

java.sql.SQLException: Connection is null.
at org.apache.tomcat.dbcp.dbcp2.DelegatingConnection.checkOpen(DelegatingConnection.java:611)
at org.apache.tomcat.dbcp.dbcp2.DelegatingConnection.createStatement(DelegatingConnection.java:258)

所以我决定不关闭我的联系;我的代码几乎可以正常运行,但随后经常出现此错误:

Caused by: org.postgresql.util.PSQLException: FATAL: remaining connection slots are reserved for non-replication superuser connections

下面是我的连接配置:

public class StageDB {
    public StageDB() {}
    public static Connection getConnection() {
        BasicDataSource ds = new BasicDataSource();
        ds.setDriverClassName(JDBC_DRIVER);
        ds.setUsername(USER);
        ds.setPassword(PASS);
        ds.setUrl(DB_URL);
        ds.setTimeBetweenEvictionRunsMillis(20*1000);
        ds.setMinIdle(0);
        ds.setMaxIdle(10);
        ds.setMaxOpenPreparedStatements(100);
        conn = ds.getConnection();
        return conn;
    }
}

我应该提到我已经尝试过使用这些设置,也使用默认值,但结果相同。我可能做错了什么?

【问题讨论】:

  • ds.close() 是怎么回事?你创建一个数据源然后立即关闭它?
  • 这看起来像是 dbcp 中的一个错误。是的,当您不再需要它时,您应该调用 conn.close()。如果您的后端是 Oracle,那么您是否尝试过 Oracle 的 UCP?
  • @Kayaman,代码在 DAO 使用的数据源类中。它返回 conn(参见 ds.getConnection())。
  • @user2194279 所以每次你得到一个连接,你建立一个BasicDataSource,做ds.getConnection(),然后用ds.close();关闭它?
  • @Jean-de-Laveren,一个错误?如何解决?尝试不同版本的 dbcp perharps?后端主要是 Postgres。

标签: java database-connection connection-pooling


【解决方案1】:

在意识到我做错了之后,我找到了解决方案。所以这就是我能够想出的,与我的设计方法完美配合。

//1. Create pooled connections datasource class using the Singleton.

/***************************
* This is the pooled datasource class
****************************/
public class PrimaryDS {
    private PrimaryDS primaryDS;

    public PrimaryDS() {
        ds = new BasicDataSource();
        ds.setDriverClassName(JDBC_DRIVER);
        ds.setUsername(USER);
        ds.setPassword(PASS);
        ds.setUrl(DB_URL);
    }

    public static PrimaryDS getInstance() {
        primaryDS = primaryDS == null ? new PrimaryDS() : primaryDS;
        return primaryDS;
    }

    public BasicDataSource getDataSource() {
        return ds;
    }

    public void setDataSource(BasicDataSource ds) {
        this.ds = ds;
    }
}

//2. DAOs make call to the datasource. Initialize DS in the DAO's constructor.

/***************************
* This is in the DAO's constructor
****************************/
ds = PrimaryDS.getInstance().getDataSource();

//3. DAOs have the database manupulation methods. In each of these methods, 
//create connection object from ds, and ensure it's closed in the catch - finally.

/***************************
* This is inside one of the DAO methods
****************************/
try {
    conn = ds.getConnection();
    stmt = null;
    rs = null;
    PreparedStatement pstmt = conn.prepareStatement(ACTIVE_ACCOUNTS_SQL);
    pstmt.setByte(1,status);
    ResultSet rs = pstmt.executeQuery();
    // TODO loop through rs
} catch (SQLException e) {
    e.printStackTrace();
} finally {
    try {
        if(rs != null) rs.close();
        if(stmt != null) stmt.close();
        if(conn != null) conn.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

【讨论】: