I\'ve got a class that implements Iterator with a ResultSet as a data member. Essentially the class looks like this:
public class A implements Iterator{
There are a couple of things you could do depending on what you want your class A. If the major use case is to go through every single result then perhaps its best to preload all the Entity objects and throw away the ResultSet.
If however you don't want to do that you could use the next() and previous() method of ResultSet
public boolean hasNext(){
boolean next = entities.next();
if(next) {
//reset the cursor back to its previous position
entities.previous();
}
}
You do have to be careful to make sure that you arent currently reading from the ResultSet, but, if your Entity class is a proper POJO (or at least properly disconnected from ResultSet then this should be a fine approach.
You can use ResultSetIterator, just put your ResultSet in the constructor.
ResultSet rs = ...
ResultSetIterator = new ResultSetIterator(rs);
ResultSet has an 'isLast()' method that might suit your needs. The JavaDoc says it is quite expensive though since it has to read ahead. There is a good chance it is caching the look-ahead value like the others suggest trying.
You could try the following:
public class A implements Iterator {
private ResultSet entities;
private Entity nextEntity;
...
public Object next() {
Entity tempEntity;
if ( !nextEntity ) {
entities.next();
tempEntity = new Entity( entities.getString...etc....)
} else {
tempEntity = nextEntity;
}
entities.next();
nextEntity = new Entity( entities.getString...ext....)
return tempEntity;
}
public boolean hasNext() {
return nextEntity ? true : false;
}
}
This code caches the next entity, and hasNext() returns true, if the cached entity is valid, otherwise it returns false.
Here's my iterator that wraps a ResultSet. The rows are returned in the form a Map. I hope you'll find it helpful. The strategy is that I always bring one element in advance.
public class ResultSetIterator implements Iterator<Map<String,Object>> {
private ResultSet result;
private ResultSetMetaData meta;
private boolean hasNext;
public ResultSetIterator( ResultSet result ) throws SQLException {
this.result = result;
meta = result.getMetaData();
hasNext = result.next();
}
@Override
public boolean hasNext() {
return hasNext;
}
@Override
public Map<String, Object> next() {
if (! hasNext) {
throw new NoSuchElementException();
}
try {
Map<String,Object> next = new LinkedHashMap<>();
for (int i = 1; i <= meta.getColumnCount(); i++) {
String column = meta.getColumnName(i);
Object value = result.getObject(i);
next.put(column,value);
}
hasNext = result.next();
return next;
}
catch (SQLException ex) {
throw new RuntimeException(ex);
}
}
}
entities.next returns false if there are no more rows, so you could just get that return value and set a member variable to keep track of the status for hasNext().
But to make that work you would also have to have some sort of init method that reads the first entity and caches it in the class. Then when calling next you would need to return the previously cached value and cache the next value, etc...