iT邦幫忙

2024 iThome 鐵人賽

DAY 5
0
Software Development

我的SpringBoot絕學:7+2個專案,從新手變專家系列 第 5

Day5 第二個專案:待辦事項清單(3)實作篇

  • 分享至 

  • xImage
  •  

我們上一篇完成與資料庫的連接,接著來我們要實現實現概念篇中設計的功能。

在src/main/java/com/restufulapi/restfulapi/Entity下,新增Todo.java。
如果Entity不存在就新增,之後如果有類似的情況也是用同樣的方式處理。

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內建的功能產生的,不需要自己手打。

教大家如何使用

  1. 我們completed的下方按下右鍵
  2. 選擇Generate

  1. 點擊Constructor

  1. 先按下Select None,產生空的public todo。

  2. 我們再重複同樣的步驟到Constructor那一步,這次選取id、title、completed後按下OK。

然後我們來設定getter和setter

  1. 右鍵
  2. 選擇Generate
  3. 點擊Getter and Setter

  1. 選取id、title、completed後按下OK。

啟動專案,我們可以在console看到Hibernate開頭的內容,後面就是Spring Data JPA自動產生的SQL statement。

Repository

在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

不分成Service、ServiceImpl的原因

如果之前看過其他的教學,可能會認為要分成Service和ServiceImpl,這麽做是為了重複使用Service的架構,他們認為會有相同的命名但是內部的操作不同的情況發生。

但是我認為沒有這個必要,大部分的service的操作都是固定的,不會重複使用,額外分成Service和ServiceImpl就是浪費時間的做法。

Controller用到的功能在Service實作的原因

在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));
    }
}

Controller

在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


上一篇
Day4 第二個專案:待辦事項清單(2)連接資料庫
下一篇
Day6 第二個Spring Boot專案:待辦事項清單(4)測試
系列文
我的SpringBoot絕學:7+2個專案,從新手變專家31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言