8.16.1 JPQLでのデータベースの参照および更新方法
JPQLは,データベースを検索・更新したり,データベースが持っている集合関数などの機能を利用したりするためのクエリ言語です。SQLがテーブルを対象としたクエリ言語であるのに対して,JPQLはエンティティクラスを対象としたJPA仕様で定義されているクエリ言語です。
クエリはアノテーションまたはO/Rマッピングファイルで定義できます。クエリと同じ永続化ユニットでエンティティが定義されている場合,エンティティの集合を表す抽象スキーマ型をクエリで使用できます。また,パス式を使用すると,永続化ユニット内で定義されたリレーションシップをわたってクエリを使用できます。パス式の詳細については,「8.17.4(2) パス式」を参照してください。
アプリケーションにJPQLを記述して実行すると,次の順序で接続先のデータベースに対してSQLが発行されます。
-
JPQLが実行されると,CJPAプロバイダによってJPQLの内容が解釈される。
-
対象となるエンティティクラス内に記述されたアノテーションやO/Rマッピングファイルの情報を基に,接続先のデータベース製品固有のSQL文に組み立てて発行する。
ここでは,JPQLの使用方法について説明します。
(1) Queryオブジェクトの取得方法
JPQLを使用してQueryオブジェクトを取得するためには,CJPAプロバイダが提供する次のEntityManagerインタフェースのメソッドを使用します。EntityManagerインタフェースのメソッドについて説明します。
(a) Query createQuery(String JPQL文)
createQueryの記述例を次に示します。引数には実行するJPQL文を指定します。
Query q = em.createQuery( "SELECT c " + "FROM Customer c " + " WHERE c.name LIKE "Smith");
(b) Query createNamedQuery(String クエリ名)
あらかじめ名前を付けて定義しておくことのできるクエリを名前付きクエリといいます。名前付きクエリは,@NamedQueryを任意のエンティティクラスに付与して定義します。@NamedQueryのname属性にクエリ名を指定して,query属性にはJPQL文を指定します。
CJPAプロバイダの場合,同じ名称の名前付きクエリを複数指定することはできません。同じ名称の名前付きクエリを複数指定した場合には,警告メッセージKDJE55535-Wを出力します。CJPAプロバイダで指定した場合,どのクエリが動作するかは保証しません。
次に,@NamedQueryの定義例を示します。この例では,@NamedQueryを使用してあらかじめfindAllCustomersWithNameという名前でクエリを登録しています。アプリケーションのcreateNamedQueryメソッドに登録した名前付きクエリ名を渡すことで,事前に登録されているクエリを取得して利用します。
-
@NamedQueryでのクエリ名の登録
@NamedQuery( name="findAllCustomersWithName", query="SELECT c FROM Customer c WHERE c.name LIKE :custName" ) @Entity public class Customer { ・・・ }
-
createNamedQueryメソッドでの名前付きクエリの記述例
@Stateless public class MySessionBean { ・・・ @PersistenceContext public EntityManager em; ・・・ public void doSomething() { ・・・ Query q = em.createNamedQuery("findAllCustomersWithName") .setParameter("custName", "Smith"); } }
なお,同一の永続化ユニット内では,ほかのエンティティで定義した名前付きクエリを使用することもできます。
(2) パラメタの指定方法
JPQLでは,クエリを生成する際に,WHERE節に記述する条件式にパラメタを使用して,動的に値を設定することができます。パラメタの値はQueryインタフェースのsetParameterメソッドで設定します。パラメタには,位置パラメタと名前付きパラメタがあります。それぞれについて説明します。
- 位置パラメタ
-
WHERE節の中のパラメタを組み込みたい位置に,「?」と数値の組み合わせを記述します。パラメタの値は,QueryインタフェースのsetParameterメソッドで設定します。位置パラメタの記述形式と記述例を次に示します。
-
記述形式
Query setParameter(int 位置, Object 値)
-
記述例
Query q = em.createQuery( "SELECT c FROM Customer c WHERE c.balance < ?1") .setParameter(1, 20000);
-
- 名前付きパラメタ
-
WHERE節の中のパラメタを組み込みたい位置に,「:」と任意の文字列(ただし,0〜9の文字を除く)の組み合わせを記述します。パラメタの値は,QueryインタフェースのsetParameterメソッドで設定します。名前付きパラメタの記述形式と記述例を次に示します。
-
記述形式
Query setParameter(String パラメタ名, Object 値)
パラメタ名の先頭には「:」を指定しないでください。また,名前付きパラメタは大文字・小文字を区別します。
-
記述例
Query q = em.createQuery( "SELECT c FROM Customer c WHERE c.name LIKE :custName") .setParameter("custName", "John");
-
なお,パラメタを使用するときには次のことに注意してください。
-
一つのクエリで位置パラメタと名前付きパラメタを混在して使用しないでください。CJPAプロバイダの場合,混在して使用したときの動作は保証しません。
-
setParameterメソッドには,java.util.Date型またはjava.util.Calendar型のオブジェクトをパラメタ値として設定するときに,次のように指定できます。なお,パラメタ値の時間タイプはTemporalType列挙型で指定する必要があります。詳細についてはJavaのドキュメントを参照してください。
-
Query setParameter(int 位置, Date 日付, TemporalType 時間タイプ)
-
Query setParameter(String パラメタ名, Date 日付, TemporalType 時間タイプ)
-
Query setParameter(int 位置, Calendar カレンダ, TemporalType 時間タイプ)
-
Query setParameter(String パラメタ名, Calendar カレンダ, TemporalType 時間タイプ)
-
(3) クエリ結果の取得および実行
生成したクエリを実行して,そのクエリ結果を返したり,更新クエリを実行したりするためには,Queryインタフェースの次のメソッドを使用します。それぞれのメソッドについて説明します。
(a) Object getSingleResult()
このメソッドは,クエリ結果を単一のオブジェクトとして返す場合に使用します。
メソッドを実行するとデータを検索します。検索の結果,ヒットした単一の行をエンティティオブジェクトに格納し,Object型で返します。Object型の戻り値は対象のエンティティクラスにキャストする必要があります。
複数の行がヒットした場合は,NonUniqueResultException例外が発生します。ヒットする行がなかった場合は,NoResultException例外が発生します。
(b) List getResultList()
このメソッドは,クエリ結果をリストとして返す場合に使用します。
メソッドを実行するとデータを検索します。検索の結果,ヒットした複数の行をエンティティオブジェクトに格納し,リストで返します。実行結果として複数の行が返されること想定しているため,ヒットする行が一つもなかった場合は,空のリストが返ります。
(c) int executeUpdate()
このメソッドは,更新クエリを実行する場合に使用します。
メソッドを実行するとテーブルから複数の行を一斉に削除または更新するクエリを実行します。実行結果にはヒットした行の件数が返ります。