Created
May 2, 2012 08:16
-
-
Save komiya-atsushi/2574993 to your computer and use it in GitHub Desktop.
Play! framework の JPAQuery を Iterator/Iterable でラップして for-each で回せるようにしたもの。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 <T> エンティティクラスの型を指定します。 | |
*/ | |
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