SpringBoot JPQLの利用

## JPQL利用の流れ

#### 1.EntityManagerを用意
```
@PersistenceContext
EntityManager entityManager;
```
#### 2.クエリ作成
```
Query 変数 = EntityManager.createQuery(クエリ文);
```
#### 3.結果の取得
```
List 変数 = Query.getResultList();
```
#### 4.結果のインスタンスを取得
```
エンティティクラス 変数 = Query.getResult();
```
## JPQLにパラメータを設定する

前回のコードに追記
```PersonDAO.java
	public List<T> find(String fstr);
	//findメソッド、検索文字をfstr
```
```PersonDAOPersonImpl.java
	@SuppressWarnings("unchecked")
	@Override
	public List<Person> find(String fstr) {
	
		List<Person> list = null;
		String qstr = "from Person where id = :fstr";
		//JPQLの中に:〇〇としたらパラメータ用の変数として扱われる		
		
		Query query = entityManager.createQuery(qstr).setParameter("fstr",Long.parseLong(fstr));
		//setParameterで第一引数の変数に第二引数の値を設定
		//IDの値はLong型なのでLong.parseLong(fstr)に変換して検索フィールドの型に合わせる
		
		list = query.getResultList();
		//クエリを実行した結果をlistに入れる
		
		return list;
	}
```
serchメソッドを変更
```HelloController.java
    //ID検索メソッド
    @RequestMapping(value="/find",method=RequestMethod.POST)
    public ModelAndView search(HttpServletRequest request,ModelAndView mav) {
    	
        mav.setViewName("find");
        //HTMLファイル名
        
        String param = request.getParameter("find_str");
        //HTMLの検索バーfind_strの値をparamに取得
        
        if (param == "") {
        	mav = new ModelAndView("redirect:/find");
        	//空なら同じページを返す
        	
        }else{
        	mav.addObject("title","FindResult");
        	mav.addObject("msg","「" + param + "」の検索結果" );
        	
        	mav.addObject("value",param);
        	
        	List<Person>list =dao.find(param);
        	
        	mav.addObject("data",list);
        }
        return mav;
    }
```

## 複数のパラメータ指定もできる

```PersonDAOPersonImpl.java
	@SuppressWarnings("unchecked")
	@Override
	public List<Person> findByName(String name){
		return (List<Person>)entityManager.createQuery("from Person where name = '" + name + "'").getResultList();
		//nameは複数をかえすかもしれないのでList
		}

	@SuppressWarnings("unchecked")
	@Override
	public List<Person> find(String fstr) {
	
		List<Person> list = null;
		String qstr = "from Person where id = :fid or name like :fname or mail like :fmail";
		//JPQLの中に「:〇〇」としたらパラメータ用の変数として扱われる
		
		Long fid = 0L;
		//Long型だから0でなく0Lにしている
		
		try {
			fid = Long.parseLong(fstr);
			//fstrの値をLong型の数値に変換
			
		} catch (NumberFormatException e) {
			//できなかったときNumberFormatExceptionのインスタンスを生成
			
			e.printStackTrace();
			//例外の発生元とそこまでの経路の情報を詳細に示す
		}
		
		Query query = entityManager.createQuery(qstr)
				.setParameter("fid",fid)
				.setParameter("fname","%" + fstr + "%")
				.setParameter("fmail",fstr + "%");
		//setParameterで第一引数の変数に第二引数の値を設定
		//IDの値はLong型なのでLong.parseLong(fstr)にして検索フィールドの型に合わせる
		
		list = query.getResultList();
		//クエリを実行した結果をlistに入れる
		
		return list;
	}
```

#### ？による番号指定パラメータ
:〇〇のほかに、？1のような指定もできる
```PersonDAOPersonImpl.java
	@SuppressWarnings("unchecked")
	@Override
	public List<Person> find(String fstr) {
	
		List<Person> list = null;
		String qstr = "from Person where id = ?1 or name like ?2 or mail like ?3";
		//JPQLの中に「:〇〇」としたらパラメータ用の変数として扱われる
		
		Long fid = 0L;
		//Long型だから0でなく0Lにしている
		
		try {
			fid = Long.parseLong(fstr);
			//fstrの値をLong型の数値に変換
			
		} catch (NumberFormatException e) {
			//できなかったときNumberFormatExceptionのインスタンスを生成
			
			e.printStackTrace();
			//例外の発生元とそこまでの経路の情報を詳細に示す
		}
		
		Query query = entityManager.createQuery(qstr)
				.setParameter(1,fid)
				.setParameter(2,"%" + fstr + "%")
				.setParameter(3,fstr + "%");
		//setParameterで第一引数の変数に第二引数の値を設定
		//IDの値はLong型なのでLong.parseLong(fstr)にして検索フィールドの型に合わせる
		
		list = query.getResultList();
		//クエリを実行した結果をlistに入れる
		
		return list;
	}
```

## クエリアノテーション
DAOクラスの中にクエリが文字リテラルとして埋め込まれている状態はあまりよくない
クエリ文のみをクラスで管理できるもの
```構文
@NamedQuery(
	name = "任意のクエリ名",
	query = "保存したいクエリ文"
	)
```

