【问题标题】:java call mysql c3p0 functionjava调用mysql c3p0函数
【发布时间】:2012-07-03 07:09:09
【问题描述】:

我有一个 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 查询并在函数中获取结果的最佳方法。

【问题讨论】:

  • 这个问题的一些变体以前已经被问过很多次了,例如见stackoverflow.com/questions/4106606/… 此外,c3p0 与这个问题无关。
  • 错误来自哪一行?
  • 问题是上层函数get ResultSet Object rs,而不是Connection Object conn,可以做rs.close(),但是不能用conn.close()。我的以下问题是当我执行 rs.close() 时,连接也会被释放吗?因为我需要释放连接,并把它放回连接池。

标签: java mysql c3p0


【解决方案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);
    }   
}   

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

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

所以,当 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;
}

【讨论】:

    【解决方案2】:

    错误堆栈说

    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() 方法中循环结果集对象时填充其实例列表。示例代码 sn-p 如下所示。

    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()  
    

    【讨论】:

    • 非常感谢您的建议。我担心的是资源开销,我需要复制/释放 ResultSet 两次,这对于大型查询来说成本很高。你还有什么办法吗?
    • @user1374194 或者,您可以将con 对象传递给fetch 方法,但不要在fetch 方法中关闭它。在fetch方法中返回rs,在关闭连接对象之前在调用方法中使用。
    【解决方案3】:

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-05-13
      • 1970-01-01
      • 2012-12-30
      • 2012-06-15
      • 1970-01-01
      • 1970-01-01
      • 2010-10-18
      • 2013-03-06
      相关资源
      最近更新 更多