我們上一篇完成與資料庫的連接,接著來我們要實現實現概念篇中設計的功能。
在src/main/java/com/restufulapi/restfulapi/Entity下,新增Todo.java。
如果Entity不存在就新增,之後如果有類似的情況也是用同樣的方式處理。
我們之前規劃過todo entity的格式,現在我們按照規劃的內容做。
告知Spring這是一個entity
@Entity
public class Todo {
表示這個是id也是主鍵
@Id
會在資料庫的欄位屬性添加AUTO_INCREMENT,id會從1開始編號,之後2 3 4遞增。
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private boolean completed;
public Todo() {
}
public Todo(Long id, String title, boolean completed) {
this.id = id;
this.title = title;
this.completed = completed;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public boolean isCompleted() {
return completed;
}
public void setCompleted(boolean completed) {
this.completed = completed;
}
}
在private boolean completed這行以下的部分是使用IDEA內建的功能產生的,不需要自己手打。
教大家如何使用
先按下Select None,產生空的public todo。
我們再重複同樣的步驟到Constructor那一步,這次選取id、title、completed後按下OK。
然後我們來設定getter和setter
啟動專案,我們可以在console看到Hibernate開頭的內容,後面就是Spring Data JPA自動產生的SQL statement。
在src/main/java/com/restufulapi/restfulapi/Repository下,新增TodoRepository.java選擇Interface,它的作用是負責從資料庫中取得資料,或是將修改後的結果傳回資料庫儲存。
我們直接繼承JpaRepository來實現對資料庫進行增刪查改以及將結果分頁和排序,節省開發的時間。
JpaRepository<>中的內容是有限制的,先放entity,再放id的資料型態。
Todo是我們的entity,Long是id的資料型態。
public interface TodoRepository extends JpaRepository<Todo, Long> {
}
如果之前看過其他的教學,可能會認為要分成Service和ServiceImpl,這麽做是為了重複使用Service的架構,他們認為會有相同的命名但是內部的操作不同的情況發生。
但是我認為沒有這個必要,大部分的service的操作都是固定的,不會重複使用,額外分成Service和ServiceImpl就是浪費時間的做法。
在src/main/java/com/restufulapi/restfulapi/Service下,新增TodoService.java,負責實作controller用到的功能。
有人會疑惑為什麼不直接寫在controller裡面,這個小專案確實可以省略service層,但是大專案會有很多的controller要使用某一個service。
我們如果省略service層,就代表要呼叫其他的controller,那麽就要確認controller的輸入和輸出,找出適合的輸入內容,截取想要的輸出,感覺繞了不少路。
如果有service層,我們就能很直觀的確認輸入和輸出,不會摻雜不必要的資訊。
宣告這是service層
@Service
public class TodoService {
我們要將資料取出或儲存需要todoRepository的協助
採用Dependency Injection的寫法,詳細的說明我們不會談論,可以自行尋找IoC和DI的教學,主要目的是減少todoRepository的修改對todoService的影響。
就算資料庫從H2換成其他資料庫,我們也不需要重寫TodoService.java的內容
private final TodoRepository todoRepository;
public TodoService(TodoRepository todoRepository){
this.todoRepository = todoRepository;
}
將從controller取得的todo,儲存到資料庫中,把資料庫儲存後的結果回傳給controller
public Todo addTodo(Todo todo){
return todoRepository.save(todo);
}
取得資料庫中所有的todo,回傳給controller
public List<Todo> getAllTodos(){
return todoRepository.findAll();
}
根據提供的id編號在資料庫中尋找todo,如果存在就回傳,不存在的話會跳出例外,提醒沒有找到這個id的todo
public Todo getTodoById(Long id){
return findOrElseThrow(id);
}
根據提供的id編號在資料庫中刪除todo,如果資料庫中沒有這個id的todo,就會跳出例外
public void deleteTodo(Long id){
Todo todo = findOrElseThrow(id);
todoRepository.delete(todo);
}
根據提供的id編號修改對應的todo,修改後存入資料庫,當title留空時,會保留原本的title,不會清空,如果資料庫中沒有這個id的todo,就會跳出例外
public Todo updateTodo(Todo todo){
Todo oldTodo = findOrElseThrow(todo.getId());
oldTodo.setTitle(todo.getTitle() == null ? oldTodo.getTitle() : todo.getTitle());
oldTodo.setCompleted(todo.isCompleted());
return todoRepository.save(oldTodo);
}
將指定id的todo設定為完成,如果資料庫中沒有這個id的todo,就會跳出例外
public Todo setCompleteTodo(Long id){
Todo oldTodo = findOrElseThrow(id);
oldTodo.setCompleted(Boolean.TRUE);
return todoRepository.save(oldTodo);
}
將指定id的todo設定為未完成,如果資料庫中沒有這個id的todo,就會跳出例外
public Todo setUncompleteTodo(Long id){
Todo oldTodo = findOrElseThrow(id);
oldTodo.setCompleted(Boolean.FALSE);
return todoRepository.save(oldTodo);
}
在資料庫中尋找todo,如果存在就回傳,不存在的話會跳出例外
public Todo findOrElseThrow(Long id){
return todoRepository.findById(id).orElseThrow(() -> new RuntimeException("Todo not found with id: " + id));
}
}
在src/main/java/com/restufulapi/restfulapi/Controller下,新增TodoController.java,負責和外界溝通以及回傳經過處理的結果。
按照我們之前規劃的專案的功能,編寫TodoController.java
表示是RESTful API的controller
@RestController
TodoController中的所有網址的開頭都是/api/todo
@RequestMapping("/api/todo")
public class TodoController {
TodoService已經說過了,同樣的理由
private final TodoService todoService;
public TodoController(TodoService todoService) {
this.todoService = todoService;
}
當Http Method是POST時才會觸發,其餘Http Method不會動作。
新增todo,並將新增的todo資訊回傳。
@RequestBody用來接收傳入的JSON資料
@PostMapping("/")
public Todo addTodo(@RequestBody Todo todo) {
return todoService.addTodo(todo);
}
當Http Method是GET時才會觸發
將資料庫中所有的todo回傳
@GetMapping("/all")
public List<Todo> getAllTodos() {
return todoService.getAllTodos();
}
當Http Method是GET時才會觸發
將指定的todo內容回傳
@PathVariable用來取得網址中內含的id資訊,("id")對應的是{id},要符合才能正確取得id資訊
@GetMapping("/{id}")
public Todo getTodo(@PathVariable("id") Long id) {
return todoService.getTodoById(id);
}
當Http Method是DELETE時才會觸發
將指定的todo刪除
@DeleteMapping("/{id}")
public String deleteTodo(@PathVariable("id") Long id) {
todoService.deleteTodo(id);
return "Todo deleted successfully";
}
當Http Method是PUT時才會觸發
修改指定的todo內容
@PutMapping("/{id}")
public Todo updateTodo(@PathVariable("id") Long id, @RequestBody Todo todo) {
todo.setId(id);
return todoService.updateTodo(todo);
}
當Http Method是PATCH時才會觸發
把指定的todo設定為完成
@PatchMapping("/{id}/completed")
public Todo completeTodo(@PathVariable("id") Long id) {
return todoService.setCompleteTodo(id);
}
當Http Method是PATCH時才會觸發
把指定的todo設定為未完成
@PatchMapping("/{id}/uncompleted")
public Todo incompleteTodo(@PathVariable("id") Long id) {
return todoService.setUncompleteTodo(id);
}
}
https://mega.nz/file/RVlVBbZT#0CaZUQlvslDYS2XZO2Mx03CRcIbwN278fF6cwNo5Tl4