JPAの微妙なところでEclipseLinkとHibernateを比べてみる @OneToMany(2)
ということで、前回に引き続き@OneToManyをEclipseLinkとHibernateで使い比べてみました。
※Derbyを使用しています。
@Entity public class Parent implements Serializable { @Id @GeneratedValue(strategy = GenerationType.TABLE) private Long id; private String name; @JoinColumn(name = "parent_id", nullable= true) // 前回からの差分 @OneToMany(fetch= FetchType.EAGER, cascade= CascadeType.ALL) private List<EChild> echildren; @JoinColumn(name = "parent_id", nullable= true) // 前回からの差分 @OneToMany(fetch= FetchType.LAZY, cascade= CascadeType.ALL) private List<LChild> lchildren; } @Entity public class EChild implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.TABLE) private Long id; private String name; // @ManyToOne //あってもなくても同じ // private Parent parent; } @Entity public class LChild implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.TABLE) private Long id; private String name; // @ManyToOne //あってもなくても同じ // private Parent parent; }
※アクセサは省略しています。
DBエンジニアの99.9%ぐらいが一対多のテーブル構成と言われて思い浮かべるものですね。
@JoinColumnを付けることで実現ができます。
その時、多側のカラム名に併せる形でカラム名を指定して上げる必要があります。
変に密結合していて嫌な感じですが、しょうがない。
さて、これに対し、以下の様なJPQLを発行します。
select p from Parent as p
今回はこれに加えて、以下のコマンドも発行してみました。
em.find(Parent.class, 1L);
EclipseLink2.4.0の場合
以下の様なSQLが発行されていました。
//JPQL SELECT ID, NAME FROM PARENT SELECT ID, NAME, PARENT_ID FROM ECHILD WHERE (PARENT_ID = ?) //Parentの数分繰り返し //findById SELECT ID, NAME FROM PARENT WHERE (ID = ?) SELECT ID, NAME FROM ECHILD WHERE (parent_id = ?)
Hibernate4.1.9.Finalの場合
以下の様なSQLが発行されていました。
//JPQL select parent0_.id as id0_, parent0_.name as name0_ from Parent parent0_ select echildren0_.parent_id as parent3_0_1_, echildren0_.id as id2_1_, echildren0_.id as id2_0_, echildren0_.name as name2_0_, echildren0_.parent_id as parent3_2_0_ from EChild echildren0_ where echildren0_.parent_id=? //Parentの数分繰り返し //findById select parent0_.id as id0_1_, parent0_.name as name0_1_, echildren1_.parent_id as parent3_0_3_, echildren1_.id as id2_3_, echildren1_.id as id2_0_, echildren1_.name as name2_0_ from Parent parent0_ left outer join EChild echildren1_ on parent0_.id=echildren1_.parent_id where parent0_.id=? Hibernate: select parent0_.id as id0_, parent0_.name as name0_ from Parent parent0_
何が意外だったかというと、Hibernateの場合、単独で取ってくる場合とJPQLで一覧で取ってくる場合に一欄の場合は無数にクエリーを発行してしまうような形となっていたこと。
EclipseLinkだと常にそうなってはいますけど。
L2Cacheを有効にした場合にキャッシュから取ってきやすくするようにこうなってるんだろうなぁと思いつつ。
EclipseLinkの方はどうでも良いレベルでなんとなくコーディングミスの匂いが。parent_idがselect句に入っていたり入っていなかったり、where句だけ小文字になっていたり。
まあ、なんだろう。うん。
そんなもんですかね。
これを見るだけだと、Lazyにすれば早くなるというのは場合によっては嘘になるけれども、大抵の場合はLazyでいい気がするなぁ。
Eagerを多用するのはEoDを加速させたい場合だけでしょうか。
その場合に性能問題で、EagerをLazyに変更したくなった場合、当初Eagerで作ったものをLazyに張り替えるというのは大変な場合があるので困ったものです。