1

我有一个要查询的 java 库mysql database,返回ResultSet另一个 Java 函数。由于mysql超时问题,我曾经c3p0 pool实现查询。

cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl(url);
cpds.setUser(user);
cpds.setPassword(passwd);
cpds.setMaxPoolSize(maxPoolSize);
cpds.setMinPoolSize(minPoolSize);
cpds.setAcquireIncrement(20);


public ResultSet fetch() {
    PreparedStatement pst = null;
    ResultSet rs = null;
    String query = null;
    Connection conn = null;

    try {

        conn = cpds.getConnection();

        query = "...";
        pst = conn.prepareStatement(query);
        rs = pst.executeQuery();

    } catch (SQLException ex)  {
        Logger lgr = Logger.getLogger(Query.class.getName());                       
        lgr.log(Level.SEVERE, ex.getMessage(), ex);
    }finally {
        try {
            if(conn != null) {
                conn.close();
            }
        } catch (SQLException ex) {
            Logger lgr = Logger.getLogger(Query.class.getName());
            lgr.log(Level.SEVERE, ex.getMessage(), ex);
        }   
    }   

    return rs;
  }       
}

我收到了这个错误

 SEVERE: Operation not allowed after ResultSet closed java.sql.SQLException: Operation not allowed after ResultSet closed 
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1075)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:989)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:984)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:929)
    at com.mysql.jdbc.ResultSetImpl.checkClosed(ResultSetImpl.java:795)
    at com.mysql.jdbc.ResultSetImpl.next(ResultSetImpl.java:7146)
    at com.mchange.v2.c3p0.impl.NewProxyResultSet.next(NewProxyResultSet.java:622)

原因很明显,但我在想什么是调用 Mysql 查询并在函数中获取结果的最佳方法。

4

3 回答 3

1

在 finally 子句中,连接在方法返回之前关闭。

}finally {
    try {
        if(conn != null) {
            conn.close();
        }
    } catch (SQLException ex) {
        Logger lgr = Logger.getLogger(Query.class.getName());
        lgr.log(Level.SEVERE, ex.getMessage(), ex);
    }   
}   

此 Connection 是由 c3p0 管理的 PooledConnection。close() 方法只是将连接返回到池,而不关闭它。在连接返回池之前清理语句,以防止资源泄漏和池损坏。

当 Statements 关闭时,其当前的 ResultSet 对象(如果存在)也将关闭。在此处检查 java 7 API 语句 close() 方法

因此,当 fetch() 返回时 ResultSet 被关闭。

建议:

这是 java JDBC 编程中常见的解决问题。

第一个选项,将fetch()更改为作为模板方法运行的代码

public ResultSet fetch(ResultSetIterator rsIterator ) {
    PreparedStatement pst = null;
    ResultSet rs = null;
    String query = null;
    Connection conn = null;

    try {

        conn = cpds.getConnection();

        query = "select * from tb_user";
        pst = conn.prepareStatement(query);
        rs = pst.executeQuery();
        
        rsIterator.iterate(rs);
        
    } catch (SQLException ex)  {
        Logger lgr = Logger.getLogger(Query.class.getName());                       
        lgr.log(Level.SEVERE, ex.getMessage(), ex);
    }finally {
        try {
            if(conn != null) {
                conn.close();
            }
        } catch (SQLException ex) {
            Logger lgr = Logger.getLogger(Query.class.getName());
            lgr.log(Level.SEVERE, ex.getMessage(), ex);
        }   
    }   

    return rs;
} 

ResultSetIterator有处理 ResultSet 的代码

第二种选择,使用已经实现的工具,例如Commons DbUtils,点击链接查看示例

其他选项,使用 ER 映射工具、JPA、休眠等...抽象连接句柄

最后,为了解决连接池的超时问题和测试,使用DBCP代替c3p0,一个更健壮的解决方案

private static DataSource setupDataSource() {
    BasicDataSource ds = new BasicDataSource();
    ds.setDriverClassName(getDriver());
    ds.setUsername(getUser());
    ds.setPassword(getPassword());
    ds.setUrl(getConnectionString());
    
    ds.setDefaultAutoCommit(false);
    ds.setInitialSize(4);
    ds.setMaxActive(60);
    ds.setMaxIdle(10);
    
    ds.setValidationQuery("/* ping */ SELECT 1");//config to validate against mysql
    ds.setValidationQueryTimeout(3);
    ds.setTestOnBorrow(true);
    ds.setTestOnReturn(true);
    
    return ds;
}
于 2012-07-03T08:56:29.847 回答
0

谢谢大家的建议。我有一些想法和顾虑:

1)在上层函数中运行ResultSet rs.close()。但是我不确定连接资源是否被释放。释放连接资源非常重要。

2) 创建另一个Object List 来临时保存ResultSet 结构,并将其返回给上层函数。我担心的是成本,因为我需要两次创建/释放临时资源。这是大型查询的问题。

3) 创建一个伪查询函数“SELECT 1”,并在上层函数中运行特定时间(例如,在触发 mysql wait_timeout 之前,如每 20 分钟一次)。这将使用 mysql timeout 来关闭连接。这是一种浪费mysql资源。

于 2012-07-03T14:42:56.263 回答
0

错误堆栈说

SEVERE: Operation not allowed after ResultSet closed java.sql.SQLException: 
  Operation not allowed after ResultSet closed 

抛出此错误是因为您尝试使用返回的 ResultSet 对象实例,
该实例实际上是在数据库连接关闭请求期间释放的。因此,您不能更有建设性地使用返回的 ResultSet 实例。

文档说con.close()立即释放此 Connection 对象的数据库和 JDBC 资源,而不是等待它们自动释放。 ”。这里的JDBC 资源是指使用正在关闭的连接对象创建的所有 Statement 对象、ResultSet 对象等。

建议的解决方案
您应该定义一个ResultDataObject类或一些有意义的东西,并在方法中循环结果集对象时填写其实例列表fetch()。示例代码片段如下所示。

public List<ResultDataObject> fetch() {  
    List<ResultDataObject> list = null; // new ArrayList<ResultDataObject>( 24 );  
    // ...  
    rs = pst.executeQuery();  

    // now prepare the list with results filled and return  
    if ( list == null ) list = new ArrayList<ResultDataObject>( 24 );  

    // now read from result set  
    while ( rs.next() ) {  
        ResultDataObject resultData = new ResultDataObject(); // or something relevant  

        // use the following type methods to read from rs and fill result object  
        resultData.setXXX( rs.getXXX( ... ) );
        // ...

        list.add( resultData );
    } // while rs  

    // do something if required before return
    // ...

    return list;
} // fetch()  
于 2012-07-03T08:43:59.163 回答