[#1080] Add support for JDBC's Statement.setFetchSize() in ResultQuery.fetchLazy()

This commit is contained in:
Lukas Eder 2012-01-28 12:26:12 +00:00
parent 15e62bbae6
commit 21837feaff
4 changed files with 134 additions and 75 deletions

View File

@ -990,87 +990,89 @@ public abstract class jOOQAbstractTest<
}
@Test
public void testLazyFetching() throws Exception {
public void testFetchLazy() throws Exception {
for (int fetchSize : Arrays.asList(0, 1)) {
// ---------------------------------------------------------------------
// A regular pass through the cursor
// ---------------------------------------------------------------------
Cursor<B> cursor = create().selectFrom(TBook()).orderBy(TBook_ID()).fetchLazy();
// ---------------------------------------------------------------------
// A regular pass through the cursor
// ---------------------------------------------------------------------
Cursor<B> cursor = create().selectFrom(TBook()).orderBy(TBook_ID()).fetchLazy(fetchSize);
assertTrue(cursor.hasNext());
assertTrue(cursor.hasNext());
assertEquals(Integer.valueOf(1), cursor.fetchOne().getValue(TBook_ID()));
assertEquals(Integer.valueOf(2), cursor.fetchOne().getValue(TBook_ID()));
assertTrue(cursor.hasNext());
assertTrue(cursor.hasNext());
assertEquals(Integer.valueOf(1), cursor.fetchOne().getValue(TBook_ID()));
assertEquals(Integer.valueOf(2), cursor.fetchOne().getValue(TBook_ID()));
assertTrue(cursor.hasNext());
assertTrue(cursor.hasNext());
assertFalse(cursor.isClosed());
assertTrue(cursor.hasNext());
assertTrue(cursor.hasNext());
assertFalse(cursor.isClosed());
Iterator<B> it = cursor.iterator();
assertTrue(it.hasNext());
assertTrue(cursor.hasNext());
assertTrue(it.hasNext());
assertTrue(cursor.hasNext());
assertTrue(it.hasNext());
assertTrue(cursor.hasNext());
assertEquals(Integer.valueOf(3), it.next().getValue(TBook_ID()));
assertEquals(Integer.valueOf(4), it.next().getValue(TBook_ID()));
assertFalse(cursor.isClosed());
Iterator<B> it = cursor.iterator();
assertTrue(it.hasNext());
assertTrue(cursor.hasNext());
assertTrue(it.hasNext());
assertTrue(cursor.hasNext());
assertTrue(it.hasNext());
assertTrue(cursor.hasNext());
assertEquals(Integer.valueOf(3), it.next().getValue(TBook_ID()));
assertEquals(Integer.valueOf(4), it.next().getValue(TBook_ID()));
assertFalse(cursor.isClosed());
assertFalse(it.hasNext());
assertFalse(cursor.hasNext());
assertFalse(it.hasNext());
assertFalse(cursor.hasNext());
assertFalse(it.hasNext());
assertFalse(cursor.hasNext());
assertTrue(cursor.isClosed());
assertFalse(it.hasNext());
assertFalse(cursor.hasNext());
assertFalse(it.hasNext());
assertFalse(cursor.hasNext());
assertFalse(it.hasNext());
assertFalse(cursor.hasNext());
assertTrue(cursor.isClosed());
assertEquals(null, it.next());
assertEquals(null, it.next());
assertEquals(null, cursor.fetchOne());
assertEquals(null, cursor.fetchOne());
assertEquals(null, it.next());
assertEquals(null, it.next());
assertEquals(null, cursor.fetchOne());
assertEquals(null, cursor.fetchOne());
cursor.close();
cursor.close();
assertTrue(cursor.isClosed());
cursor.close();
cursor.close();
assertTrue(cursor.isClosed());
// ---------------------------------------------------------------------
// Prematurely closing the cursor
// ---------------------------------------------------------------------
cursor = create().selectFrom(TBook()).orderBy(TBook_ID()).fetchLazy();
// ---------------------------------------------------------------------
// Prematurely closing the cursor
// ---------------------------------------------------------------------
cursor = create().selectFrom(TBook()).orderBy(TBook_ID()).fetchLazy(fetchSize);
assertTrue(cursor.hasNext());
assertTrue(cursor.hasNext());
assertEquals(Integer.valueOf(1), cursor.fetchOne().getValue(TBook_ID()));
assertEquals(Integer.valueOf(2), cursor.fetchOne().getValue(TBook_ID()));
assertFalse(cursor.isClosed());
assertTrue(cursor.hasNext());
assertTrue(cursor.hasNext());
assertEquals(Integer.valueOf(1), cursor.fetchOne().getValue(TBook_ID()));
assertEquals(Integer.valueOf(2), cursor.fetchOne().getValue(TBook_ID()));
assertFalse(cursor.isClosed());
cursor.close();
assertTrue(cursor.isClosed());
assertFalse(cursor.hasNext());
assertNull(cursor.fetchOne());
cursor.close();
assertTrue(cursor.isClosed());
assertFalse(cursor.hasNext());
assertNull(cursor.fetchOne());
// ---------------------------------------------------------------------
// Fetching several records at once
// ---------------------------------------------------------------------
cursor = create().selectFrom(TBook()).orderBy(TBook_ID()).fetchLazy();
// ---------------------------------------------------------------------
// Fetching several records at once
// ---------------------------------------------------------------------
cursor = create().selectFrom(TBook()).orderBy(TBook_ID()).fetchLazy(fetchSize);
assertTrue(cursor.fetch(0).isEmpty());
assertTrue(cursor.fetch(0).isEmpty());
List<B> list = cursor.fetch(1);
assertTrue(cursor.fetch(0).isEmpty());
assertTrue(cursor.fetch(0).isEmpty());
List<B> list = cursor.fetch(1);
assertEquals(1, list.size());
assertEquals(Integer.valueOf(1), list.get(0).getValue(TBook_ID()));
assertEquals(1, list.size());
assertEquals(Integer.valueOf(1), list.get(0).getValue(TBook_ID()));
list = cursor.fetch(2);
assertEquals(2, list.size());
assertEquals(Integer.valueOf(2), list.get(0).getValue(TBook_ID()));
assertEquals(Integer.valueOf(3), list.get(1).getValue(TBook_ID()));
list = cursor.fetch(2);
assertEquals(2, list.size());
assertEquals(Integer.valueOf(2), list.get(0).getValue(TBook_ID()));
assertEquals(Integer.valueOf(3), list.get(1).getValue(TBook_ID()));
list = cursor.fetch(2);
assertTrue(cursor.isClosed());
assertEquals(1, list.size());
assertEquals(Integer.valueOf(4), list.get(0).getValue(TBook_ID()));
list = cursor.fetch(2);
assertTrue(cursor.isClosed());
assertEquals(1, list.size());
assertEquals(Integer.valueOf(4), list.get(0).getValue(TBook_ID()));
}
}
@Test

View File

@ -38,6 +38,7 @@ package org.jooq;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
@ -83,20 +84,47 @@ public interface ResultQuery<R extends Record> extends Query {
Result<R> fetch() throws DataAccessException;
/**
* Execute the query and return the generated result
* Execute the query and "lazily" return the generated result
* <p>
* The returned {@link Cursor} holds a reference to the executed
* {@link PreparedStatement} and the associated {@link ResultSet}. Data can
* be fetched (or iterated over) lazily, fetching records from the
* {@link ResultSet} one by one.
* <p>
* Depending on your JDBC driver's default behaviour, this may load the
* whole database result into the driver's memory. In order to indicate to
* the driver that you may not want to fetch all records at once, use
* {@link #fetchLazy(int)}
* <p>
* Client code is responsible for closing the cursor after use.
*
* @return The resulting cursor.
* @throws DataAccessException if something went wrong executing the query
* @see #fetchLazy(int)
*/
Cursor<R> fetchLazy() throws DataAccessException;
/**
* Execute the query and "lazily" return the generated result
* <p>
* The returned {@link Cursor} holds a reference to the executed
* {@link PreparedStatement} and the associated {@link ResultSet}. Data can
* be fetched (or iterated over) lazily, fetching records from the
* {@link ResultSet} one by one.
* <p>
* Depending on your JDBC driver's behaviour, this will load only
* <code>fetchSize</code> records from the database into memory at once. For
* more details, see also {@link Statement#setFetchSize(int)}
* <p>
* Client code is responsible for closing the cursor after use.
*
* @return The resulting cursor.
* @throws DataAccessException if something went wrong executing the query
* @see #fetchLazy()
* @see Statement#setFetchSize(int)
*/
Cursor<R> fetchLazy(int fetchSize) throws DataAccessException;
/**
* Execute a query, possibly returning several result sets.
* <p>

View File

@ -101,6 +101,11 @@ abstract class AbstractDelegatingSelect<R extends Record>
return getDelegate().fetchLazy();
}
@Override
public final Cursor<R> fetchLazy(int fetchSize) {
return getDelegate().fetchLazy(fetchSize);
}
@Override
public final List<Result<Record>> fetchMany() {
return getDelegate().fetchMany();

View File

@ -78,13 +78,14 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
/**
* Generated UID
*/
private static final long serialVersionUID = -5588344253566055707L;
private static final long serialVersionUID = -5588344253566055707L;
private transient boolean lazy;
private transient boolean many;
private transient Cursor<R> cursor;
private Result<R> result;
private List<Result<Record>> results;
private transient boolean lazy;
private transient int size;
private transient boolean many;
private transient Cursor<R> cursor;
private Result<R> result;
private List<Result<Record>> results;
AbstractResultQuery(Configuration configuration) {
super(configuration);
@ -107,6 +108,17 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
return (ResultQuery<R>) super.bind(index, value);
}
@Override
protected final PreparedStatement prepare(Configuration configuration, String sql) throws SQLException {
PreparedStatement statement = super.prepare(configuration, sql);
if (size > 0) {
statement.setFetchSize(size);
}
return statement;
}
@Override
protected final int execute(Configuration configuration, PreparedStatement statement) throws SQLException {
Connection connection = configuration.getConnection();
@ -184,9 +196,21 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
@Override
public final Cursor<R> fetchLazy() {
return fetchLazy(0);
}
@Override
public final Cursor<R> fetchLazy(int fetchSize) throws DataAccessException {
lazy = true;
execute();
lazy = false;
size = fetchSize;
try {
execute();
}
finally {
lazy = false;
size = 0;
}
return cursor;
}