わかりやすいJPA(1):SELECT文とPath式

はじめに

この連載は、JPAについて、書籍「わかりやすいJavaEE」の内容を補完するために書き下ろしました。JPQL、エンティティグラフ、ロック機構などについて解説します。18章のJPQLについての解説を先に読んでおくといいかもしれません。

クエリを実行して動作を確認するためのツールとして、暫定的に、書籍『Pro JPA2(Expert’s Voice in Java) 』(Mike Keith , Merrick Schincariol,  Apress社, 2013)のサポートサイトにあるツールとデータベースを使います。原本はサーブレットでしたが、簡単に実行できるようにJSFアプリケーションに直し、NetBeans用のプロジェクトファイルにしました。

解説を読む前に、ダウンロードして手元で実行できるようにしておいてください。次のリンクからプロジェクトファイルをダウンロードできます。

ただし、プロジェクトを実行する前に以下の手順を実行しておいてください。

手順を実行した解説ビデオもあります。参考にしてください。

(手順1)プロジェクトを実行する前に、データベースを作成する

  1. ダウンロードしたJpqlTest.zipを適切な場所に解凍する。内容はJpqlTextプロジェクトとdb.sqlです。
  2. NetBeansを起動し、サービスタブを開く
  3. 普段使っているデータベース接続をマウスの右ボタンでクリックし、[コマンドの実行]を選ぶ
  4. エディタがコマンド画面になるので、db.sqlファイルの内容をここにペーストする
    (あるいは、エクスプローラーからファイルアイコンをドラッグ・ドロップしてもよい)
  5. コマンド画面の上部にある[履歴 接続]のドロップダウンボックスをクリックし、データベース接続を選ぶ。3.でクリックした普段使っているデータベース接続を選択するとよい。
  6. [SQLの実行]ボタンを押す

(手順2)永続性ユニットを作成する

  1. JpqlTestプロジェクトを右クリックし、[新規]⇒[その他]と選択する
  2. 新規ファイルダイアログの左欄で[持続性]、右欄で[持続性ユニット]を選択して、[次へ]を押す
  3. [New持続性ユニット]で、[データソース]欄に適切なデータソースを指定して[終了]を押す。データソースは、db.sqlでテーブルとデータを作成したデータベースに関連づけられているものを選択する。
    (「わかりやすいJavaEE」ではjdbc/mydbでした)

JpqlTestで操作できるエンティティ(ドメインモデル)

まず操作対象とするエンティティについて、理解しておきましょう。

JpqlTestが扱うエンティティのセット(ドメインモデルといいます)は、Employeeエンティティを中心に5つあります。次の図を見て、関係を把握しましょう(下記の説明を参照)。

domain model

Employeeエンティティ

中心になるエンティティで、id, name(氏名), salary(給与), date(入社期日) などの基本項目に加えて、address(住所記録)、phones(所有するすべての電話)、department(部署)、manager(直属の上司)、direct(統括するすべての部下)、project(所属するプロジェクトチーム)などがあります。

その他のエンティティ

Employeeのメンバを順に見ていけば、すぐに関係が理解できます。

address

addressはAddressエンティティを参照する一方向のOne-To-Oneの関係です。一方向であることを示すために、矢印は片側にだけ付けています。これ以外のオブジェクトメンバは全て双方向です。

phones

phonesは所有する電話のリストで、複数の電話番号をもっているため、PhoneエンティティのemployeesとOne-To-Manyの関係です。

department

departmentは所属部署です。同じ部署には複数の社員が所属するので、DepartmentエンティティのemployeesとMany-To-Oneの関係です。

manager

managerは直属の上司を表します。ManegerがEmployee型であることに注意してください。一人のmanagerに複数の部下がいるので、EmployeeエンティティのdirectとMany-To-Oneの関係です。

direct

directは直属の部下を表しています。同じEmployeeエンティティのmanagerとOne-To-Manyの関係になります。このように、Employeeエンティティの中に、Employee型にもとづく関係があっても何ら問題ありません。

project

projectは所属するプロジェクトチームを表します。一人の社員が複数のプロジェクトに所属する一方、ひとつのプロジェクトには複数の社員が所属するので、これはProjectエンティティのemployeesとMany-To-Manyの関係です。

