Criteria APIとHibernateは、Javaのデータ永続化(ORM)において異なるレイヤーの話であり、一言で言えば**「標準規格(ルール)」と「その実装(製品)」**の関係にあります。
しかし、歴史的な経緯から「Hibernate独自のCriteria」も存在するため、混乱を避けるために整理して解説します。
1. 根本的な役割の違い
まず、全体像を把握するための関係図は以下の通りです。
| 項目 | JPA Criteria API | Hibernate |
|---|---|---|
| 分類 | インターフェース(仕様) | フレームワーク(実装) |
| 立ち位置 | Jakarta Persistence (旧JPA) という標準規格の一部。 | JPAという規格を実際に動かすための「エンジン」。 |
| 役割 | 型安全なクエリを構築するための「書き方」を定義。 | データベースとの接続、SQL生成、キャッシュ管理など。 |
| 互換性 | 規格なので、EclipseLinkなど他の実装でも使える。 | Hibernate独自の機能を使うと他の実装への切り替えが困難。 |
2. クエリ構築における違い
「Criteria API」は、Javaコードを使って動的にクエリを組み立てるための仕組みです。一方で「Hibernate」には、このCriteria APIを動かす機能以外にも、HQL(Hibernate Query Language)などの書き方があります。
データの取得方法の比較
- Hibernate(HQL/JPQL):
- 文字列でクエリを書く。
"FROM User WHERE name = :name"- メリット: 直感的で読みやすい。
- デメリット: タイポ(打ち間違い)が実行時まで分からない。
- Criteria API:
- Javaのメソッドを組み合わせて書く。
cb.equal(user.get("name"), "Taro")- メリット: 型安全(Type-safe)。コンパイル時にエラーを検知できる。IDEの補完が効く。
- デメリット: コードが冗長になりやすく、複雑なクエリは読みにくい。
3. 「Hibernate Criteria」という古い存在
ここが最も混乱を招くポイントですが、実はCriteriaには2種類あります。
- Hibernate Criteria (Legacy):
- JPAが普及する前に、Hibernateが独自に提供していた古いAPIです。
- 現在は**非推奨(Deprecated)**となっており、使うべきではありません。
- JPA Criteria API:
- 現在の標準です。Hibernateも現在はこのJPA標準のCriteria APIを使うことを推奨しています。
どちらを意識すべきか?
現在、Javaでデータベース操作を行う場合、基本的には**「JPA Criteria API」というルールを使って、裏側で「Hibernate」というエンジンを動かしている**状態になります。
使い分けの基準
- 単純な検索や、決まりきったクエリ: 文字列で記述するJPQL(またはSpring Data JPAのメソッド名による生成)の方が、コードが短く可読性が高いため推奨されます。
- 検索条件が動的に変わる画面(検索フォームなど): if文などで文字列を連結するとバグの温床になるため、プログラムでクエリを組み立てるCriteria APIが真価を発揮します。
まとめると、Hibernateは「大きな箱(フレームワーク)」であり、Criteria APIはその中にある「高度なクエリを組み立てるためのツール」の一つだと捉えるのが正解です。
Java->criteriaAPI->hibernate->DB
その認識で、概ね間違いありません。 非常に的確なイメージです。
より正確に、データの流れと役割を分解すると以下のようになります。
データアクセスのフロー詳細
- Java (アプリケーション層) 「こういうデータが欲しい」というロジックを、Criteria API を使ってJavaコードとして記述します。
- Criteria API (JPA規格層) ここで「型安全なクエリの設計図」が作られます。しかし、これ自体は単なる「インターフェース(ルール)」なので、実体はありません。
- Hibernate (ORM実装層) Hibernateがその「設計図」を読み取り、実行環境(MySQLやPostgreSQLなど)に合わせた最適な SQL に翻訳します。
- JDBC (接続層 / ドライバ) Hibernateが生成したSQLを、Java標準のデータベース接続機能である JDBC を介してデータベースに送信します。
- DB (データベース層) SQLを実行し、結果を返します。
処理のイメージ例
「名前が 'M' で始まるユーザーを取得する」という処理を例にすると、役割分担は以下のようになります。
| ステップ | 担当 | 実際の動きのイメージ |
|---|---|---|
| 指示 | Java (Criteria) | cb.like(root.get("name"), "M%") と書く。 |
| 翻訳 | Hibernate | 「これは SELECT * FROM users WHERE name LIKE 'M%' だな」とSQLを作る。 |
| 運搬 | JDBC | そのSQLをデータベースへ運び、結果を持ち帰る。 |
| 変換 | Hibernate | DBから返ってきた表形式のデータを、再び Javaのインスタンス(Entity) に詰め直す。 |
補足:なぜ間にいろいろ挟むのか?
直接SQLを書かずにこのフローを通る最大のメリットは、「DBの種類が変わっても、Javaのコードを書き直さなくて済む」 ことにあります。
Hibernateが「方言(Dialect)」を吸収してくれるため、OracleからPostgreSQLに乗り換えても、Java側のCriteria APIのコードはそのまま動くのがこのアーキテクチャの強みです。
joinやサブクエリはJPQLがいい
基本的には**「可読性(読みやすさ)」を重視するなら、JPQLの方がおすすめ**です。
Joinやサブクエリが絡む複雑なクエリをCriteria APIで書こうとすると、コードが非常に長く、直感的に理解しにくいものになってしまうからです。
それぞれの使い分けのポイントを整理しました。
1. なぜ複雑なクエリはJPQLがいいのか?
JPQLの場合
SQLに近い感覚で書けるため、構造が一目でわかります。
Criteria APIの場合
型安全ではありますが、手続きが非常に多くなります。
これにサブクエリが加わると、さらにSubqueryオブジェクトの定義や相関条件の設定が必要になり、数行のSQLを表現するのに数十行のJavaコードを書くことも珍しくありません。
2. JPQL vs Criteria API 比較
| 特徴 | JPQL | Criteria API |
|---|---|---|
| 可読性 | 高い(SQLに近い) | 低い(冗長になりがち) |
| 複雑なJoin | 楽に書ける | 記述がかなり大変 |
| サブクエリ | 直感的 | 構造が複雑になる |
| 型の安全性 | 低い(文字列のため) | 高い(コンパイルエラーで気付く) |
| 動的な条件変更 | 苦手(文字列結合が必要) | 得意(if文でパーツを追加できる) |
3. 使い分けのガイドライン
今の開発現場でよく採用される「現実的な使い分け」は以下の通りです。
- JPQLを使うべきケース
- クエリの構造が固定されている(Join先やサブクエリの形が決まっている)。
- 後から見たときに「何をしているクエリか」をすぐ把握したい。
- Criteria APIを使うべきケース
- 「ユーザーが画面で選択した項目によって、JoinするテーブルやWHERE句の条件がバラバラに変わる」といった動的クエリ。
- テーブル名やカラム名の変更に強くしたい(リファクタリング耐性重視)。
結論
Joinやサブクエリが必要なほど複雑なロジックであれば、まずはJPQLで書いてみて、もし「検索条件が多すぎて文字列操作(if文での連結など)が辛い」と感じた場合にのみ、Criteria APIを検討するのが最も効率的です。