Search code examples
javajdbcpaginationfirebirdjaybird

Jaybird (Firebird JDBC) ResultSet FetchSize -- Pagination long lasting queries


I am trying to find a way how to force jaybird to do "pagination" in ResultSet. Suppose we have some long lasting sql query (it returns for example 5000 rows in 15 seconds). However, fetching of first 50 (random) rows takes just a fraction of second. So long as we do not add order by clause to the query, the server quickly returns first rows, which can be immediately shown in the client application. This is btw the default behaviour of flamerobin client.

I try to simulate this behaviour with setting the Statement parameters like in the code below, but without success. Is there a way to force jaybird not to load all rows to the ResultSet? I suppose that the method stmt.setFetchSize(50) has this purpose but it is perhaps wrong. Jaybird version used was 2.2.7 and Firebird version used was 2.5.4. Thank you for your advices.

String user = "user";
String pass = "pass";
Connection conn = DriverManager.getConnection(s, user, pass);
conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
conn.setAutoCommit(false);
Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(50);
stmt.setFetchDirection(ResultSet.FETCH_FORWARD);
ResultSet rs = null;
String sql = "select * from TABLE"; //long lasting select 
boolean ok = stmt.execute(sql);
if (ok) {
    rs = stmt.getResultSet();
    while (rs.next()) {
         //do something
    }
}
if (rs != null) {
    rs.close();
}

if (stmt != null) {
    stmt.close();
}

I try to achieve same think as flamerobin client does -- on the fly load of the data to the table (only when we need them -- scroll down in the table). We develop application which is a client of two tier ERP system (DB server firebird, client on netbeans platform).We wrote some database components which fills JXTable with data on the same principle like "interbase Delphi components" did in the past. The code above is symplified, in the component code we load the first 100 rows to the JTable table model and when user scrolls down we load another 100 rows etc. However I notice that the load time of the first 100 rows is the same as if we load all rows to the dataset. That is the code

boolean ok = stmt.execute(sql);
if (ok) {
    rs = stmt.getResultSet();
    int rows = 0;
    while (rs.next() rows < 100) {
         //do something
         rows++;
    }
}

took practicaly same time as the first chunk of source code. It seems, that the stmt.execute(sql) command waits until all select rows are returned from server. However I set that I want to get 50 rows chunks, so I supposed that the while cycle will start immediately after getting first 50 rows from the DB server. So I want start the while cycle after fetching first 50 rows (as if I set stmt.setMaxRows(50)), but I want to have oportunity to let the result set open and fetch another rows on demand.


Solution

  • Using setFetchSize normally does exactly what you expect: it will fetch rows in batches of the specified size (Firebird 3 can decide to return less when it considers the batch too big).

    However as you have specified defaultHoldable in your connection string, the result set is HOLD_CURSORS_OVER_COMMIT, and holdable result sets are fully cached client-side before they are returned.

    You either need to set the holdability to CLOSE_CURSORS_AT_COMMIT for this specific statement, or - if you do this for all statements - just remove the defaultHoldable from your connection string.