※データベースには、この他ProjectのサブクラスであるDesigenProjectエンティティとQualithProjectエンティティが含まれています。

SELECT 文とは

SELECE 文 はデータベースから、該当するデータを一括して抽出する命令文です。その一般的な形式は次のようです。SELEC句には実行結果の形式を指定し、FROM句には検索する対象のエンティティを指定します。残りのWHERE(フィルタリング条件)とORDER BY(並び順)は、必要なければ省略可能できます。

SELECT <・・・>
FROM <・・・>
[WHERE <・・・>
[ORDER BY <・・・>

※[ ]部分は省略可能

最も簡単なSELECT文は次のようです。

SELECT e FROM Employee e

出力結果

(JpqlTestの[SELECT1]ボタンで実行できます。あるいは、上段の入力領域にコマンドを直接入力して[dynamic]ボタンをおしても実行できます)

[ Employee [id: 1, name: John, salary: 55000, address_Id: 1, phones_Sz: 2, manager_Id: 9, department_Id: 2, directs_Sz: ***, projects_Sz: 1] ]
[ Employee [id: 2, name: Rob, salary: 53000, address_Id: 2, phones_Sz: 1, manager_Id: 9, department_Id: 2, directs_Sz: ***, projects_Sz: 2] ]
[ Employee [id: 3, name: Peter, salary: 40000, address_Id: 3, phones_Sz: 2, manager_Id: 9, department_Id: 2, directs_Sz: ***, projects_Sz: 3] ]
[ Employee [id: 4, name: Frank, salary: 41000, address_Id: 4, phones_Sz: 2, manager_Id: 10, department_Id: 1, directs_Sz: ***, projects_Sz: 1] ]
[ Employee [id: 5, name: Scott, salary: 60000, address_Id: 5, phones_Sz: 2, manager_Id: 10, department_Id: 1, directs_Sz: ***, projects_Sz: 2] ]
[ Employee [id: 6, name: Sue, salary: 62000, address_Id: 6, phones_Sz: 3, manager_Id: 10, department_Id: 1, directs_Sz: ***, projects_Sz: 2] ]
[ Employee [id: 7, name: Stephanie, salary: 54000, address_Id: 7, phones_Sz: 2, manager_Id: 10, department_Id: 1, directs_Sz: ***, projects_Sz: 1] ]
[ Employee [id: 8, name: Jennifer, salary: 45000, address_Id: 8, phones_Sz: 1, manager_Id: ***, department_Id: 1, directs_Sz: ***, projects_Sz: 2] ]
[ Employee [id: 9, name: Sarah, salary: 52000, address_Id: 9, phones_Sz: 3, manager_Id: 10, department_Id: 2, directs_Sz: 3, projects_Sz: 2] ]
[ Employee [id: 10, name: Joan, salary: 59000, address_Id: 10, phones_Sz: 3, manager_Id: ***, department_Id: 1, directs_Sz: 5, projects_Sz: 3] ]
[ Employee [id: 11, name: Marcus, salary: 35000, address_Id: ***, phones_Sz: ***, manager_Id: ***, department_Id: ***, directs_Sz: 1, projects_Sz: ***] ]
[ Employee [id: 12, name: Joe, salary: 36000, address_Id: ***, phones_Sz: ***, manager_Id: 11, department_Id: 3, directs_Sz: ***, projects_Sz: ***] ]
[ Employee [id: 13, name: Jack, salary: 43000, address_Id: ***, phones_Sz: ***, manager_Id: ***, department_Id: 3, directs_Sz: ***, projects_Sz: ***] ]

※EmployeeクラスのtoStringメソッドで、上の出力形式を指定しています。他のエンティティでも同じですので、一度、確認しておいてください。

JPQLはSQLと似ていますが、SQLがデータベーステーブルを対象とするのに対して、JPQLはエンティティを対象とする点が大きな違いです。

また、SQLでは、SELECT  * FROM ~ のように書きますが、JPQLでは変数( ここではe ) を指定します。この e は、識別変数(identification variable)といい、Employeeエンティティのエイリアスです。つまり、Entityの全体を意味するので、実行するとEmployeeオブジェクト全体を取得できます。データベーステーブルでは全てのカラムということになります。

また、このSELECT文を実行した結果は、エンティティマネージャーのgetResultListメソッド(後のページで解説)を使って、EmployeeエンティティのListとして取得できます。

JPQLはSQLに変換して実行される

JPQLは、SQLに変換して実行されるので、実行の際、どのようなSQLに変換されるか、知っておくと、無駄なクエリをたくさん発行してパフォーマンスが低下するという事態をさけることができます。

そこで、永続性ユニット(persistence.xml)に次の2行を追加しておきましょう。これで、サーバーログの中に、SQLの実行ログが表示されるようになります。

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" ~省略~>
  <persistence-unit name="JpqlTestPU" transaction-type="JTA">
    <jta-data-source>jdbc/mydb2</jta-data-source>
    <properties>
      <property name="javax.persistence.schema-generation.database.action" value="create"/>
      <property name="eclipselink.logging.level.sql" value="FINE"/>
      <property name="eclipselink.logging.parameters" value="true"/>     
    </properties>
  </persistence-unit>
</persistence>

Path式

Java言語と同じように、識別変数にドット( . )を付けるとそのメンバを指定できます。このような表現をパス式(Path Expression)と言います。

SELECT e.name FROM Employee e

e.nameは、Employeeのメンバであるnameを指します。
JpqlTestを実行して、[SELECT4]というボタンを押してみてください。このボタンは、上のクエリを実行し、結果を表示します。結果として次のような名前の文字列を取得していることがわかるはずです。

[ John ]
[ Rob ]
[ Peter ]
[ Frank ]
[ Scott ]
[ Sue ]
[ Stephanie ]
[ Jennifer ]
[ Sarah ]
[ Joan ]
[ Marcus ]
[ Joe ]
[ Jack ]

ドメインモデルで説明したように、Employeeクラスには単純なフィールド変数以外に、あらゆるオブジェクト関係マッピングを持つフィールドがありました。

例えば、住所記録を表すaddress があるので、e.addressはもちろん、e.address.state のように、Addressエンティティのメンバもアクセスできます。Adressクラスのメンバは、ドメインモデルの図を見てください(ソースコード全体は、ダウンロードしたJpqlTestプロジェクトにあります)。

そこで、つぎのようなSELECT文を実行してみましょう。

SELECT e.address.state FROM Employee e

JpqlTest画面の最上段にある入力領域の枠内にこのSELECT文を書いて、[dynamic]ボタンをクリックすると、次のように表示されます。確かに、Addressエンティティのフィールドから値を取得していることが分かります。

[ NY ]
[ NY ]
[ NY ]
[ CA ]
[ CA ]
[ CA ]
[ CA ]
[ NJ ]
[ NY ]
[ CA ]

このように、パス式はとても強力です。可能な限りどこまでもオブジェクトの連鎖(オブジェクトグラフといいます)をたどることができます。データベースでは、実際にはいくつかのテーブルにまたがる操作ですが、オブジェクトの考え方だけで、簡単に記述できるのは大きな利点です。

ただ、パス式を使うとデータベース側ではいくつものテーブルに対するSQL文が発行されます。そのため、場合によっては検索がひどくスピードダウンしてしまいます。ですから、実際にはどのようなSQLが実行されているか、GlassFishサーバーログを見て、常々、確認するようにしてください。

なお、スピードダウンを回避する方法は JOIN FETCH(第5回)、エンティティグラフ(第8~10回)で解説します。

読者になる

コメント

  1. BlueMoon より:

    表結合が簡単な記述でできるのですね。ログの取得方法の記載も助かります。やはり実際にどんなSQLが実行されているかは気になるところです。

    プロジェクトをダウンロードして試してみました。Mysql環境ですが動かすことができました。1点気になったのですが、オリジナルのデータソース定義(jdbc/jpql)が残っていますので、持続性ユニット作成のウィザードでデータソースの選択肢に出てきてしまいます。防ぐには、ファイルタブのsetup配下に有るglassfish-resources.xmlファイル内のエントリを削除すれば良いです。

    • t.kawaba より:

      ありがとうございます。
      修正したJpqlTest.zip に入れ替えました。
      解説ビデオも付けました。

  2. BlueMoon より:

    早速の対応、有難うございました。
    動画まで作成頂き、お疲れ様でした。

コメントをどうぞ

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

%d人のブロガーが「いいね」をつけました。