クラスの前に追記する
```Person.java
import jakarta.persistence.NamedQuery;

@NamedQuery(
	name = "findWithName",
	query = "from Person where name like :fname"
	)
//クエリ文に名前を付けてアノテーションに保存できる
//from Person where name like :fnameというクエリにfindWithNameという名前をつける
```
findメソッド書き換え
```PersonDAOPersonImpl.java
	@SuppressWarnings("unchecked")
	@Override
	public List<Person> find(String fstr) {
	
		List<Person> list = null;
		
		Query query = entityManager
				.createNamedQuery("findWithName")
				//.createNamedQueryでさっきの保存したクエリを呼び出し
				
				.setParameter("fname","%" + fstr + "%");
		
		list = query.getResultList();
		//クエリを実行した結果をlistに入れる
		
		return list;
	}
```
## @Queryを用いたクエリ管理
リポジトリインターフェースのメソッド宣言前に記述する
リポジトリ：データベースアクセスを行うところ

アノテーションを記述したメソッドを呼び出す際に
指定されたクエリが使われるようになる
```構文
@Query(クエリのテキスト)
```
```PersonRepository.java
package com.example.sample1app.repositories;

import java.util.List;
import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import com.example.sample1app.Person;

@Repository
//このクラスがデータベースアクセスのためのものであることを示す

public interface PersonRepository extends JpaRepository<Person,Long> {
/*インターフェースがJpaRepository＜Person,Long＞クラスを継承しているということ
JpaRepository：新たなリポジトリを作るときのベースとなるもの
対象エンティティがPersonでプライマリーキーがLong型の値であることを示す*/
	
	@Query("SELECT d FROM Person d ORDER BY d.name")
	List<Person> findAllOrderByName();	
	
	public Optional<Person>findById(Long name);
	//findByIdによってIDを引数にPersonインスタンスを取り出す
	//Optionalクラス：nullかもしれないクラスのこと（import java.util.Optional;により使用可能）
	//nullだった場合もOptionalインスタンスが取り出される
}
```
example書き換え
```HelloController.java
    @RequestMapping("/")
    public ModelAndView example(@ModelAttribute("formModel") Person Person,ModelAndView mav) {
    	
        mav.setViewName("index");
        //HTMLファイル名
        
        mav.addObject("title","JPA使用練習");
        mav.addObject("msg","サンプルデータ");
        
       List <Person> list = repository.findAllOrderByName();
        //findAll():JpaRepositoryに用意されたメソッド
        //エンティティが自動的にすべて取り出される
        
        mav.addObject("data",list);
        
        return mav;
    }
```

クエリが実行されテーブルの並びが名前で昇順になっている
![スクリーンショット 2024-08-27 152928.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3860616/10506e03-f01a-e7a6-3a10-7fb0279c2fa0.png)

ORDER BY d.id DESCにしてみたらID降順で表示された！
![スクリーンショット 2024-08-27 153327.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3860616/6104eff8-c7a1-5957-5c97-c0b6d84475bd.png)


## クエリ内でのパラメータ指定

追記していく
## @NamedQueryのパラメータ指定
```Person.java
import jakarta.persistence.NamedQueries;

@NamedQueries({
    @NamedQuery(name = "findWithName",
    			query = "from Person where name like :fname"),
  //クエリ文に名前を付けてアノテーションに保存できる
  //from Person where name like :fnameというクエリにfindWithNameという名前をつける
    @NamedQuery(name="findByAge",
                query="from Person where age >= :min and age < :max"),
}) 
```
```PersonDAO.java
	public List<T> findByAge(int min,int max);
	//年齢がmin,maxの間の人を検索
```
```PersonDAOPersonImpl.java
	@SuppressWarnings("unchecked")
	@Override
	public List<Person> findByAge(int min,int max){
		return (List<Person>)entityManager
				.createNamedQuery("findByAge")
				.setParameter("min",min)
				.setParameter("max", max)
				.getResultList();
	}
```
```HelloController.java
    //ID検索メソッド
    @RequestMapping(value="/find",method=RequestMethod.POST)
    public ModelAndView search(HttpServletRequest request,ModelAndView mav, String[] params) {
    	
        mav.setViewName("find");
        //HTMLファイル名
        
        String param = request.getParameter("find_str");
        //HTMLの検索バーfind_strの値をparamに取得
        
        if (param == "") {
        	mav = new ModelAndView("redirect:/find");
        	//空なら同じページを返す
        	
        }else{
        	String [] paramss = param.split(",");
        	//文字列.split(区切り文字);文字列から配列を作る
        	
        	mav.addObject("title","FindResult");
        	mav.addObject("msg","「" + param + "」の検索結果" );
        	
        	mav.addObject("value",param);
        	
        	List<Person>list =dao.findByAge(
        			Integer.parseInt(paramss[0]),
        			Integer.parseInt(paramss[1]));
        	
        	mav.addObject("data",list);
        }
        return mav;
    }
```

## @Queryのパラメータ指定
クエリに:〇〇を用いてパラメータ指定

メソッドでは@Paramを用いて引数となるmax,minを用意
この値がクエリに渡され実行される

```PersonRepository.java
import org.springframework.data.repository.query.Param;

	@Query("from Person where age > :min and age < :max")
	public List<Person> findByAge (@Param("min") int min,@Param("max") int max);
	//パラメータ指定による検索
```
```HelooController.java
repository.findByAge
//findメソッドのdao.findByAgeを変更
```