Spring BootでDAOからデータベースにアクセスしてみた-その2（クライテリア）

# 背景・目的
過去に、Spring Bootについて下記の記事にまとめました。今回は、Criteria APIによるデータベースアクセスを試します。

- [Spring BootでDAOからデータベースにアクセスしてみた-その1（DAOとアノテーション）](https://qiita.com/zumax/items/4dd856cd108d23a15188)
- [Spring Bootでバリデーションを試してみた](https://qiita.com/zumax/items/f09bacf18f3bb96d1331)
- [Spring Bootでデータを保存する-その2](https://qiita.com/zumax/items/06d0465218478840a949)
- [Spring Bootでデータを保存する-その1](https://qiita.com/zumax/items/2e387254c736659bf20c)
- [Spring Bootでデータベースへのアクセスを試してみた](https://qiita.com/zumax/items/7487b86f56ea12c31d44)
- [Sprint Bootの環境を構築したときのメモ](https://qiita.com/zumax/items/8effa97f338dd0224b22)

# まとめ
下記に特徴をまとめます
|特徴|説明|
|:--|:--|
|Criteria API|テキスト文字列を使用せずに、Javaエンティティ・クラスとフィールドによって強く型付けされた問合せを作成できる|
|Criteria クラス|下記の3つのクラスがある<BR>・CriteriaBuilder<BR>・CriteriaQuery<BR>・Root|


# 概要
前回、[Spring BootでDAOからデータベースにアクセスしてみた-その1（DAOとアノテーション）](https://qiita.com/zumax/items/4dd856cd108d23a15188)では、DAOやリポジトリを介して、JPQLでクエリを実行していました。

データベースへのクエリのうち、JPQLはSQLに近いものでしたが、JPAにはCriteria APIという機能もあります。

## Criteria API
[JPAの新機能： Criteria API](https://www.oracle.com/webfolder/technetwork/jp/javamagazine/Java-MA16-JPA-Criteria.pdf
)を基に整理します。

- Criteria APIでは、テキスト文字列を使用せずに、Javaエンティティ・クラスとフィールドによって強く型付けされた問合せを作成できる
- Criteria APIは、JPQLと同じくエンティティ・クラスの抽象スキーマに基づく
- Criteria APIを使うと、オブジェクトによって強く型付けされたデータベース問合せをJavaコードだけで作成できる
    - 入力したJPQLやSQLによる問合せにエラーがないかを心配する必要がなくなるため、エラーの可能性を減らすことができる
    - この問合せには完全な移植性があるというメリットがある。
- Criteria APIには、型保証された問合せの生成をサポートするさまざまなMetamodel APIも含まれる。

### クラス
3つのクラスがある

- CriteriaBuilderクラス
    - クエリ生成を管理する
- CriteriaQueryクラス
    - クエリ実行
- Rootクラス
    - 検索されるエンティティのルート
    - 必要なエンティティを絞る


# 実践
## 前提
[Spring BootでDAOからデータベースにアクセスしてみた-その1（DAOとアノテーション）](https://qiita.com/zumax/items/4dd856cd108d23a15188)で構築した環境を基にします。

## Criteria APIによる検索
getAllメソッドを修正します

### DAOの修正
1. PersonDAOPersonImplクラスのgetAllメソッドを、下記のように書き換えます
    ```java
        @Override
        public List<Person> getAll() {
            List<Person> list = null;
    
            CriteriaBuilder builder = entityManager.getCriteriaBuilder();
            CriteriaQuery<Person> query = builder.createQuery(Person.class);
            Root<Person> root = query.from(Person.class);
            query.select(root);
            list = (List<Person>)entityManager.createQuery(query).getResultList();
    
            return list;
        }
    ```

### コントローラーを修正
1. WebControllerクラスのindexメソッドを、下記の通り書き換えます
    ```java
      @RequestMapping("/")
      public ModelAndView index(
        @ModelAttribute("formModel") Person person,
        ModelAndView mav) {
    
        mav.setViewName("index");
        mav.addObject("title","Test page");
        mav.addObject("message","This is JPA sample content.");
        // List<Person> list = repository.findAll();
        // List<Person> list = repository.findAllOrderByName();
    
        List<Person> list = dao.getAll();
    
        mav.addObject("people", list);
    
        return mav;
      }
    ```

### 動作確認
1. ビルドしSpring Bootを再実行します
    ```bash
    $ mvn package
    $ java -jar target/rest-db-0.0.1-SNAPSHOT.jar
    ```
1. localhost:8080にアクセスします。表示されました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/eb4abc7b-f763-2c7f-4160-ad89202b276b.png)

## Criteria APIによる名前の検索
findメソッドを修正します。

### DAOの修正
1. PersonDAOPersonImplクラスの find メソッドを、下記のように書き換えます
    ```java
        @Override
        public List<Person> find(String fstr){
            CriteriaBuilder builder = entityManager.getCriteriaBuilder();
            CriteriaQuery<Person> query = builder.createQuery(Person.class);
            Root<Person> root = query.from(Person.class);
            
            query.select(root).where(builder.equal(root.get("firstName"), fstr));
            List<Person> list = null;
    
            list = (List<Person>)entityManager.createQuery(query).getResultList();
    
            return list;
        }
    ```
    

### コントローラーを修正
1. WebControllerクラスの search メソッドを、下記の通り書き換えます
    ```java
      @RequestMapping(value = "/find", method=RequestMethod.POST)
      public ModelAndView search(HttpServletRequest request, ModelAndView mav){
        mav.setViewName("find");
        String param = request.getParameter("find_str");
        if (param == ""){ 
          mav = new ModelAndView("redirect:/find");
        } else {
          mav.addObject("title","Find result");
          mav.addObject("message","Result for " + param);
          mav.addObject("value", param);
          List<Person> list = dao.find(param);
    
          mav.addObject("people", list);
        }
        return mav; // Add this return statement
      }
    ```

### 動作確認
1. ビルドしSpring Bootを再実行します
    ```bash
    $ mvn package
    $ java -jar target/rest-db-0.0.1-SNAPSHOT.jar
    ```
1. localhost:8080/findにアクセスします。表示されました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/c6acbf47-7530-10d7-ff30-1b42a6011cb3.png)
1. firstNameに「Ichiro」を入力し、Clicksします。想定通り絞り込めました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/72fd45bb-395d-f632-be21-ba30e0275910.png)

# 考察
今回は、Criteria APIを使ったデータアクセスを試しました。
確かに、JPQLにあるようなSQLライクなコードは書かないので、移植性は高いと思いました。

# 参考

[Spring Boot 3 プログラミング入門](https://www.amazon.co.jp/Spring-Boot-3-%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E5%85%A5%E9%96%80-%E6%8E%8C%E7%94%B0%E6%B4%A5%E8%80%B6%E4%B9%83/dp/4798069167)

https://dev.classmethod.jp/articles/java-spring-boot-criteria-search/

https://www.oracle.com/webfolder/technetwork/jp/javamagazine/Java-MA16-JPA-Criteria.pdf