Mến chào các bạn lần đầu đến với chủ đề Java Spring trên lopHocViTinh.com!
Hôm nay chúng ta sẽ demo 01 Restful API Backend có gắn thêm đồ chơi Swagger - công cụ tương tự Junit Tests nhưng có màn hình dạng web cực kỳ trực quan - dễ sử dụng.
Đầu tiên chúng ta tạo mới 01 project Spring Boot bằng cách vào trang này:
p/s: các bạn cũng có thể tạo project trong IDE của mình đang có (Ví dụ: IntelliJ Idea hoặc VS Code)
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.7'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
// https://mvnrepository.com/artifact/io.springfox/springfox-swagger2
implementation group: 'io.springfox', name: 'springfox-swagger2', version: '2.9.2'
// https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui
implementation group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2'
// https://mvnrepository.com/artifact/io.springfox/springfox-boot-starter
//implementation group: 'io.springfox', name: 'springfox-boot-starter', version: '3.0.0'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation 'org.springframework.boot:spring-boot-starter-web'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.mysql:mysql-connector-j'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
Các bạn nào tạo project trên web spring.io theo mình nên né đầu giờ sáng ra (có lẽ vì giờ này lập trình viên vào nhiều chăng???)
Chúng ta tạo 01 cơ sở dữ liệu tên: db_demo
(ở đây mình dùng MySQL WorkBench - tool mặc định của Oracle)
create database `db_demo`;
use `db_demo`;
create table `todo`(
`id` int not null auto_increment,
name varchar(128),
status int,
primary key(id)
)ENGINE=InnoDB;
INSERT INTO `db_demo`.`todo` (`id`, `name`, `status`) VALUES ('1', 'Hotdog', 1);
INSERT INTO `db_demo`.`todo` (`id`, `name`, `status`) VALUES ('2', 'Jelly roll', 1);
INSERT INTO `db_demo`.`todo` (`id`, `name`, `status`) VALUES ('3', 'Croissant', 1);
INSERT INTO `db_demo`.`todo` (`id`, `name`, `status`) VALUES ('4', 'Hamburger bun', 1);
INSERT INTO `db_demo`.`todo` (`id`, `name`, `status`) VALUES ('5', 'Bagels', 1);
INSERT INTO `db_demo`.`todo` (`id`, `name`, `status`) VALUES ('6', 'Donut', 1);
INSERT INTO `db_demo`.`todo` (`id`, `name`, `status`) VALUES ('7', 'Rolls', 1);
INSERT INTO `db_demo`.`todo` (`id`, `name`, `status`) VALUES ('8', 'Breadsticks', 1);
INSERT INTO `db_demo`.`todo` (`id`, `name`, `status`) VALUES ('9', 'Baguette', 1);
INSERT INTO `db_demo`.`todo` (`id`, `name`, `status`) VALUES ('10', 'Tiramisu', 1);
INSERT INTO `db_demo`.`todo` (`id`, `name`, `status`) VALUES ('11', 'Biscuit', 1);
INSERT INTO `db_demo`.`todo` (`id`, `name`, `status`) VALUES ('12', 'Pizza', 1);
INSERT INTO `db_demo`.`todo` (`id`, `name`, `status`) VALUES ('13', 'Cheese cake', 1);
INSERT INTO `db_demo`.`todo` (`id`, `name`, `status`) VALUES ('14', 'Pate chaud', 1);
//truncate table `todo`;
//drop table `todo`;
//drop database `db_demo`;
Đừng chạy 03 dòng cuối cùng nhé!!!
Tiếp theo là file cấu hình kết nối cơ sở dữ liệu: application.properties
server.port=8080Các bạn chú ý dùm cho dòng cấu hình cuối cùng -> Đây chính là dòng fix lỗi swagger (mình đã mò chỗ này rất lâu mới tìm được)
spring.jpa.hibernate.ddl-auto=update
#install mysql using Docker
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/db_demo
spring.datasource.username=root
spring.datasource.password=*****
#allow table's name like tblProduct
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.properties.hibernate.format_sql=true
spring.mvc.pathmatch.matching-strategy=ant-path-matcher
package backendapi.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}
package backendapi.controller;
import backendapi.model.Todo;
import backendapi.service.ITodoService;
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("/api/todo")
@CrossOrigin //phải có cái này lúc truy xuất bằng JS mới cho phép
public class TodoApiController {
@Autowired
ITodoService todoService;
@GetMapping()
public ResponseEntity<Iterable<Todo>> findAllTodo() {
List<Todo> todos = (List<Todo>) todoService.findAll();
if(todos.isEmpty()){
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(todos, HttpStatus.OK);
}
@PostMapping
public ResponseEntity<Todo> saveTodo(@RequestBody Todo todo) {
return new ResponseEntity<>(todoService.save(todo), HttpStatus.CREATED);
}
@PutMapping("/{id}")
public ResponseEntity<Todo> updateTodo(@PathVariable Long id, @RequestBody Todo todo) {
Optional<Todo> todoOptional = todoService.findById(id);
if(!todoOptional.isPresent()) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
todo.setId(todoOptional.get().getId());
return new ResponseEntity<>(todoService.save(todo), HttpStatus.OK);
}
@DeleteMapping("/{id}")
public ResponseEntity<Todo> deleteTodo(@PathVariable Long id) {
Optional<Todo> todoOptional = todoService.findById(id);
if(!todoOptional.isPresent()){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
todoService.remove(id);
return new ResponseEntity<>(todoOptional.get(), HttpStatus.NO_CONTENT);
}
}
Và CachingController.java (bài này mình tạo thêm tính năng Cache và Clear Cache để load dữ liệu từ API cho nhanh)
package backendapi.controller;
import backendapi.service.CachingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CachingController {
@Autowired
CachingService cachingService;
@GetMapping("clearAllCaches")
public void clearAllCaches() {
cachingService.evictAllCaches();
}
}
package backendapi.model;
import javax.persistence.*;
import java.io.Serializable;
@Entity
@Table(name="todo")
public class Todo implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name="name")
private String name;
@Column(name="status")
private int status;
public Todo() {}
public Todo(String name, int status) {
this.name = name;
this.status = status;
}
@Override
public String toString() {
return "Todo{" +
"id=" + id +
", name='" + name + '\'' +
", status=" + status +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
}
Tạo thư mục repository chứa file: ITodoRepository.java
package backendapi.repository;
import backendapi.model.Todo;
import org.springframework.data.repository.PagingAndSortingRepository;
public interface ITodoRepository extends PagingAndSortingRepository<Todo, Long> {
}
Cuối cùng là thư mục service chứa các file như sau:
package backendapi.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
@Service
public class CachingService {
@Autowired
CacheManager cacheManager;
public void evictAllCaches() {
System.out.println(cacheManager.getCacheNames());
cacheManager.getCacheNames().stream()
.forEach(cacheName -> cacheManager.getCache(cacheName).clear());
System.out.println(cacheManager.getCacheNames());
}
}
ITodoService.java
package backendapi.service;
import backendapi.model.Todo;
public interface ITodoService extends IGeneralService<Todo, Long>{
}
package backendapi.service;
import backendapi.model.Todo;
import backendapi.repository.ITodoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class TodoService implements ITodoService {
@Autowired
private ITodoRepository todoRepository;
@Override
@Cacheable("todos")
public Iterable<Todo> findAll(){return todoRepository.findAll(); }
@Override
public Optional<Todo> findById(Long id){ return todoRepository.findById(id); }
@Override
public Todo save(Todo todo){ return todoRepository.save(todo); }
@Override
public void remove(Long id){ todoRepository.deleteById(id);}
}
package backendapi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching
//@EnableSwagger2
public class BackendapiApplication {
public static void main(String[] args) {
SpringApplication.run(BackendapiApplication.class, args);
}
}
/*
link test Swagger2:
http://localhost:8080/swagger-ui.html
*/
package backendapi;
import backendapi.controller.CategoryApiController;
import backendapi.service.CategoryService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
public class NullTests {
@Autowired
private CategoryApiController categoryApiController;
@Autowired
private CategoryService categoryService;
@Autowired
private MockMvc mockMvc;
@Test
void contextLoads(){//đảm bảo có dữ liệu trong 02 khu vực quan trọng-> kết nối success
assertThat(categoryApiController).isNotNull();
assertThat(categoryService).isNotNull();
}
@Test
public void shouldReturnStatusOK() throws Exception{//test status 200
this.mockMvc.perform(get("/api/category")).andDo(print()).andExpect(status().isOk());
}
}
p/s: ví dụ này mình dùng cho controller category -> sửa lại thành TodoApiController dùm mình.
Màn hình file Readme |
[ Link bài viết gốc ]
Không có nhận xét nào:
Đăng nhận xét