--- title: 'SpringBoot crud restful api (使用資料庫還有postman)' disqus: hackmd --- ###### tags: `SpringBoot` SpringBoot crud restful api (使用資料庫還有postman) === [TOC] ## 筆記目的 > 讓你了解怎麼樣使用mysql還有spring boot 實現restful api 讓你可以前後端分離,本筆記只有後端部分,所以使用postman來當作客戶端來測試 本筆記的架構大概如下: ![123](https://i.imgur.com/QqjIjPr.png) > 介面層(Controller) 接收前端請求(處理網址) 業務邏輯層(Service):根據請求做資料處理或是處理從DAO回來的資料。 資料訪問層(Dao):對資料庫做增修查改等操作。 開發的順序很簡單: 1. 先完成applications的設定 2. 完成model 3. 完成dao也就是repository 4. 完成service 5. 完成controller 6. 完成exception [引用自Day 17 - Spring Boot Todo List RESTful API 實作](https://ithelp.ithome.com.tw/articles/10244715) 具體請參考: * [Spring Boot Hibernate MySQL CRUD REST API Tutorial | Full Course](https://www.youtube.com/watch?v=4Ico2HjRTCE) * [application.properties (ithome)](https://ithelp.ithome.com.tw/articles/10217667) * [完整的設定application(ithome)](https://ithelp.ithome.com.tw/articles/10223745) ## 先備條件 1. 請安裝mysql 並設定好root的密碼 2. 請在spring boot 啟用時對以下的東西打勾 Spring Web Spring data jpa mysql driver lombok ![](https://i.imgur.com/c7L6YGq.png) ## 程式碼部分 ### 專案架構 ![](https://i.imgur.com/xxdm2KO.png) ### application.properties ```xml= spring.jpa.hibernate.ddl-auto=update spring.datasource.url=jdbc:mysql://localhost:3306/helloworld?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC ###helloworld is database name資料庫名稱,不是資料表名稱喔 spring.datasource.username=root spring.datasource.password=yourpassword ###下面的都照抄,不懂去翻ithome的連結 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect spring.jpa.properties.hibernate.dialect.storage_engine=innodb ``` ### pom.xml 在此省略,因為只要你有在先備條件裡面打勾,便會自動生成 ### Model ```java= package com.example.demo.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; @Entity @Table(name="employees") public class Employee { public Employee() { super(); // TODO Auto-generated constructor stub } public Employee(long id, String firstName, String lastName, String email) { super(); this.id = id; this.firstName = firstName; this.lastName = lastName; this.email = email; } @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @Column(name = "first_name", nullable = false) private String firstName; @Column(name = "last_name") private String lastName; @Column(name = "email") private String email; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } } ``` ### Dao ``` package com.example.demo.dao; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.example.demo.model.Employee; @Repository public interface EmployeeRepository extends JpaRepository<Employee, Long>{ } ``` > 繼承JpaRepository讓我不需要去實作方法,只是要記住他的語法,所以重點都會擺在 > service impl ### Service ```java= package com.example.demo.service; import java.util.List; import org.springframework.stereotype.Service; import com.example.demo.model.Employee; @Service public interface EmployeeService { Employee saveEmployee(Employee employee); List<Employee> getAllEmployees(); Employee getEmployeeById(long id); Employee updateEmployee(Employee employee, long id); void deleteEmployee(long id); } ``` > service的介面,重點是回傳的資料型態,還有方法名稱要取的好,參數當然也是 ### Service impl ```java= package com.example.demo.service.impl; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.example.demo.dao.EmployeeRepository; import com.example.demo.model.Employee; import com.example.demo.service.EmployeeService; import exception.ResourceNotFoundException; @Service public class EmployeeServiceImpl implements EmployeeService { @Autowired EmployeeRepository employeeRepository; public EmployeeServiceImpl(EmployeeRepository employeeRepository) { super(); this.employeeRepository = employeeRepository; } @Override public Employee saveEmployee(Employee employee) { return employeeRepository.save(employee); } @Override public List<Employee> getAllEmployees() { return employeeRepository.findAll(); } @Override public Employee getEmployeeById(long id) { // Optional<Employee> employee = employeeRepository.findById(id); // if(employee.isPresent()) { // return employee.get(); // }else { // throw new ResourceNotFoundException("Employee", "Id", id); // } return employeeRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Employee", "Id", id)); } @Override public Employee updateEmployee(Employee employee, long id) { // we need to check whether employee with given id is exist in DB or not Employee existingEmployee = employeeRepository.findById(id) .orElseThrow(() -> new ResourceNotFoundException("Employee", "Id", id)); existingEmployee.setFirstName(employee.getFirstName()); existingEmployee.setLastName(employee.getLastName()); existingEmployee.setEmail(employee.getEmail()); // save existing employee to DB employeeRepository.save(existingEmployee); return existingEmployee; } @Override public void deleteEmployee(long id) { // check whether a employee exist in a DB or not employeeRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Employee", "Id", id)); employeeRepository.deleteById(id); } } ``` > 千萬要記住要`Autowired EmployeeRepository employeeRepository` > 基本上就是宣告一個類別型態為EmployeeRepository的實體,而employeeRepository是變數名稱,所以都要使用employeeRepository來call 內建的方法 put的邏輯比較難,務必要看懂她的巧妙思考方式 ### Controller ```java= package com.example.demo.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; 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.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.example.demo.model.Employee; import com.example.demo.service.EmployeeService; @RestController @RequestMapping("/api/employees") public class EmployeeController { @Autowired EmployeeService employeeService; // public EmployeeController(EmployeeService employeeService) { // super(); // this.employeeService = employeeService; // } // build create employee REST API @PostMapping() public ResponseEntity<Employee> saveEmployee(@RequestBody Employee employee) { return new ResponseEntity<Employee>(employeeService.saveEmployee(employee), HttpStatus.CREATED); } // build get all employees REST API @GetMapping public List<Employee> getAllEmployees() { return employeeService.getAllEmployees(); } // build get employee by id REST API // http://localhost:8080/api/employees/1 @GetMapping("{id}") public ResponseEntity<Employee> getEmployeeById(@PathVariable("id") long employeeId) { return new ResponseEntity<Employee>(employeeService.getEmployeeById(employeeId), HttpStatus.OK); } // build update employee REST API // http://localhost:8080/api/employees/1 @PutMapping("{id}") public ResponseEntity<Employee> updateEmployee(@PathVariable("id") long id, @RequestBody Employee employee) { return new ResponseEntity<Employee>(employeeService.updateEmployee(employee, id), HttpStatus.OK); } // build delete employee REST API // http://localhost:8080/api/employees/1 @DeleteMapping("{id}") public ResponseEntity<String> deleteEmployee(@PathVariable("id") long id) { // delete employee from DB employeeService.deleteEmployee(id); return new ResponseEntity<String>("Employee deleted successfully!.", HttpStatus.OK); } } ``` ### Exception ``` package exception; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.http.HttpStatus; @ResponseStatus(value = HttpStatus.NOT_FOUND) public class ResourceNotFoundException extends RuntimeException{ /** * */ private static final long serialVersionUID = 1L; private String resourceName; private String fieldName; private Object fieldValue; public ResourceNotFoundException(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; } } ``` ## 測試 基本上這裡測試的邏輯都是使用postman,基本的操作是所謂的crud 就是大家說的增刪改查就對了 | 資料庫操作 | 對應的postman request | | ---------- | --------------------- | | create | Post | | read | Get | | update | Put | | delete | Delete | 所以我的測試邏輯很簡單,你想要新增員工就使用Post請求,然會去看看我們的controller是 怎麼寫url的 ```java=21 @RequestMapping("/api/employees") ``` 所以你可以知道你要適用的url就會是`http://localhost:8080`+`/api/employees` =`http://localhost:8080/api/employees` 然後再加上一些變化。以下會詳細給出例子 ### 新增員工 ![](https://i.imgur.com/Owbip6Z.png) ``` http://localhost:8080/api/employees ``` put the block code into body ``` { "firstName":"mike", "lastName":"sandy", "email":"hotmail@gmail.com" } ``` > 因為我這裡是新增員工,所以我不需要給定員工id,這個程式碼會自動幫我加上 ### 查詢員工 ``` http://localhost:8080/api/employees/5 ``` ![](https://i.imgur.com/00BhPUN.png) ### 更改員工 ``` http://localhost:8080/api/employees/5 ``` ``` { "id": 5, "firstName": "Terry", "lastName": "sandy", "email": "a57249683012@gmail.com" } ``` > 更改員工便需要在body的地方加上id,不然你不知道你是要更改誰而且要傳回完整的你想要更改的,不然他會變成null,舉例而言,如果你把lastname刪掉,他就會變成 ![](https://i.imgur.com/qSEFkHP.png) ### 刪除員工 ``` http://localhost:8080/api/employees/5 ``` ![](https://i.imgur.com/EZAypUo.png) 打開資料庫後會發現5號員工確實被刪除了 ![](https://i.imgur.com/FaoTLnf.png)