2012-09-24 12 views
6

Tôi thấy mẫu này rất nhiều.truy vấn được phân trang/công thức lặp lại

Mở 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); 

On khách hàng:

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

Hầu hết những nơi tung phiên bản riêng của họ về hai phương pháp này, và việc triển khai là đáng ngạc nhiên khó khăn để có được quyền. Có rất nhiều lỗi trường hợp cạnh.

Có một công thức hoặc thư viện chuẩn cho các truy vấn này không?

(bạn có thể thực hiện một số giả định đơn giản hóa cho bộ nhớ phía máy chủ, ví dụ: T có thứ tự tự nhiên).

Trả lời

1

Đây là một cách sử dụng AbstractIterator từ thư viện google-ổi và mùa xuân-jdbc để thực sự truy vấn cơ sở dữ liệu:

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 ẩn hầu hết các biến chứng liên quan đến văn bản thực hiện của riêng bạn Iterator. Bạn chỉ cần thực hiện phương thức computeNext trả về giá trị tiếp theo trong trình lặp hoặc gọi endOfData để cho biết không có thêm giá trị nào trong trình lặp.

+0

Tôi không nghĩ rằng đây sẽ dtrt nếu bảng được đồng thời sửa đổi, bởi vì tất cả các offsets tiếp theo sẽ không phù hợp. – ashm

+0

Trong trường hợp đó, bạn chuyển một tham số bổ sung cho id cho vị trí tiếp tục từ thay vì sử dụng bộ đếm trang và bù đắp. Cấu trúc mã không thay đổi. –

1

Đây là một cái gì đó phù hợp với tôi. Nó cũng sử dụng AbstractIterator từ thư viện google-guava nhưng tận dụng Java8 Stream để đơn giản hóa việc thực hiện. Nó trả về một Iterator của phần tử kiểu 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(); 

Nó cũng có thể trả về một Iterable bằng cách sử dụng phương pháp tham khảo theo cách sau:

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