Spring Data JPAで、where句を可変にする

# 目的
- 一覧検索画面に検索結果を表示する
- 検索条件は、入力されている項目は利用し、見入力の場合は無視してwhere句絞り込みを行う

# 前提
- FW : spring-boot

# 手順
## repositoryの変更
- 検索対象であるRepositoryインターフェースで、***JpaSpecificationExecutor*** を継承するように追加する

```java:testRepository
@Repository
public interface TestRepository extends JpaRepository<TestEntity, Integer>, JpaSpecificationExecutor<TestEntity> {

}
```

- （補）リポジトリは、Entityをリストで管理するための仕組み

## Specificationsクラスの用意
- 検索条件を作成する
- このメソッドが、一つの検索条件としてあとで使われる
- Specificationクラスは、where句で利用する検索条件の役割を果たす

```java:testSpecifications
/**
	 * 案件区分が検索条件に存在する場合、where条件に加える
	 * @param anken_kbn
	 * @return
	 */
	public Specification<TestEntity> ankenKbnContains(String anken_kbn) {
		if (Util.stringIsEmpty(anken_kbn))
			return null;
		else
			return new Specification<TestEntity>() {
				@Override
				public Predicate toPredicate(Root<TestEntity> root, CriteriaQuery<?> query,
						CriteriaBuilder cb) {
					return cb.like(root.get("anken_kbn"), "%" + anken_kbn + "%");
				}
			};
	}
```
- この部分は、anken_kbnという項目での部分一致検索を作成している
- andやorでチェーンして使う条件の分だけメソッドを用意する
- ここでは他にも
 - Anken_name
 - Rease_campany_name    
で検索できるメソッドを用意している
- Specificationクラスで用意したメソッドが***nullを返した場合は、無視***されて検索が行われる、という動きをするため、
- **渡されたパラメータanken_kbn** を、Util.stringIsEmpty(anken_kbn)で、anken_kbuが **空文字** である場合は **null** を返している
- 値が**空でなかった** 場合は、 **検索条件** を作成してオブジェクトを返している。

## DaoでSpecificationを利用し、検索を行う
- 検索条件をメソッドチェーンで設定することができる
- この時、先ほどのリポジトリが必要となるため、それがインジェクションできるところでこのメソッドを定義するか、または今回の場合は、コントローラにインジェクションされたリポジトリオブジェクトを第一引数にうけとって、それを利用している
- 第二引数のFormは画面上から受け渡された値を保持している。
- public List<TestEntity> findBySearchItem(TestRepository testRepository,TestForm form
 
```java:testDao
@Override
	public List<TestEntity> findBySearchItem(TestRepository testRepository,TestForm form) {
		TestSpecifications spec = new TestSpecifications();
		List<TestEntity> result = TestRepository.findAll(Specifications.where(spec.ankenKbnContains(form.getAnken_kbn()))
				.and(spec.ankenNameContains(form.getAnken_name()))
				.and(spec.reaseCampanyNameContains(form.getRease_campany_name()))
				);
		System.out.println("検索結果");
		System.out.println(result);

		return result;
	}
```

- repositoryインターフェースが用意しているfindAllメソッドのパラメータに検索条件となるオブジェクトを渡す
 
```
Specifications.where(spec.ankenKbnContains(form.getAnken_kbn()))
				.and(spec.ankenNameContains(form.getAnken_name()))
				.and(spec.reaseCampanyNameContains(form.getRease_campany_name()))
```

- 以上のように、and条件などで検索条件をチェーンにする
- specはspecificationsのオブジェクトで、そのクラスに作った先ほどのankenKbnContainsメソッドを使っている
- その次のformは、画面上から受け取った値を出している。それが検索条件の文字列となる

##　Controlerから利用する
- testListFormに、画面へ返すリストとして検索結果を入れる
- testDaoはインジェクションされている
- testRepositoryはインジェクションされている
- Daoを利用する。Daoで先ほどrepositoryクラスのインスタンスを必要としていたので、testRepositoryを渡している

```java:testControleller
	@RequestMapping("/search")
	public ModelAndView search(@ModelAttribute("testListForm") TestListForm contractListForm,
			ModelAndView mav) {

		mav.setViewName("index");
		Iterable<TestEntity> testEntityList = testDao.findBySearchItem(testRepository, testListForm);
		
		System.out.println("検索結果");
		System.out.println(tstEntityList);
		
		mav.addObject("contractList", hdrEntityList);
		mav.addObject("contractListF", new TestListForm());
		return mav;

	}
```

## 以上
- 手探りで可変where句を作成した
- 書き方としてこれがいい書き方なのか自身がない、もっと優れた書き方があるのかもしれない