Ngoài những phương thức mặc định cơ bản như save, saveAndFlush, findById, deleteInBatch, thì spring boot jpa cũng cho chúng ta định nghĩa ra các hàm hoặc các câu query để đáp ứng nhu cầu nghiệp vụ. Trong bài này chúng ta sẽ cùng tìm hiểu sâu hơn về JPA nhé.

Các hàm truy vấn theo cú pháp

JPA cho cho phép chúng ta định nghĩa các hàm theo cú pháp kiểu:

[action]By[Tên trường][Toán tử][And hoặc Or]

Ở đây chúng ta có:

  1. action: Là hành động ví dụ: find, delete.
  2. Tên trường: Là tên của trường trong lớp java, ví dụ: Name, Id.
  3. Toán tử: Ví dụ Not, In, NotIn
    Ví dụ:
package vn.techmaster.jpa.repo;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import vn.techmaster.jpa.entity.Person;

import java.util.Collection;
import java.util.List;

@Repository
public interface PersonRepository extends JpaRepository<Person, Long> {

    // lấy bản ghi đầu tiên của trường name bằng tham số truyền vào
    Person findFirstByName(String name);

    // lấy bản ghi đầu tiên có trường name khác tham số truyền vào
    Person findFirstByNameNot(String name);

    // lấy tất cả các bản ghi có trường name nằm trong danh sách của tham số truyền vào
    List<Person> findByNameIn(Collection<String> names);
}

Các hàm truy vấn theo query

Spring boot jpa cho phép chúng ta tạo câu truy vấn thông qua JPQL hoặc native query, tuy nhiên mình khuyến khích bạn sử dụng JPQL cho thân thiện với java, nó cũng dễ viết hơn với native query nếu tên bảng, tên trường của bạn quá phức tạp.

Truy vấn với JPQL

Chúng ta có thể truy vấn danh sách các bản ghi có name gần đúng như sau:

// Truy vấn danh sách các bản ghi có trường name gần đúng với tham số truyền vào
@Query("SELECT e FROM Person e WHERE e.name LIKE %?1%")
List<Person> findByNameLikeJpql(String keyword);

Truy vấn với native query

Chúng ta có thể truy vấn danh sách các bản ghi có name gần đúng như sau:

@Query(
    value = "SELECT * FROM persons WHERE name LIKE %?1%",
    nativeQuery = true
)
List<Person> findByNameLikeNative(String keyword);

Ở đây thay vì sử dụng tên lớp Java là Person chúng ta sẽ sử dụng tên bảng là persons, đồng thời chúng ta cũng phải thêm thuộc tính nativeQuery = true và Query annotation để nói cho spring biết rằng câu truy vấn được sử dụng là native.

Phân trang

Thông thường chúng ta sẽ lấy một ít dữ liệu mỗi lần để tránh tràn bộ nhớ, vì có thể có hàng trăm triệu bản ghi, nếu mỗi bản ghi có dung lượng 1KB thi sẽ cần hàng trăm GB bộ nhớ, điều này rõ ràng là không ổn.
Nắm được điều này, spring cũng đã cung cấp cho chúng ta cơ chế phân trang theo số trang và số lượng phần tử trong trang thông qua interface Pageable. Ví dụ.

@Query("SELECT e FROM Person e WHERE e.name LIKE %?1%")
List<Person> findByNameLikeJpqlPagination(
    String keyword,
    Pageable pageable
);

Khởi chạy dự án

Chúng ta sẽ cập nhật lại mã nguồn của lớp SpringBootJpaStartUp như sau:

package vn.techmaster.jpa;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.PageRequest;
import vn.techmaster.jpa.entity.Person;
import vn.techmaster.jpa.repo.PersonRepository;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

@EnableCaching
@SpringBootApplication
public class SpringBootJpaStartUp {

