追記
- Controllerのコードの一部が切れていたので修正
- Controllerのアノテーション周りの話が抜けていたので追記
内容
詳しく触れないこと
本編
雛形作成
- https://start.spring.io/ で作成
- Project:
Gradle Project
- Language:
Java
- Spring Boot:
2.1.8
- Project Metadaba:
- Group:
com.example
- Artifact:
todoapp
- Options: そのまま
- Dependencies:
Spring Web
Spring Data JPA
H2 Database
Lombok
- ダウンロードされたzipファイルをunzipしてIntelliJでopenしたところから開始
パッケージ構成
com.example.todoapp
直下に以下を並べる
controllers
repositories
models
services
とかもあるべきかもしれないけど今回は割愛
コード
models
package com.example.todoapp.models;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Data
@Entity
public class Task {
@Id
@GeneratedValue
private Long id;
private String summary;
private Boolean done = false;
public Task(){}
}
- 各アノテーションについて
@Data
- 各フィールドのsetterとgetterを定義してくれる
toString()
メソッドやhashCode()
メソッドを定義してくれる
@Entity
- エンティティであることを示す。
- このクラスのフィールドを持つテーブルがDBに作られる
@Id
@GeneratedValue
- 主キーの値の生成方法を指定する
strategy
やgenerator
を指定して挙動を変えられそう(試してない)
repositories
package com.example.todoapp.repositories;
import com.example.todoapp.models.Task;
import org.springframework.data.jpa.repository.JpaRepository;
public interface TaskRepository extends JpaRepository<Task, Long> {
}
JpaRepository
を継承したinterfaceを定義
JpaRepository
の祖先にいるCrudRepository
インターフェースがfindById
などのインターフェースを定義している
- 実装は
SimpleJpaRepository
なのかな?
JpaRepository
は対象のエンティティクラスとその主キーの型を指定する
- ここで指定した主キーの型は、
findById
やdeleteById
の引数の型として使われる
- 今回作成した
Task
エンティティの主キーはLong
型なのでここではLong
を指定
controllers
package com.example.todoapp.controllers;
import com.example.todoapp.models.Task;
import com.example.todoapp.repositories.TaskRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping(value = "/api")
public class ApiController {
@Autowired
TaskRepository taskrepo;
@GetMapping(value = "/todos")
public List<Task> tasklist(){
return taskrepo.findAll();
}
@GetMapping(value = "todo/{id}")
public Optional<Task> retrieve(@PathVariable Long id){
return taskrepo.findById(id);
}
@PostMapping(value = "/todo")
public ResponseEntity<Task> newTask(@RequestBody Task task){
Task result = taskrepo.save(task);
return new ResponseEntity<Task> (result, HttpStatus.CREATED);
}
@PutMapping(value = "/todo/{id}")
public Task update(@PathVariable Long id, @RequestBody Task task){
task.setId(id);
return taskrepo.save(task);
}
@DeleteMapping(value = "/todo/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long id){
taskrepo.deleteById(id);
}
}
- 各アノテーションについて
@RestController
- 各メソッドの戻り値がそのままレスポンスボディになる
@Controller
を使う場合、メソッドの戻り値はStringにしてテンプレートに埋め込む形で使うらしい
@RequestMapping
- このコントローラが受け付けるリクエストのパスやメソッドやクエリパラメータを指定できる
@Autowired
@Component
アノテーションをつけて登録されているBeanとの紐付けを行う
@Repository
や@Service
をつけたクラスも、内部では@Component
が付加されている
- ここでは
TaskRepository
インターフェースを満たすクラスが自動的にtaskrepo
に格納される
@GetMapping
, @PostMapping
, @PutMapping
, @DeleteMapping
@RequestMapping
のメソッドを固定したもの
@GetMapping
の場合、@RequestMapping(method=RequestMethod.GET)
と同等
@ResponseStatus
動作確認
新規登録
$ curl http://localhost:8080/api/todo -XPOST -H 'Content-Type: application/json' -d '{"summary": "my first task"}' | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 75 0 47 100 28 170 101 --:--:-- --:--:-- --:--:-- 170
{
"id": 1,
"summary": "my first task",
"done": false
}
リスト取得
$ curl http://localhost:8080/api/todos | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 49 0 49 0 0 285 0 --:--:-- --:--:-- --:--:-- 286
[
{
"id": 1,
"summary": "my first task",
"done": false
}
]
1件取得
$ curl http://localhost:8080/api/todo/1 | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 47 0 47 0 0 2045 0 --:--:-- --:--:-- --:--:-- 2136
{
"id": 1,
"summary": "my first task",
"done": false
}
更新
$ curl http://localhost:8080/api/todo/1 -XPUT -H 'Content-Type: application/json' -d '{"summary": "updated my task", "done": true}' | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 92 0 48 100 44 2632 2412 --:--:-- --:--:-- --:--:-- 2666
{
"id": 1,
"summary": "updated my task",
"done": true
}
削除(レスポンスボディがないのでStatusCodeで確認)
$ curl http://localhost:8080/api/todo/1 -XDELETE -v
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> DELETE /api/todo/1 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 204
< Date: Wed, 25 Sep 2019 15:29:37 GMT
<
* Connection #0 to host localhost left intact
まとめ
- 動かしたいもの自体はめっちゃ簡単に動かせた
- アノテーション種類多すぎて何使えばいいのかわからん
- とりあえず今回使ったものくらいは最低限おさえておきたい
- あとは何かするときに都度調べられればよさそう