1

クエリを実行するJavaライブラリがあり、別のJava関数にmysql database戻ります。ResultSetmysql のタイムアウトの問題のため、以前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);
    }   
}   

この接続は、c3p0 によって管理される PooledConnection です。close() メソッドは、接続を閉じずにプールに返すだけです。リソースのリークやプールの破損を防ぐために、接続がプールに返される前にステートメントがクリーンアップされます。

ステートメントが閉じられると、現在の 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 を処理するコードがあります

2 番目のオプションは、 Commons DbUtilsなどの既に実装されているツールを使用し、リンクに従ってサンプルを表示します。

その他のオプションとして、接続ハンドルを抽象化する ER マッピング ツール、JPA、休止状態などを使用します。

最後に、タイムアウトの問題とプールされた接続のテストに対処するには、より堅牢なソリューションである c3p0 の代わりに DBCP を使用します。

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 構造を一時的に保存し、それを上位レベルの関数に戻します。一時的なリソースを2回作成/解放する必要があるため、私の懸念はコストです。大規模なクエリの問題です。

3) 偽のクエリ関数「SELECT 1」を作成し、上位レベルの関数で特定の時間実行します (たとえば、20 分ごとなど、mysql の wait_timeout がトリガーされる前)。これは mysql タイムアウトを使用して接続を閉じます。これは一種の無駄な 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()この接続オブジェクトのデータベースと 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 に答える