    public static void main(String[] args) {
        ApplicationContext applicationContext = SpringApplication
            .run(SpringBootJpaStartUp.class);
        PersonRepository personRepository = applicationContext
            .getBean(PersonRepository.class);
        Person person = new Person();
        person.setName("Techmaster");
        person = personRepository.save(person);
        System.out.println("save person: " + person);
        Optional<Person> fetchedPersonById = personRepository.findById(
            person.getId()
        );
        System.out.println("fetchedPersonById: " + fetchedPersonById);

        List<Person> fetchedPersonsByNameIn = personRepository
            .findByNameIn(Arrays.asList("Techmaster", "hello"));
        System.out.println("fetchedPersonsByNameIn: " + fetchedPersonsByNameIn);

        Person fetchedPersonByName = personRepository
            .findFirstByName("hello");
        System.out.println("fetchedPersonByName: " + fetchedPersonByName);

        Person fetchedPersonByNameNot = personRepository
            .findFirstByNameNot("hello");
        System.out.println("fetchedPersonByNameNot: " + fetchedPersonByNameNot);

        List<Person> fetchedPersonsByNameLikeJpql = personRepository
            .findByNameLikeJpql(
                "t"
            );
        System.out.println("fetchedPersonsByNameLikeJpql: " + fetchedPersonsByNameLikeJpql);

        List<Person> fetchedPersonsByNameLikeNative = personRepository
            .findByNameLikeNative(
                "t"
            );
        System.out.println("fetchedPersonsByNameLikeNative: " + fetchedPersonsByNameLikeNative);

        List<Person> fetchedPersonsPagination = personRepository
            .findByNameLikeJpqlPagination(
                "t",
                PageRequest.of(0, 10)
            );
        System.out.println("fetchedPersonsPagination: " + fetchedPersonsPagination);
    }
}

Và khi chạy chương trình chúng ta sẽ nhận được kết quả:

save person: Person(id=25, name=Techmaster)
fetchedPersonById: Optional[Person(id=25, name=Techmaster)]
fetchedPersonsByNameIn: [Person(id=1, name=Techmaster), Person(id=22, name=Techmaster), Person(id=23, name=Techmaster), Person(id=24, name=Techmaster), Person(id=25, name=Techmaster)]
fetchedPersonByName: null
fetchedPersonByNameNot: Person(id=1, name=Techmaster)
fetchedPersonsByNameLikeJpql: [Person(id=1, name=Techmaster), Person(id=25, name=Techmaster)]
fetchedPersonsByNameLikeNative: [Person(id=1, name=Techmaster), Person(id=2, name=Techmaster), Person(id=3, name=Techmaster), Person(id=4, name=Techmaster), Person(id=5, name=Techmaster), Person(id=6, name=Techmaster), Person(id=25, name=Techmaster)]
fetchedPersonsPagination: [Person(id=1, name=Techmaster), Person(id=2, name=Techmaster), Person(id=3, name=Techmaster), Person(id=4, name=Techmaster), Person(id=5, name=Techmaster), Person(id=6, name=Techmaster), Person(id=7, name=Techmaster), Person(id=8, name=Techmaster), Person(id=9, name=Techmaster), Person(id=10, name=Techmaster)]

Tổng kết

Như vậy chúng ta đã cùng nhau tìm hiểu spring jpa nâng cao để lấy ra dữ liệu trong cơ sở dữ liệu.


Cám ơn bạn đã quan tâm đến bài viết|video này. Để nhận được thêm các kiến thức bổ ích bạn có thể:

  1. Đọc các bài viết của TechMaster trên facebook: https://www.facebook.com/techmastervn
  2. Xem các video của TechMaster qua Youtube: https://www.youtube.com/@TechMasterVietnam nếu bạn thấy video/bài viết hay bạn có thể theo dõi kênh của TechMaster để nhận được thông báo về các video mới nhất nhé.
  3. Chat với techmaster qua Discord: https://discord.gg/yQjRTFXb7a