確認したバージョン
GlassFish4.0にバンドルされているJPA2でDBはPostgreSQL9.2で現象を確認しています。
JPA2によるLIMIT OFFSET句
JPA2(EclipseLink)はJPQLで
javax.persistence.TypedQuery#setMaxResults(int)
及びjavax.persistence.TypedQuery#setFirstResult(int)
によってPostgreSQLのLIMIT OFFSET
句をサポートしています。
例えば、ApplicationUserというEntityに対してfind
というNamedQueryで取得するようなJavaのコードを以下のように書いた場合、
@PersistenceUnit
private EntityManager entityManager;
public List<ApplicationUser> find() {
List<ApplicationUser> list = this.entityManager
.createNamedQuery("ApplicationUser.find", ApplicationUser.class)
.setFirstResult(0)
.setMaxResults(10)
.getResultList();
return list;
}
SQLは以下の様に出力されます(必要部分のみに加工)
SELECT
user_id,
user_name,
FROM
application_user
LIMIT 10 OFFSET 0
ここまでは期待通りなのですが、これをNativeQueryと絡めて実行した場合、以下の様になります
@PersistenceUnit
private EntityManager entityManager;
public List<ApplicationUser> find() {
List<ApplicationUser> list = this.entityManager
// この部分がNativeQueryかNamedQueryか違うだけ。
.createNativeQuery("SELECT user_id, user_name FROM application_user", ApplicationUser.class)
.setFirstResult(0)
.setMaxResults(10)
.getResultList();
return list;
}
結果実行されるSQLは以下のようになります。
SELECT
user_id,
user_name,
FROM
application_user
LIMIT OFFSET
句が消えてしまっている事が分かるでしょうか。
ちなみに実行結果はどちらも同じになります。
この事から、NamedQueryを用いた場合はSQLによって処理が行なわれますが、NativeQueryを用いた場合は一旦全件取得した上で、プログラムにより結果件数のみ返すようになっているようです。
自分の場合は大量データを取得する場所でNativeQueryとsetMaxResults/setFirstResultが利用されており、パフォーマンス遅延でこの問題に気がつきました。
おまけ
お願いだから気を利かせて実行せずに、潔く実行時にExceptionで落としてください。。。