Spring Boot学習③～Spring Data JPAを使ってデータベースにアクセス～


以下の動画を参考に、ハンズオン形式でSpring BootでWebアプリを制作していきます。

https://www.youtube.com/watch?v=F7AuKgTwWTg&list=PLD6WyeaFzSSbS0q1unoPpBpnBNyzPWo3j&index=9

今回はPart8。
Spring Data JPAを使ってデータベースにアクセスします。

準備として、MySQLでIDとパスワードが入ったテーブルを準備しています。

![スクリーンショット 2023-12-28 162817.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3114345/f925070c-3c1d-abf0-c75a-431b213ef8b6.png)

今回やることとしては、ログインフォームに入力された値と`user_info`テーブルの中のデータを照合して画面の表示を変えます。

Spring Data JPAの導入に関しては、以下のサイトが分かりやすいです。

https://www.alpha.co.jp/blog/202209_01

まず、DBとの接続設定をしてあげましょう。
ほとんどテンプレ化しているようなので、調べれば出てきます。
今回はMySQLとの接続です。
接続設定は`application.properties`と`pom.xml`の二つにコードを追加する必要があります。

# application.properties

``` application.properties
spring.datasource.url=jdbc:mysql://localhost/spring_dev
spring.datasource.username=devUser01
spring.datasource.password=mysql
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
```

上から順に
`spring_dev`というスキーマを使うよ
ユーザー名は`devUser01`だよ
パスワードは`mysql`だよ
MySQLと接続するよ
って感じですね。

# pom.xml

``` pom.xml
<!-- Spring Data JPA -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

```

`spring-boot-starter-data-jpa`はデータアクセス関連のAPIです。
この依存性注入を行うことで、次に紹介するエンティティやレポジトリを利用することができます。



DBの操作を行うには、以下の二つのパッケージが必要です。
①エンティティ
②レポジトリ

エンティティとは、DBのテーブルに対応したパッケージです。
MySQLの特定のスキーマと接続しただけでは、その中にあるテーブルを利用することはできません。
テーブル一つにつきエンティティを一つ作ってあげて、MySQLの中にはこんなテーブルがあるんだよーと紹介してあげる必要があります。

一方でレポジトリとは、データベースとの対話をしてくれる存在です。
データベースへのアクセスやCRUD操作などは、このレポジトリを用意することで可能になります。

それぞれコードを見てみましょう。

# エンティティ
``` UserInfo.java
@Entity
@Table(name = "user_info")
@data_mom 
public class UserInfo {
	@Id
	@Column(name = "login_id")
	private String loginId;
	
	private String password;

}
```

`@Entity`とアノテーションすることで、その下のクラスがエンティティとして定義されます。
`@Table(name = "user_info")`では、`user_info`というテーブルを使いますよーと宣言。
`@Data`は例のごとく、便利メソッドの追加です。

クラス内の`@Id`はそのテーブルの主キーはこれだよーというお知らせです。
`@Column`は、変数名とカラム名が異なる時に修正するための手段です。変数名とカラム名が一致している場合（今回の`password`のように）は何も書かなくても自動で理解してくれますが、少しでも異なるとわかんないよー状態になります。
`login_id`というカラムを`loginId`というJavaの命名規則に従って宣言するために必要なアノテーションです。

以上で、`spring_dev`スキーマ内の`user_info`テーブルのデータがどのようなものかを分からせてやりました。

# レポジトリ
```UserInfoRepository.java
@Repository
public interface UserInfoRepository extends JpaRepository<UserInfo,String>{

}

```

レポジトリでは、データ操作を行うためのコードを追加します。
といっても、今回はたった1行なので簡単です。
作るレポジトリは、`JpaRepository`というインターフェースを拡張したものです。

`JpaRepository`はデータ操作に必要なメソッドをもったインターフェースです。ダイヤモンド演算子の中でデータ操作したエンティティ名とその主キーの型を指定することで、事前にもっているメソッドの中身をそのエンティティ専用に書き換えてくれます。

なので今回は、`UserInfo`というエンティティ（主キーの型は`loginId`の`String`）を操作する`UserInfoRepository`インターフェースを作ったぜということです。


エンティティとレポジトリを作ったことで、`user_info`テーブルの操作を行えるようになりました＝！

今回行いたいのは、フォームに記入されたログインIDがDBに存在するのか、存在したときにはパスワードとの組み合わせが一致しているのかを確かめることです。

なので、まずはログインIDが`user_info`内に存在するかを確認するためのパッケージを作成します。

# LoginService.java

``` LoginService.java
@Service
@RequiredArgsConstructor
public class LoginService {
	
	private final UserInfoRepository repository;
	
	public Optional<UserInfo> searchUserById(String loginId){
		return repository.findById(loginId);
	}

}
```

まず、サービスクラスには`@Service`というアノテーションを付けます。
これによって、このクラスがレポジトリとコントローラーを繋ぐサービスクラスであることがSpringに伝わります。（伝わることの効用は少し難しいので省略します）

次に、`private final UserInfoRepository repository;`で先ほど作ったレポジトリのフィールドを宣言します。
この時に、`@RequiredArgsConstructor`とアノテーションをつけ、`final`修飾子をつけることで、`LoginService`クラスのコンストラクタが生成される際に、引数に`UserInfoRepository repository`が追加されます。

最後に、ID検索のメソッドを追加します。

戻り値の型が`Optional<UserInfo>`なのは、検索したIDが存在しなかった場合に`null`が返ってくる可能性があるからです。
`String型`などで`null`が返ってくるとヌルポインターエラーになる可能性がありますが、`Optional<UserInfo>`にしておけば`Optional.empty()`と値が存在しないことを正確に教えてくれるため、エラーなどにも対応しやすくなります。

`searchUserById`とメソッド名を名づけ、引数には画面で入力されるIDを受け取ります。

メソッドの中身の部分は、‘repository.findById(loginId);`で入力された値が存在するかを確かめます。
`findById`はSpring Data JPAで用意されているメソッドで、引数の値が、参照しているテーブルに存在するかを確かめます。
存在する場合は`true`、しない場合は`Optional.empty()`を返します。

これで準備は整いました。
IDの判別をログインコントローラに追加しましょう。

# LoginController
```LoginController.java
@Controller
@RequiredArgsConstructor
public class LoginController {
	
	private final LoginService service;

	@GetMapping("/login")
	public String view(Model model, LoginForm form) {
		
		return "login"; //login.htmlをする
	}
	
	@PostMapping("/login")
	public String login(Model model, LoginForm form) {
		var userInfo = service.searchUserById(form.getLoginId());
		var isCorrectUserAuth = userInfo.isPresent() 
				&& form.getPassword().equals(userInfo.get().getPassword());
		if(isCorrectUserAuth) {
			return "redirect:/menu";
		}else {
			model.addAttribute("errorMsg","IDとPASSの組み合わせが間違っています。");
			return "login";
		}
	}
}

```

コントローラクラスにも`@RequiredArgsConstructor`をつけて、コンストラクタの引数に`LoginService service`を追加します。
こうすることで、コンストラクタの再定義をおこなったり、LoginController内でLoginServiceのインスタンス化を行う必要がありません。

主に追加した部分は以下です。

```LoginController.java
	
	@PostMapping("/login")
	public String login(Model model, LoginForm form) {
		var userInfo = service.searchUserById(form.getLoginId());
		var isCorrectUserAuth = userInfo.isPresent() 
				&& form.getPassword().equals(userInfo.get().getPassword());
		if(isCorrectUserAuth) {
			return "redirect:/menu";
		}else {
			model.addAttribute("errorMsg","IDとPASSの組み合わせが間違っています。");
			return "login";
		}
	}
}

```

`var userInfo = service.searchUserById(form.getLoginId());`
で、ログインフォームに記入されたLoginIdのエンティティを`userInfo`に代入しています。

`var isCorrectUserAuth = userInfo.isPresent() 
				&& form.getPassword().equals(userInfo.get().getPassword());`
では、上の`userInfo`が存在しているかどうか、そして、存在している場合は`&&`以下に続き、フォームに入力されたパスワードとエンティティのパスワードが一致しているかを確かめます。

どちらも`true`なら`isCorrectUserAuth`は`true`となります。

`if`文以下は前回と変わりません。
完全に一致している場合はメニュー画面にリダイレクトし、それ以外はログイン画面に文言が追加されて表示されます。

次→https://qiita.com/19960417akiho/items/4f89cf51b69125c4cdc9