[Java]Spring + MySQL を用いたREST API作成メモ

* SpringとMySQLを利用したREST API作成メモ

## API例

* 以下のようなユーザー情報の登録、取得、一覧取得用APIを作成する。

* **ユーザー登録API(`POST /api/users`)**

  * リクエスト

    ```http
    POST /api/users HTTP/1.1
    Host: localhost:8080
    Content-Type: application/json
    Content-Length: 24
    
    {
        "name":"test4"
    }
    ```

  * レスポンス

    ```json
    {
        "id": 4,
        "name": "test4"
    }
    ```

    

* **ユーザー取得API(`GET /api/users/{user_id}`)**

  * リクエスト

    ```http
    GET /api/users/4 HTTP/1.1
    Host: localhost:8080
    Content-Type: application/json
    ```

  * レスポンス

    ```json
    {
        "id": 4,
        "name": "test4"
    }
    ```

    

* **ユーザー一覧取得API (`GET /api/users`)**

  * リクエスト

    ```http
    GET /api/users HTTP/1.1
    Host: localhost:8080
    Content-Type: application/json
    ```

  * レスポンス

    ```json
    [
        {
            "id": 1,
            "name": "test"
        },
        {
            "id": 2,
            "name": "test2"
        },
        {
            "id": 3,
            "name": "test3"
        },
        {
            "id": 4,
            "name": "test4"
        }
    ]
    ```

## 接続用MySQL DB構築

* [こちら](https://qiita.com/KWS_0901/items/4f6eb6a50e5f77430d0a)の手順でMySQLコンテナを立ち上げる。

* 以下のSQL`users`テーブルを作成する。

  ```sql
  CREATE TABLE `sample_db`.`users` (
    `id` BIGINT NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(45) NULL,
    PRIMARY KEY (`id`),
    UNIQUE INDEX `id_UNIQUE` (`id` ASC));
  ```



## API 実装

### Application層 Controller (`UserController`)

* リクエストを受け付け、ドメイン層`UserRepository`を呼び出す。

```java
package com.example.restservice.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.restservice.exception.NotFoundException;
import com.example.restservice.model.User;
import com.example.restservice.repository.UserRepository;

// Controller
@RestController
@RequestMapping("/api")
public class UserController {

	@Autowired
	UserRepository UserRepository;

	@GetMapping("/users")
	public List<User> getAllUsers() {
		return UserRepository.findAll();
	}

	@PostMapping("/users")
	public User createUser(@Validated @RequestBody User User) {
		return UserRepository.save(User);
	}

	@GetMapping("/users/{id}")
	public User getUserById(@PathVariable(value = "id") Integer UserId) {
		return UserRepository.findById(UserId)
				.orElseThrow(() -> new NotFoundException("User", "id", UserId));
	}

}
```



### Domain層 Respository Interface(`UserRepository`)

* データ操作を担う。

```java
package com.example.restservice.repository;

import java.util.List;

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

import com.example.restservice.model.User;

// Repository Interface
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}
```



###  Infrastructure層 Entity(`User`)

```java
package com.example.restservice.model;

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 lombok.Data;


@Entity
@Data
@Table(name = "users")
public class User {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;
    @Column(name = "name")
    private String name;

}
```



### 例外(`NotFoundException`)

* 取得API実行時にスローする例外

```java
package com.example.restservice.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException {
    private String resourceName;
    private String fieldName;
    private Object fieldValue;

    public NotFoundException( String resourceName, String fieldName, Object fieldValue) {
        super(String.format("%s not found with %s : '%s'", resourceName, fieldName, fieldValue));
        this.resourceName = resourceName;
        this.fieldName = fieldName;
        this.fieldValue = fieldValue;
    }

    public String getResourceName() {
        return resourceName;
    }

    public String getFieldName() {
        return fieldName;
    }

    public Object getFieldValue() {
        return fieldValue;
    }
}
```



### DB接続設定

* `src/main/resources/application.properties`に以下を記述する。

  ```properties
  spring.datasource.url=jdbc:mysql://localhost:3306/sample_db
  spring.datasource.username=mysqluser
  spring.datasource.password=mysqlpass
  spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  ```

  

## 動作確認

* 上記の[API例](#API例)のリクエストを投げ、レスポンスを受信できることを確認する。



## 参考情報

* [Spring Bootで実装するWebAPIのアーキテクチャを考える](https://qiita.com/YutaKase6/items/7d88fa23f81366905270#infrastructure%E5%B1%A4)
* [callicoder](https://github.com/callicoder)/[spring-boot-mysql-rest-api-tutorial](https://github.com/callicoder/spring-boot-mysql-rest-api-tutorial)