JPQLの集計関数に価値はあるか?
まったく価値がないわけではない。次のようにHAVING句で集計関数を利用してSELECT句でエンティティを返す場合は便利だ。
select d from Department d left outer join d.employees e group by d having count(e) > 5
Javaのコード上でもエンティティとして受け取ることができる。
String jpql = "select d from Department d left outer join d.employees e group by d having count(e) > 5";
List<Department> list = entityManager.createQuery(jpql).getResultList();
しかし、次のように集計関数をSELECT句に含める場合は便利とはいえない。Javaのコードには結果がObject配列として返されるからだ。
select d.name, count(e), avg(e.salary) from Department d left outer join d.employees e group by d having count(e) > 5
String jpql = "select d.departmentName, count(e), avg(e.salary) from Department d left outer join d.employees e group by d having count(e) > 5";
List<Object[]> list = entityManager.createQuery(jpql).getResultList();
Object配列ではなく、DTOとして受け取るための仕組みはある。その場合はコンストラクタ式を使う(適切なコンストラクタをもつDTOをあらかじめ作成しておく必要がある)。
String jpql = "select hoge.DepartmentDto(d.departmentName, count(e), avg(e.salary)) from Department d left outer join d.employees e group by d having count(e) > 5";
List<DepartmentDto> list = entityManager.createQuery(jpql).getResultList();
集計結果をどうせDTOにマッピングするならば、JPQLではなくSQLを使いその結果をDTOにマッピングしてもかわらない(Kuina-Daoにはこの機能がある)。エンティティを返さないのならばJPAの特徴である永続コンテキストや遅延ローディングは意味を成さないからだ。
集計のクエリはSQLのツールで試しながらつくりたいだろうからその意味でもSQLで作成が向いているといえる。それに、集計にはRDBMS固有の構文が役立つことが多いためSQLで記述したほうが効率的だ。
JPQLはエンティティを問い合わせる言語であって、集計を扱う言語ではないだろう。