post-image

[Thực hành] Quản lý khách hàng: Phân trang và tìm kiếm

7. Spring Data Repository

Mục tiêu

Luyện tập triển khai phân trang (pagination) trong Spring Data JPA.

Mô tả

Trong phần này, chúng ta sẽ bổ sung các chức năng sau cho ứng dụng Quản lý khách hàng:

  • Phân trang danh sách khách hàng
  • Tìm kiếm khách hàng

Hướng dẫn

Bước 1: Cập nhật phương thức findAll() của lớp CustomerService và CustomerServiceImpl để trả về Page<Customer> thay vì Iterable<Customer>.

  • Pageable là đối tượng có chứa 3 thuộc tính page, size và sort hỗ trợ cho việc phân trang. Pageable được tự động ánh xạ từ request
  • Page<> là đối tượng chứa danh sách các thực thể và các thuộc tính của một trang. Page<> được trả về trong response.
public interface CustomerService {
    Page<Customer> findAll(Pageable pageable);
    ...
}
public class CustomerServiecImpl implements CustomerService {
    ...
    @Override
    public Page<Customer> findAll(Pageable pageable) {
        return customerRepository.findAll(pageable);
    }
    ...
}

Bước 2: Cập nhật CustomerController sử dụng phương thức findAll() mới của CustomerService

Phương thức listCustomers() có thêm tham số Pageable, là đối tượng được mapping tự động từ URL. Pageable được tạo ra từ 3 tham số là page, size và sort. Ví dụ: /customers?page=1&size=10&sort=firstname.

@GetMapping("/customers")
public ModelAndView listCustomers(Pageable pageable){
    Page<Customer> customers = customerService.findAll(pageable);
    ModelAndView modelAndView = new ModelAndView("/customer/list");
    modelAndView.addObject("customers", customers);
    return modelAndView;
}

Để việc hỗ trợ mapping tự động này xảy ra, cần cập nhật lớp ApplicationConfig, sử dụng annotation @EnableSpringDataWebSupport.

@Configuration
@EnableWebMvc
@EnableTransactionManagement
@EnableJpaRepositories("com.codegym.cms.repository")
@ComponentScan("com.codegym.cms")
@EnableSpringDataWebSupport
public class ApplicationConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware {
...
}

Bước 3: Cập nhật file /customer/list.html để hiển thị các nút phân trang.

<div>
    <a th:href="@{'/customers'(page=${customers.number - 1})}" th:if="${customers.hasPrevious()}">Previous</a>
    <span th:text="${customers.number + 1}"></span>/<span th:text="${customers.totalPages}"></span>
    <a th:href="@{'/customers'(page=${customers.number + 1})}" th:if="${customers.hasNext()}">Next</a>
</div>

Trong đoạn mã trên, đường link đến trang tiếp theo (Next) sẽ hiển thị nếu có trang tiếp theo. Đường link đến trang trước (Previous) sẽ hiển thị nếu có trang trước.

Chạy ứng dụng, thêm nhiều hơn 20 customer để quan sát kết quả.

Bước 4: Thêm chức năng tìm kiếm khách hàng

Thêm phương thức findAllByFirstNameContaining vào interface CustomerRepository. Phương thức này sẽ được Spring Data Repository tự động tạo proxy để thực hiện câu lệnh truy vấn.

public interface CustomerRepository extends PagingAndSortingRepository<Customer, Long> {
    ...
    Page<Customer> findAllByFirstNameContaining(String firstname, Pageable pageable);
}

Cập nhật interface CustomerService và lớp CustomerService để hỗ trợ việc tìm kiếm theo trường firstname:

public interface CustomerService {
    ...
    Page<Customer> findAllByFirstNameContaining(String firstname, Pageable pageable);
}
public class CustomerServiecImpl implements CustomerService {
    ...
    @Override
    public Page<Customer> findAllByFirstNameContaining(String firstname, Pageable pageable) {
        return customerRepository.findAllByFirstNameContaining(firstname, pageable);
    }
}

Cập nhật file /customer/list.html, bổ sung form để nhập firstname để tìm kiếm:

<body>
    <a href="/create-customer">Create new customer</a>
<h1>Customers</h1>
    <form>
        Search by first name:
        <input name="s" type="text"/>
        <input type="submit" value="Search"/>
    </form><br/>
<table border="1">
...
</body>

Cập nhật phương thức listCustomers() của CustomerController để nhận vào tham số “s”.

@GetMapping("/customers")
public ModelAndView listCustomers(@RequestParam("s") Optional<String> s, Pageable pageable){
    Page<Customer> customers;
    if(s.isPresent()){
        customers = customerService.findAllByFirstNameContaining(s.get(), pageable);
    } else {
        customers = customerService.findAll(pageable);
    }
    ModelAndView modelAndView = new ModelAndView("/customer/list");
    modelAndView.addObject("customers", customers);
    return modelAndView;
}

Tham số “s” có kiểu là Optional<String>, nhằm hỗ trợ trường hợp null (tức là hiển thị toàn bộ khách hàng mà không thực hiện tìm kiếm).

Chạy ứng dụng và quan sát kết quả. Thử nhập vào trường firstname và thực hiện tìm kiếm.

Mã nguồn tham khảo (nhánh paging): https://github.com/codegym-vn/spring-jpa-customer-management/tree/paging

Leave a Reply

Your email address will not be published. Required fields are marked *