Spring DATA JPAでDB接続

[前回](https://qiita.com/r_saiki/items/8d4e3107308f8bee627f)はSpring JDBCを使ったので、今回はSpring DATA JPAを使ってDBを操作します。

# 環境
**クライアント側**
OS: macOS Big Sur バージョン11.1
STS: Spring Tool Suite 4 Version: 4.9.0.RELEASE
**DBサーバー側**
OS: Amazon Linux
DB: mariaDB Ver 15.1
**DB環境構築の参考サイト**
・[AWSのEC2でmariaDBを使う方法](https://qiita.com/yuta-38/items/e6e5d9c57fbe8a20259b)
・[CentOS7にMariaDBをインストールして外部ホストから接続する方法](https://qiita.com/NoriIka/items/cccaf60eacee6fb6951b#4-%E5%A4%96%E9%83%A8%E3%83%9B%E3%82%B9%E3%83%88%E7%94%A8mariadb%E3%83%A6%E3%83%BC%E3%82%B6%E4%BD%9C%E6%88%90)
・[Macのhomebrewでmysqlクライアントのみインストールする](https://qiita.com/fault/items/b854d90ae6eef5aa3417)
**参考ライブラリ**
[mvnrepository](https://mvnrepository.com/)

# 前提
・STSでHelloWorldを出せる。
・DB環境が構築が完了してる（ローカルでも可）。
・lombokが使える状態。([lombokインストール](https://qiita.com/r_saiki/items/e650962af13961511331))

# Spring DATA JPAとは
JavaEE標準のORM(オブジェクト・リレーショナル・マッピング)によって、SQLを自動生成して実行してくれるライブラリです。


# 事前に作成したテーブル
事前にサンプル用ユーザーテーブルを作成しておく。

~~~sql
mysql> use sampledb;
Database changed
--テーブル作成時、user_idに自動連番の設定をしていない場合は以下を実行
mysql> alter table users change user_id user_id int(11) auto_increment;
mysql> show fields from users;
+-----------+-------------+------+-----+---------+----------------+
| Field     | Type        | Null | Key | Default | Extra          |
+-----------+-------------+------+-----+---------+----------------+
| user_id   | int(11)     | NO   | PRI | NULL    | auto_increment |
| password  | varchar(10) | YES  |     | NULL    |                |
| user_name | varchar(10) | YES  |     | NULL    |                |
| age       | int(11)     | YES  |     | NULL    |                |
| role      | varchar(10) | YES  |     | NULL    |                |
+-----------+-------------+------+-----+---------+----------------+
5 rows in set (0.18 sec)

mysql> select * from users;
+---------+----------+-----------+------+---------+
| user_id | password | user_name | age  | role    |
+---------+----------+-----------+------+---------+
|       1 | 1pass    | Benimaru  |   24 | admin   |
|       2 | 2pass    | Senku     |   17 | general |
|       3 | 3pass    | Garp      |   60 | general |
+---------+----------+-----------+------+---------+
3 rows in set (0.17 sec)
~~~

# application.properties
application.propertiesにDBの設定を入れます。

~~~ini:application.properties
spring.datasource.url=jdbc:mysql://(接続先IPアドレス):3306/sampledb
spring.datasource.username=testuser
spring.datasource.password=password
spring.datasource.driverClassName=org.mariadb.jdbc.Driver
~~~

# pom.xml
pom.xmlに使用するDBとjpaのライブラリを追加後、Mavenを更新する。

~~~xml:pom.xml追加部分
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mariadb.jdbc</groupId>
			<artifactId>mariadb-java-client</artifactId>
		</dependency>
~~~

# プロジェクト構成
[MyApp2プロジェクトソースはここ](https://github.com/RSrepository/SprinBoot_DB_Sample/tree/master/SpringDataJPA/MyApp2)
下のような感じで、パッケージを作成する。
**controller**・・・ここにユーザーリクエストを受け付けるRESTコントローラークラスを作成。
**entity**・・ユーザーテーブルと対応したデータクラスを作成。
**repository**・・・DB操作をするインターフェースを作成。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558865/c02b48e0-1c11-9217-10a2-f1afb98b1a49.png)

# エンティティ作成
usersテーブルと対応するデータクラスを作成する。

**jpa関連のアノテーション**
＠Entity・・・テーブルとマッピングしたいオブジェクトにつける。
＠Table・・・マッピングするテーブル名を指定。
＠JsonIgnoreProperties・・・これが無いとjsonでレスポンスを返す際にエラーが発生する。（[参考](https://qiita.com/niwasawa/items/9735d5dc4a4a71e84ccd#%E5%AF%BE%E7%AD%961-springjacksonserializationfail-on-empty-beansfalse)）
＠Id・・・主キーにつける。
＠GeneratedValue・・・DBで連番が設定されている時はこれを使う。
＠Column・・・列であることを示す。

~~~java:User.java
package com.r_saiki.springboot.entity;


import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import lombok.Data;

@Entity
@Table(name = "users")
@Data
@JsonIgnoreProperties({"hibernateLazyInitializer"})
public class User {
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
    private int userId; // ユーザーID
    @Column
    private String password; // パスワード
    @Column
    private String userName; // ユーザー名
    @Column
    private int age; // 年齢
    @Column
    private String role; // ロール
}

~~~

# リポジトリー作成
上記で作成したUserクラスと主キーのデータ型を、JpaRepositoryの型に指定し継承するだけで、基本的なDBアクセス機能が提供される。

~~~java:UserRepository.java
package com.r_saiki.springboot.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.r_saiki.springboot.entity.User;

public interface UserRepository extends JpaRepository<User, Integer> {

}
~~~

# コントローラー作成
@Autowiredにより、自動でUserRepositoryのオブジェクトが生成される。

**jpa関連メソッド**
findAll・・・全件取得。
getOne・・・引数で指定した主キーからデータを取得。
save・・・引数で渡したオブジェクトを保存する。存在する主キーを指定すると更新処理になる。
deleteById・・・引数で指定した主キーからデータを削除。

~~~java:UserController.java
package com.r_saiki.springboot.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.r_saiki.springboot.entity.User;
import com.r_saiki.springboot.repository.UserRepository;

@RestController
public class UserController {
	@Autowired
	UserRepository repository;

	// 全件取得用メソッド
	@GetMapping("/user/showUserList")
	@ResponseBody
	public List<User> showUserList() {
		return repository.findAll();
	}

	// 1件取得用メソッド
	@GetMapping("/user/{id:[0-9]+}")
	@ResponseBody
	public User showUser(@PathVariable("id") int userId) {
		return repository.getOne(userId);
	}
	
    // 1件追加用メソッド
    @PostMapping("/user/insertOne")
    public String insertOne(@RequestBody User user) {
    	repository.save(user);
        return "一件追加しました！";
    }
    
    // 1件更新メソッド
    @PostMapping("/user/updateOne")
    public String updateOne(@RequestBody User user) {
       	repository.save(user);
        return "一件更新しました！";
    }

    // 1件削除メソッド
    @PostMapping("/user/deleteOne/{id:.+}")
    public String deleteOne(@PathVariable("id") int userId) {
      	repository.deleteById(userId);
        return "一件削除しました！";
    }
}
~~~

# 実行結果
Spring Boot実行後、curlコマンドで実行結果を確認する。

## 全件取得

~~~bash:ターミナル
$ curl http://localhost:8080/user/showUserList
[{"userId":1,"password":"1pass","userName":"Benimaru","age":24,"role":"admin"},
{"userId":2,"password":"2pass","userName":"Senku","age":17,"role":"general"},
{"userId":3,"password":"3pass","userName":"Garp","age":60,"role":"general"}]
~~~

## 1件取得
userIdが２のデータが取得する。

~~~bash:ターミナル
$ curl http://localhost:8080/user/2
{"userId":2,"password":"2pass","userName":"Senku","age":17,"role":"general"}
~~~

## 1件追加
userIdは自動連番で取得されるため省略。

~~~bash:ターミナル
$ curl -X POST -H 'Content-Type: application/json' http://localhost:8080/user/insertOne -d '{"password":"InsertPass","userName":"Maki","age":17,"role":"general"}'
一件追加しました！
$ curl http://localhost:8080/user/showUserList
[{"userId":1,"password":"1pass","userName":"Benimaru","age":24,"role":"admin"},
{"userId":2,"password":"2pass","userName":"Senku","age":17,"role":"general"},
{"userId":3,"password":"3pass","userName":"Garp","age":60,"role":"general"},
{"userId":6,"password":"InsertPass","userName":"Maki","age":17,"role":"general"}]
~~~

## 1件更新
userIdが６のデータを更新。

~~~bash:ターミナル
$ curl -X POST -H 'Content-Type: application/json' ht0/user/updateOne -d '{"userId":6,"password":"UpdPass","userName":"Yuzuriha","age":88,"role":"general"}'
一件更新しました！
$ curl http://localhost:8080/user/showUserList
[{"userId":1,"password":"1pass","userName":"Benimaru","age":24,"role":"admin"},
{"userId":2,"password":"2pass","userName":"Senku","age":17,"role":"general"},
{"userId":3,"password":"3pass","userName":"Garp","age":60,"role":"general"},
{"userId":6,"password":"UpdPass","userName":"Yuzuriha","age":88,"role":"general"}]
~~~

## 1件削除
userIdが６のデータを削除。

~~~bash:ターミナル
$ curl -X POST http://localhost:8080/user/deleteOne/6
一件削除しました！
$ curl http://localhost:8080/user/showUserList
[{"userId":1,"password":"1pass","userName":"Benimaru","age":24,"role":"admin"},
{"userId":2,"password":"2pass","userName":"Senku","age":17,"role":"general"},
{"userId":3,"password":"3pass","userName":"Garp","age":60,"role":"general"}]
~~~

# 終わりに
Spring DATA JPAでのDB接続は桁違いに楽でした！
SQLをどうしても使わないといけない理由がない限り、JavaのDBアクセスはJPA一択ではと思うくらいです^^

# 参考
[@GeneratedValueを使って主キーを生成する方法](https://qiita.com/KevinFQ/items/a6d92ec7b32911e50ffe#generationtypesequence)
[SpringBoot + Spring JPAでデータベースに接続する](https://qiita.com/t-iguchi/items/685c0a1bb9b0e8ec68d2)
[Spring Boot ＋ JpaRepositoryを利用してRESTful のAPIを作成](http://hxn.blog.jp/archives/19402248.html)
[Spring Data JPA で hibernateLazyInitializer を Jackson が JSON シリアライズできなくてエラーになる](https://qiita.com/niwasawa/items/9735d5dc4a4a71e84ccd#%E5%AF%BE%E7%AD%961-springjacksonserializationfail-on-empty-beansfalse)