[Java] 48. JPAでQueryを使う方法(JPQLクエリを作成する方法)
こんにちは。明月です。
この投稿はJPAでQueryを使う方法(JPQLクエリを作成する方法)に関する説明です。
JPAで基本的にデータを取得する方法はEntityで宣言されているNameQueryを通ってデータを取得します。そしてリファレンスのJoinになったデータを取得するために取得するデータでListの関数get、size関数を通ってデータを取得します。
import java.util.List;
import java.util.Optional;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import model.User;
import model.Info;
public class Main {
// ラムダ式のインタフェース
interface Expression {
void run(EntityManager em);
}
// PersistenceからEntityManagerを呼び出してから実行と終了する関数
private static void transaction(Expression lambda) {
// FactoryManagerを生成する。"JpaExample"はpersistence.xmlに書いている名だ。
EntityManagerFactory emf = Persistence.createEntityManagerFactory("JpaExample");
// Managerを生成する。
EntityManager em = emf.createEntityManager();
try {
// transactionを取得。
EntityTransaction transaction = em.getTransaction();
try {
// transaction実行
transaction.begin();
// ラムダ式を実行する。
lambda.run(em);
// transactionをコミットする。
transaction.commit();
} catch (Throwable e) {
// エラーが発生すればrollbackする。
if (transaction.isActive()) {
transaction.rollback();
}
// エラー出力
e.printStackTrace();
}
} finally {
// 各FactoryManagerとManagerを閉める。
em.close();
emf.close();
}
}
// 実行関数
@SuppressWarnings("unchecked")
public static void main(String... args) {
transaction((em) -> {
// userクラスのNamedQueryのUser.findAllのクエリ通りデータを取得する。
List<User> users = em.createNamedQuery("User.findAll").getResultList();
// nowonbunデータ取得
Optional<User> op = users.stream().filter(x -> "nowonbun".equals(x.getId())).findFirst();
// データがあれば
if (op.isPresent()) {
// データ取得
User user = op.get();
// コンソール出力
System.out.println(user.getId());
System.out.println(user.getName());
}
});
}
}
でも、上の方法の問題はデータをすべて取得してコード上のfilterや分岐でデータを取得します。
つまり、データが多いとシステムが遅くなります。
そうならクエリでデータベースからデータを取得する段階でデータを分類して取得した後、処理する方が良いです。
JPAはクエリでデータを取得する方法が二つがあります。一つはJPAで使うJPQLクエリで取得する方法とデータベースで使うSQLクエリで取得する方法です。
まず、データベースで使うSQLクエリを利用する例です。
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;
public class Main {
// ラムダ式のインタフェース
interface Expression {
void run(EntityManager em);
}
// PersistenceからEntityManagerを呼び出してから実行と終了する関数
private static void transaction(Expression lambda) {
// FactoryManagerを生成する。"JpaExample"はpersistence.xmlに書いている名だ。
EntityManagerFactory emf = Persistence.createEntityManagerFactory("JpaExample");
// Managerを生成する。
EntityManager em = emf.createEntityManager();
try {
// transactionを取得。
EntityTransaction transaction = em.getTransaction();
try {
// transaction実行
transaction.begin();
// ラムダ式を実行する。
lambda.run(em);
// transactionをコミットする。
transaction.commit();
} catch (Throwable e) {
// エラーが発生すればrollbackする。
if (transaction.isActive()) {
transaction.rollback();
}
// エラー出力
e.printStackTrace();
}
} finally {
// 各FactoryManagerとManagerを閉める。
em.close();
emf.close();
}
}
// 実行関数
public static void main(String... args) {
transaction((em) -> {
// SQLクエリ入力
Query qy = em.createNativeQuery("select * from user u where u.id = ?");
// パラメータ入力
qy.setParameter(1, "nowonbun");
// データ取得
Object[] user = (Object[])qy.getSingleResult();
// コンソール出力
System.out.println(user[0]);
System.out.println(user[1]);
});
}
}
ManagerクラスのcreateNativeQuery関数を利用してデータベースのSQLクエリでデータを取得することができます。
問題はJPAで生成したEntityクラスのマッピングでできないことです。
JPAを利用して取得したデータはデータを修正すると、その値をデータベースにupdateするし、削除するし、様々な作業ができます。でも、クラスのマッピングができなければJPAを使う理由がないです。
そのため、JPQLクエリでデータを取得したデータではなければJPA機能を完全に使えないです。実はJPQLクエリでもSQLと全然違う種類のクエリではありません。ほぼ90%は似てます。
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;
import model.User;
public class Main {
// ラムダ式のインタフェース
interface Expression {
void run(EntityManager em);
}
// PersistenceからEntityManagerを呼び出してから実行と終了する関数
private static void transaction(Expression lambda) {
// FactoryManagerを生成する。"JpaExample"はpersistence.xmlに書いている名だ。
EntityManagerFactory emf = Persistence.createEntityManagerFactory("JpaExample");
// Managerを生成する。
EntityManager em = emf.createEntityManager();
try {
// transactionを取得。
EntityTransaction transaction = em.getTransaction();
try {
// transaction実行
transaction.begin();
// ラムダ式を実行する。
lambda.run(em);
// transactionをコミットする。
transaction.commit();
} catch (Throwable e) {
// エラーが発生すればrollbackする。
if (transaction.isActive()) {
transaction.rollback();
}
// エラー出力
e.printStackTrace();
}
} finally {
// 各FactoryManagerとManagerを閉める。
em.close();
emf.close();
}
}
// 実行関数
public static void main(String... args) {
transaction((em) -> {
// JPQLクエリ
Query qy = em.createQuery("select u from User u where u.id = :id");
// パラメータ入力
qy.setParameter("id", "nowonbun");
// データ取得
User user = (User)qy.getSingleResult();
// コンソール出力
System.out.println(user.getId());
System.out.println(user.getName());
});
}
}
SQLクエリと違う点はテーブル名です。SQLにはテーブル名を大小文字区分なしで取得ができますが、JPQLはテーブル名ではなくクラス名です。
つまり、クラス名は大小文字を区分するので、userではなく、正確にUserということに作成しなければならないです。
そしてアスタリスクマーク(*)ですべてのデータを取得することもありません。
リターンするクラスを使います。我々はUserクラスでリターンするためにUserクラスの置換名(Aliases)というuを使います。
そしてwhere節にある検索のフィールド名はSQLのカラム名ではなく、クラスの変数名です。
現在の例は変数名とSQLカラム名が同じなので同じくみえます。
そうなら変数名を変更してテストしましょう。
idに@Columnアトリビュートを使ってsqlのカラム名をマッピングして変数名はtestに変更しましょう。
그럼 JPQL에서는 id가 아닌 test를 사용해야 합니다.
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;
import model.User;
public class Main {
// ラムダ式のインタフェース
interface Expression {
void run(EntityManager em);
}
// PersistenceからEntityManagerを呼び出してから実行と終了する関数
private static void transaction(Expression lambda) {
// FactoryManagerを生成する。"JpaExample"はpersistence.xmlに書いている名だ。
EntityManagerFactory emf = Persistence.createEntityManagerFactory("JpaExample");
// Managerを生成する。
EntityManager em = emf.createEntityManager();
try {
// transactionを取得。
EntityTransaction transaction = em.getTransaction();
try {
// transaction実行
transaction.begin();
// ラムダ式を実行する。
lambda.run(em);
// transactionをコミットする。
transaction.commit();
} catch (Throwable e) {
// エラーが発生すればrollbackする。
if (transaction.isActive()) {
transaction.rollback();
}
// エラー出力
e.printStackTrace();
}
} finally {
// 各FactoryManagerとManagerを閉める。
em.close();
emf.close();
}
}
// 実行関数
public static void main(String... args) {
transaction((em) -> {
// JQPLクエリ入力
Query qy = em.createQuery("select u from User u where u.test = :id");
// パラメータ入力
qy.setParameter("id", "nowonbun");
// データ取得
User user = (User)qy.getSingleResult();
// コンソール出力
System.out.println(user.getId());
System.out.println(user.getName());
});
}
}
上のクエリではidではなく、testの変数名を使いました。もちろん、変数名なので大小文字区分は重要です。
そしたら結果はObject配列タイプではなく、UserタイプでリターンしてUserデータを使います。
JQPLのクエリはFETCHのキーワードでリファレンスデータの結果も制御できます。
上の例みたいにinfoテーブルにageが21のデータを大量インサートしました。
でも、私は20のデータだけInfoリストに検索したいです。
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;
import model.User;
public class Main {
// ラムダ式のインタフェース
interface Expression {
void run(EntityManager em);
}
// PersistenceからEntityManagerを呼び出してから実行と終了する関数
private static void transaction(Expression lambda) {
// FactoryManagerを生成する。"JpaExample"はpersistence.xmlに書いている名だ。
EntityManagerFactory emf = Persistence.createEntityManagerFactory("JpaExample");
// Managerを生成する。
EntityManager em = emf.createEntityManager();
try {
// transactionを取得。
EntityTransaction transaction = em.getTransaction();
try {
// transaction実行
transaction.begin();
// ラムダ式を実行する。
lambda.run(em);
// transactionをコミットする。
transaction.commit();
} catch (Throwable e) {
// エラーが発生すればrollbackする。
if (transaction.isActive()) {
transaction.rollback();
}
// エラー出力
e.printStackTrace();
}
} finally {
// 各FactoryManagerとManagerを閉める。
em.close();
emf.close();
}
}
// 実行関数
public static void main(String... args) {
transaction((em) -> {
// JPQL作成
Query qy = em.createQuery("select u from User u join fetch u.infos i where u.id = :id and i.age = 20");
// パラメータ入力
qy.setParameter("id", "nowonbun");
// データ取得
User user = (User)qy.getSingleResult();
// コンソール出力
System.out.println(user.getInfos().size());
System.out.println(user.getInfos().get(0).getAge());
});
}
}
join fetchの構文をいれてinfosをiに置換してi.ageを20だけ検索するように設定しました。
結果はgetInfosのデータの個数は一つだし、そのデータのageは20だけあることを確認できます。
JPQLもstringタイプで作成するクエリなのでソースのあっちこっちに作成すると管理が難しいですね。
それで普通はEntityにクエリを作成して実際のロジックソースにはクエリを読み込んで使うタイプで作成します。
Entityクラスの上に@NamedQueriesアトリビュートを利用してクエリを片付けます。
参考に私はIDEをeclipseツールを使います。Entityの上にクエリを作成するとStringデータでもQuery検査をします。でも、これがバグがあります。fetch join置換値を認識できません。
もちろん、無視してデバッグ実行しても問題ないですが、ツールでエラー表示が出ると見づらいです。
プロジェクトのpreferencesに入ってJPA -> query部分のエラー表示を変更しましょう。
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;
import model.User;
public class Main {
// ラムダ式のインタフェース
interface Expression {
void run(EntityManager em);
}
// PersistenceからEntityManagerを呼び出してから実行と終了する関数
private static void transaction(Expression lambda) {
// FactoryManagerを生成する。"JpaExample"はpersistence.xmlに書いている名だ。
EntityManagerFactory emf = Persistence.createEntityManagerFactory("JpaExample");
// Managerを生成する。
EntityManager em = emf.createEntityManager();
try {
// transactionを取得。
EntityTransaction transaction = em.getTransaction();
try {
// transaction実行
transaction.begin();
// ラムダ式を実行する。
lambda.run(em);
// transactionをコミットする。
transaction.commit();
} catch (Throwable e) {
// エラーが発生すればrollbackする。
if (transaction.isActive()) {
transaction.rollback();
}
// エラー出力
e.printStackTrace();
}
} finally {
// 各FactoryManagerとManagerを閉める。
em.close();
emf.close();
}
}
// 実行関数
public static void main(String... args) {
transaction((em) -> {
// Entityの@NamedQueryからデータを取得する。
Query qy = em.createNamedQuery("User.nowonbun", User.class);
// パラメータ入力
qy.setParameter("id", "nowonbun");
// データ取得
User user = (User)qy.getSingleResult();
// コンソール出力
System.out.println(user.getInfos().size());
System.out.println(user.getInfos().get(0).getAge());
});
}
}
そしてcreateNamedQueryを通ってEntityクラスからクエリを読み込んでデータを取得しても結果は同じです。
こんなにするとすべてのクエリを一括で管理ができますね。データベースのテーブル変更が発生する時にそのことに関するアップデータ管理も楽になりますね。
ここまでJPAでQueryを使う方法(JPQLクエリを作成する方法)に関する説明でした。
ご不明なところや間違いところがあればコメントしてください。
- [Java] 55. Spring frameworkに文字化けを解決する方法(Encoding設定)2021/06/30 16:37:16
- [Java] 54. Spring frameworkでWeb filterを使う方法2021/06/29 18:25:12
- [Java] 53. ウェブサービス(Web service)でエラーページを処理する方法2021/06/25 13:35:54
- [Java] 52. SpringフレームワークでDAOをFactory method Patternを利用して依存性注入する方法2019/10/17 07:15:48
- [Java] 51. SpringフレームワークでJPAを使い方(依存性注入@Autowired)2019/10/16 07:32:55
- [Java] 50. JPAプロジェクトでDAOクラスを作成する方法2019/10/15 20:12:35
- [Java] 49. JPAでトランザクション(transaction)を扱う方法と共通関数作成する方法(Observerパターン)2019/10/14 20:13:44
- [Java] 48. JPAでQueryを使う方法(JPQLクエリを作成する方法)2019/10/13 22:55:52
- [Java] 47. JPAのEntityクラスのリファレンス設定(cascade, fetch)2019/10/13 00:40:08
- [Java] 46. JPAのEntityクラスの基本設定(@GeneratedValue、 @ManyToMany)2019/10/11 07:30:14
- [Java] 45. JPAを設定する方法2019/10/10 07:29:43
- [Java] 44. Web Spring frameworkのviewで使うプログラム言語(JSTL) - XML2019/10/09 07:34:08
- [Java] 43. Web Spring frameworkのviewで使うプログラム言語(JSTL) - 関数、データベース2019/10/08 07:43:33
- [Java] 42. Web Spring frameworkのviewで使うプログラム言語(JSTL) - コア―、フォーマット2019/10/07 07:38:13
- [Java] 41. Web Spring webframeworkのControllerからajaxの要請する時、jsonタイプのデータを返却する方法2019/10/04 19:24:43
- check2024/04/10 19:03:53
- [Java] 64.Spring bootとReactを連結する方法(Buildする方法)2022/03/25 21:02:18
- [Javascript] Node.jsをインストールしてReactを使う方法2022/03/23 18:01:34
- [Java] 63. Spring bootでcronスケジューラとComponentアノテーション2022/03/16 18:57:30
- [Java] 62. Spring bootでWeb-Filterを設定する方法(Spring Security)2022/03/15 22:16:37
- [Java] JWT(Json Web Token)を発行、確認する方法2022/03/14 19:12:58
- [Java] 61. Spring bootでRedisデータベースを利用してセッションクラスタリング設定する方法2022/03/01 18:20:52
- [Java] 60. Spring bootでApacheの連結とロードバランシングを設定する方法2022/02/28 18:45:48
- [Java] 59. Spring bootのJPAでEntityManagerを使い方2022/02/25 18:27:48
- [Java] 58. EclipseでSpring bootのJPAを設定する方法2022/02/23 18:11:10
- [Java] 57. EclipseでSpring bootを設定する方法2022/02/22 19:04:49
- [Python] Redisデータベースに接続して使い方2022/02/21 18:23:49
- [Java] Redisデータベースを接続して使い方(Jedisライブラリ)2022/02/16 18:13:17
- [C#] Redisのデータベースを接続して使い方2022/02/15 18:46:09
- [CentOS] Redisデータベースをインストールする方法とコマンドを使い方2022/02/14 18:33:07