2012-09-24 9 views
6

Vedo questo schema molto.query impaginate/iteratore ricetta

Sul server:

// Get a bounded number of results, along with a resume token to use 
// for the next call. Successive calls yield a "weakly consistent" view of 
// the underlying set that may or may not reflect concurrent updates. 
public<T> String getObjects(
     int maxObjects, String resumeToken, List<T> objectsToReturn); 

Sul client:

// An iterator wrapping repeated calls to getObjects(bufferSize, ...) 
public<T> Iterator<T> getIterator(int bufferSize); 

maggior parte dei luoghi rotolare le proprie versioni di questi due metodi, e le implementazioni sono sorprendentemente difficile da ottenere. Ci sono molti bug di casi limite.

C'è una ricetta o una biblioteca canonica per queste domande?

(è possibile semplificare alcuni presupposti per l'archiviazione sul lato server, ad esempio T ha un ordinamento naturale).

risposta

1

Ecco uno utilizzando AbstractIterator dalla libreria google-guava e la primavera-JDBC per interrogare il database in realtà:

public Iterable<T> queryInBatches(
     final String query, 
     final Map<String, Integer> paramMap, 
     final int pageSize, final Class<T> elementType) { 
    return new Iterable<T>() { 
     @Override 
     public Iterator<T> iterator() { 
      final Iterator<List<T>> resultIter = 
        queryResultIterator(query, paramMap, pageSize, elementType); 

      return new AbstractIterator<T>() { 
       private Iterator<T> rowSet; 

       @Override 
       protected T computeNext() { 
        if (rowSet == null) { 
         if (resultIter.hasNext()) { 
          rowSet = resultIter.next().iterator(); 
         } else { 
          return endOfData(); 
         } 
        } 

        if (rowSet.hasNext()) { 
         return rowSet.next(); 
        } else { 
         rowSet = null; 
         return computeNext(); 
        } 
       }}; 
     }}; 
} 


private AbstractIterator<List<T>> queryResultIterator(
     final String query, final Map<String, Integer> paramMap, 
     final int pageSize, final Class<T> elementType) { 
    return new AbstractIterator<List<T>>() { 
     private int page = 0; 

     @Override 
     protected List<T> computeNext() { 
      String sql = String.format(
        "%s limit %s offset %s", query, pageSize, page++ * pageSize); 
      List<T> results = jdbc().queryForList(sql, paramMap, elementType); 
      if (!results.isEmpty()) { 
       return results; 
      } else { 
       return endOfData(); 
      } 
     }}; 
} 

AbstractIterator nasconde la maggior parte delle complicazioni che coinvolgono la scrittura la propria implementazione di Iterator. È necessario implementare solo il metodo computeNext che restituisce il valore successivo nell'iteratore o chiama endOfData per indicare che non ci sono più valori nell'iteratore.

+0

non ritengo questo dtrt se la la tabella viene modificata contemporaneamente, poiché tutti gli offset successivi saranno incoerenti. – ashm

+0

In questo caso si passa in un parametro aggiuntivo per l'ID per cui riprendere da invece di utilizzare il contatore di pagine e l'offset. La struttura del codice non cambia. –

1

Ecco qualcosa che funziona per me. Utilizza anche AbstractIterator dalla libreria google-guava ma sfrutta Java8 Stream per semplificare l'implementazione. Restituisce un Iterator di elementi di tipo T.

Iterator<List<T>> pagingIterator = new AbstractIterator<List<T>>() { 
    private String resumeToken; 
    private boolean endOfData; 

    @Override 
    protected List<T> computeNext() { 
     if (endOfData) { 
      return endOfData(); 
     } 

     List<T> rows = executeQuery(resumeToken, PAGE_SIZE); 

     if (rows.isEmpty()) { 
      return endOfData(); 
     } else if (rows.size() < PAGE_SIZE) { 
      endOfData = true; 
     } else { 
      resumeToken = getResumeToken(rows.get(PAGE_SIZE - 1)); 
     } 

     return rows; 
    } 
}; 

// flatten Iterator of lists to a stream of single elements 
Stream<T> stream = StreamSupport.stream(Spliterators.spliteratorUnknownSize(pagingIterator, 0), false) 
    .flatMap(List::stream); 

// convert stream to Iterator<T> 
return stream.iterator(); 

È anche possibile restituire un Iterable utilizzando metodo di riferimento nel modo seguente:

// convert stream to Iterable<T> 
return stream::iterator; 
Problemi correlati