Skip to content

Instantly share code, notes, and snippets.

@komiya-atsushi
Created May 2, 2012 08:16
Show Gist options
  • Save komiya-atsushi/2574993 to your computer and use it in GitHub Desktop.
Save komiya-atsushi/2574993 to your computer and use it in GitHub Desktop.
Play! framework の JPAQuery を Iterator/Iterable でラップして for-each で回せるようにしたもの。
package helpers;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import play.db.jpa.GenericModel.JPAQuery;
/**
* JPA のクエリ結果を Iterator で取得する機能を提供します。
* クエリの結果が大量の件数になる場合など、ヒープを圧迫しうる場合に利用すると効果的です。
* <p>
* for-each ループで回す場合は、JPAQueryIterator.create() メソッドを利用します。
* </p>
* <code>
* // 例
* for (SomeModel model : JPAQueryIterator.<SomeModel>create(SomeModel.find(""))) {
* // do something
* }
* </code>
*
* @author komiya
*
* @param &lt;T&gt; エンティティクラスの型を指定します。
*/
public class JPAQueryIterator<T> implements Iterator<T> {
/** pageSize の規定値 */
public static final int DEFAULT_PAGE_SIZE = 128;
/** JPA クエリ */
private JPAQuery query;
/** 1回のクエリで取得する件数 */
private int pageSize;
/** クエリを実行した回数 */
private int queryCount;
/**
* クエリ結果のリストを走査する Iterator オブジェクトを保持します。
*
* この iterator に null kが設定されている場合、これ以上の要素がないことを表します。
*/
private Iterator<T> iterator = Collections.<T> emptyList().iterator();
/** 次の next() メソッド呼び出しで返却する要素 */
private T nextObj;
/**
* クエリとページサイズを指定し、JPAQueryIterator オブジェクトを生成します。
*
* @param query
* @param pageSize
*/
private JPAQueryIterator(JPAQuery query, int pageSize) {
this.query = query;
this.pageSize = pageSize;
}
/**
* 次のページの結果を取得するクエリを実行します。
* <p>
* クエリの結果、得られた結果が 0 件の場合に iterator に null を代入します。
* </p>
*
* @return クエリを実行し、得られた結果が 0 件の場合に false を返却します。1 件以上の結果が得られた場合は true
* を返却します。
*/
private boolean doNextPageQuery() {
List<T> results = query.fetch(queryCount + 1, pageSize);
if (results == null || results.isEmpty()) {
iterator = null;
return false;
}
queryCount++;
iterator = results.iterator();
return true;
}
@Override
public boolean hasNext() {
if (nextObj != null) {
return true;
}
if (iterator == null) {
// iterator が null ということは、終端に達していることを意味する
return false;
}
if (iterator.hasNext() || doNextPageQuery()) {
nextObj = iterator.next();
return true;
}
return false;
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
T result = nextObj;
nextObj = null;
return result;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
/**
* 指定された JPAQuery の結果を返す Iterable オブジェクトを返却します。
*
* @param query
* @return
*/
public static <T> Iterable<T> create(JPAQuery query) {
return create(query, DEFAULT_PAGE_SIZE);
}
/**
* 指定された JPAQuery の結果を返す Iterable オブジェクトを返却します。pageSize
* により、1回のクエリで一括して取得する件数を指定できます。
*
* @param query
* @param pageSize
* @return
*/
public static <T> Iterable<T> create(JPAQuery query, int pageSize) {
if (query == null) {
throw new IllegalArgumentException("query に null は指定できません。");
}
if (pageSize < 1) {
throw new IllegalArgumentException("pageSize には 1 以上の値を指定してください。");
}
return new IterableImpl<T>(query, pageSize);
}
private static class IterableImpl<T> implements Iterable<T> {
private JPAQuery query;
private int pageSize;
public IterableImpl(JPAQuery query, int pageSize) {
this.query = query;
this.pageSize = pageSize;
}
@Override
public Iterator<T> iterator() {
return new JPAQueryIterator<T>(query, pageSize);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment