以下の動画を参考に、ハンズオン形式でSpring BootでWebアプリを制作していきます。
今回はPart8。
Spring Data JPAを使ってデータベースにアクセスします。
準備として、MySQLでIDとパスワードが入ったテーブルを準備しています。
今回やることとしては、ログインフォームに入力された値とuser_info
テーブルの中のデータを照合して画面の表示を変えます。
Spring Data JPAの導入に関しては、以下のサイトが分かりやすいです。
まず、DBとの接続設定をしてあげましょう。
ほとんどテンプレ化しているようなので、調べれば出てきます。
今回はMySQLとの接続です。
接続設定はapplication.properties
とpom.xml
の二つにコードを追加する必要があります。
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
<!-- 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操作などは、このレポジトリを用意することで可能になります。
それぞれコードを見てみましょう。
エンティティ
@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
テーブルのデータがどのようなものかを分からせてやりました。
レポジトリ
@Repository
public interface UserInfoRepository extends JpaRepository<UserInfo,String>{
}
レポジトリでは、データ操作を行うためのコードを追加します。
といっても、今回はたった1行なので簡単です。
作るレポジトリは、JpaRepository
というインターフェースを拡張したものです。
JpaRepository
はデータ操作に必要なメソッドをもったインターフェースです。ダイヤモンド演算子の中でデータ操作したエンティティ名とその主キーの型を指定することで、事前にもっているメソッドの中身をそのエンティティ専用に書き換えてくれます。
なので今回は、UserInfo
というエンティティ(主キーの型はloginId
のString
)を操作するUserInfoRepository
インターフェースを作ったぜということです。
エンティティとレポジトリを作ったことで、user_info
テーブルの操作を行えるようになりました=!
今回行いたいのは、フォームに記入されたログインIDがDBに存在するのか、存在したときにはパスワードとの組み合わせが一致しているのかを確かめることです。
なので、まずはログインIDがuser_info
内に存在するかを確認するためのパッケージを作成します。
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
@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のインスタンス化を行う必要がありません。
主に追加した部分は以下です。
@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