LoginSignup
17
16

More than 5 years have passed since last update.

JPQLのLIMIT OFFSETの罠

Last updated at Posted at 2014-07-14

確認したバージョン

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で落としてください。。。

17
16
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
16