Entity Graphを使ってみる
TweetPosted on Sunday Jan 26, 2014 at 04:05PM in Technology
JPA2.1の新機能Entity Graphを使ってみます。SELECT文を実行するときにどこまで階層を掘り下げて拾ってくるかを指定することができるようになり、むだにManaged状態のときにsize()を呼び出したりしなくてよくなる仕組みのようです。
環境
- Hibernate 4.3.0.Final
- WildFly8.0.0.CR1
- Oracle JDK7u51
- postgresql-9.3-1100.jdbc41.jar
- PostgreSQL 9.2.4
使い方
- エンティティの階層を表すEntity Graphを作ります。JPQLのような名前付きの静的なものとして作っておく他に、動的にコードから作ることも可能。Canonical MetaModelも使えるようです。
- Query#setHint()メソッドを使ってEntity Graphを渡してやってからクエリを実行します。ヒント名には2つあり、どちらを使うかで以下のように動作が変わります
- javax.persistence.fetchgraph: Entity Graphに含まれるフィールドのみロードされる。含まれないフィールドはロードされない。Entity Graphに含まれないフィールドは全てLAZY扱いになる
- javax.persistence.loadgraph: Entity Graphに含まれるフィールドはLAZYであってもロードされる。含まれないフィールドはそれぞれのフィールドの設定による
前提条件
NativeQueryで関連持ちエンティティをSELECTしてみるで使った資源を使います。エンティティはこれです
試してみる
javax.persistence.fetchgraph
Entity Graphを作る
Employeeクラスにこんなアノテーションを付けます
@NamedEntityGraph( name = "onlyFirstNameAndLastName", attributeNodes = { @NamedAttributeNode("firstName"), @NamedAttributeNode("lastName")})
orm.xmlにクエリを定義
<named-query name="findEmployees"> <query><![CDATA[ SELECT e FROM Employee e ]]></query> </named-query>
テストメソッド
@Test @Transactional @UsingDataSet({"datasets/relativeSelect/dept.yml", "datasets/relativeSelect/employees.yml"}) public void select() throws Exception { EntityGraph<?> entityGraph = em.getEntityGraph("onlyFirstNameAndLastName"); dumpEmployeeList(em.createNamedQuery("findEmployees", Employee.class) .setHint("javax.persistence.fetchgraph", entityGraph) .getResultList()); } protected void dumpEmployeeList(List<Employee> employees){ for(Employee emp : employees){ System.out.printf(" emp: id=%d, firstName=%s, lastName=%s\n", emp.getId(), emp.getFirstName(), emp.getLastName()); } }
テストメソッドを実行
javax.persistence.fetchgraphを指定して、Entity GraphではfirstNameとlastNameのみ指定しているので、deptはロードされないと思っていたら…
17:27:12,991 INFO [stdout] (pool-2-thread-34) Hibernate: select employee0_.id as id1_1_, employee0_.dept_id as dept_id4_1_, employee0_.firstName as firstNam2_1_, employee0_.lastName as lastName3_1_ from Employee employee0_ 17:27:12,993 INFO [stdout] (pool-2-thread-34) Hibernate: select dept0_.id as id1_0_0_, dept0_.deptName as deptName2_0_0_ from Dept dept0_ where dept0_.id=? 17:27:12,994 INFO [stdout] (pool-2-thread-34) Hibernate: select dept0_.id as id1_0_0_, dept0_.deptName as deptName2_0_0_ from Dept dept0_ where dept0_.id=? 17:27:12,995 INFO [stdout] (pool-2-thread-34) emp: id=-1, firstName=Taro, lastName=Yamada 17:27:12,996 INFO [stdout] (pool-2-thread-34) emp: id=-2, firstName=Jiro, lastName=Suzuki 17:27:12,996 INFO [stdout] (pool-2-thread-34) emp: id=-3, firstName=Saburo, lastName=Tanaka
何故か普通に取ってきてますね。どうも無視されているような気が…。[2]にはエンティティグラフについても言及されているようなのだが未実装という事だろうか。あるいは、この機能はあくまでヒントであって実際にどうなるかはJPAプロバイダの実装によるとかそういう事なのか。
[1]を書いた人のサンプルを見るとEclipseLinkを使っているようなのでEclipseLinkなら期待通りの動作をするかもしれない。
[4]に起票されているが、どうもJPA仕様では強制されてないからバグではないと認識されているようだ。ううむ。
javax.persistence.loadgraph
Entity Graphを作る
Deptクラスにこんなアノテーションを付けます
@NamedEntityGraph( name = "loadEmployees", attributeNodes = { @NamedAttributeNode("employees")} )
orm.xmlにクエリを定義
<named-query name="findDeptsDistinct"> <query><![CDATA[ SELECT DISTINCT d FROM Dept AS d ]]></query> </named-query>
テストメソッド
@Test @Transactional @UsingDataSet({"datasets/relativeSelect/dept.yml", "datasets/relativeSelect/employees.yml"}) public void select() throws Exception { EntityGraph<?> entityGraph = em.getEntityGraph("loadEmployees"); dumpDeptList(em.createNamedQuery("findDeptsDistinct", Dept.class) .setHint("javax.persistence.loadgraph", entityGraph) .getResultList()); } protected void dumpEmployeeList(List<Employee> employees){ for(Employee emp : employees){ System.out.printf(" emp: id=%d, firstName=%s, lastName=%s\n", emp.getId(), emp.getFirstName(), emp.getLastName()); } } protected void dumpDeptList(List<Dept> list){ for(Dept dept : list){ System.out.printf("dept: id=%d, deptName=%s\n", dept.getId(), dept.getDeptName()); dumpEmployeeList(dept.getEmployees()); } }
テストメソッドを実行
21:03:35,540 INFO [stdout] (pool-2-thread-4) Hibernate: select distinct dept0_.id as id1_0_0_, employees1_.id as id1_1_1_, dept0_.deptName as deptName2_0_0_, employees1_.dept_id as dept_id4_1_1_, employees1_.firstName as firstNam2_1_1_, employees1_.lastName as lastName3_1_1_, employees1_.dept_id as dept_id4_0_0__, employees1_.id as id1_1_0__ from Dept dept0_ left outer join Employee employees1_ on dept0_.id=employees1_.dept_id 21:03:35,544 INFO [stdout] (pool-2-thread-4) dept: id=-1, deptName=Sales 21:03:35,545 INFO [stdout] (pool-2-thread-4) emp: id=-2, firstName=Jiro, lastName=Suzuki 21:03:35,545 INFO [stdout] (pool-2-thread-4) emp: id=-1, firstName=Taro, lastName=Yamada 21:03:35,545 INFO [stdout] (pool-2-thread-4) dept: id=-2, deptName=Legal 21:03:35,545 INFO [stdout] (pool-2-thread-4) emp: id=-3, firstName=Saburo, lastName=Tanaka
めでたくSELECT文が一回になりました。こっちは大丈夫そう。
その他
Entity Graphをorm.xmlで定義しようとすると例外が起きてしまい動作しません。「named-attribute-node.value is mandatory in XML overriding.」と言われます。書き方が悪いのか、あるいは他の原因があるのかは不明。またそのうち調べる。
参考文献
Tags: jpa