Spring data JPA : Kết nối bảng với Spring Data - JPA Specifications

Bài viết này được dịch bài : Trần Anh Tuấn - Học viện lớp java07
Email liên hệ : tanhtuan093@gmail.com
Bài viết gốc : https://www.baeldung.com/spring-jpa-joining-tables

1.Tổng quan

Trong hướng dẫn này , chúng ta sẽ thảo luận về một số tính năng nâng cao hơn của Spring Data JPA Specifications , cái mà cho phép chúng ta kết nối các bảng với nhau khi tạo câu truy vấn.
Hãy bắt đầu với một bản tóm tắt ngắn gọn của JPA Specifications về cách dùng của chúng.

2.JPA Specifications

Spring Data JPA đã giới thiệu Specification interface cho phép chúng ta tạo câu lệnh truy vấn động có thể tái sử dụng.

@Entity
public class Author {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String firstName;

    private String lastName;

    @OneToMany(cascade = CascadeType.ALL)
    private List<Book> books;

    // getters and setters
}

Để tạo truy vấn động cho Author , chúng ta có thể sử dụng các phương thức của Specification interface :

public class AuthorSpecifications {

    public static Specification<Author> hasFirstNameLike(String name) {
        return (root, query, criteriaBuilder) ->
          criteriaBuilder.like(root.<String>get("firstName"), "%" + name + "%");
    }

    public static Specification<Author> hasLastName(String name) {
        return (root, query, cb) ->
          cb.equal(root.<String>get("lastName"), name);
    }
}

Cuối cùng, chúng ta sẽ cần AuthorRepository để extent JpaSpecificationExecutor:

@Repository
public interface AuthorsRepository extends JpaRepository<Author, Long>, JpaSpecificationExecutor<Author> {
}

Kết quả là , chúng ta có thể kết nối hai specifications lại với nhau và tạo các truy vấn với chúng:

@Test
public void whenFindByLastNameAndFirstNameLike_thenOneAuthorIsReturned() {
    
    Specification<Author> specification = hasLastName("Martin")
      .and(hasFirstNameLike("Robert"));

    List<Author> authors = repository.findAll(specification);

    assertThat(authors).hasSize(1);
}

3.Kết nối bảng với JPA Specifications

Từ mô hình dữ liệu, chúng ta có thể quan sát thấy Author có quan hệ một - nhiều với Book

@Entity
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    // getters and setters
}

Criteria Query API cho phép chúng ta kết nối hai bảng với nhau khi tạo Specification . Do đó, chúng ta sẽ có thể đưa các trường từ Book vào bên trong các truy vấn của mình:

public static Specification<Author> hasBookWithTitle(String bookTitle) {
    return (root, query, criteriaBuilder) -> {
        Join<Book, Author> authorsBook = root.join("books");
        return criteriaBuilder.equal(authorsBook.get("title"), bookTitle);
    };
}

Bây giờ, hãy kết hợp Specification này với những thông số đã tạo trước đó :

@Test
public void whenSearchingByBookTitleAndAuthorName_thenOneAuthorIsReturned() {

    Specification<Author> specification = hasLastName("Martin")
      .and(hasBookWithTitle("Clean Code"));

    List<Author> authors = repository.findAll(specification);

    assertThat(authors).hasSize(1);
}

Cuối cùng, chúng ta hãy xem qua câu lệnh SQL và câu lệnh JOIN được tạo ra :

select 
  author0_.id as id1_1_, 
  author0_.first_name as first_na2_1_, 
  author0_.last_name as last_nam3_1_ 
from 
  author author0_ 
  inner join author_books books1_ on author0_.id = books1_.author_id 
  inner join book book2_ on books1_.books_id = book2_.id 
where 
  author0_.last_name = ? 
  and book2_.title = ?

4.Kết luận

Trong bài viết này, chúng ta đã học cách sử dụng JPA Specifications để truy vấn một bảng dựa trên một trong các thực thể được liên kết của nó.
Spring Data JPA’s Specifications giúp tạo câu lệnh đơn giản , động , và có thể tái sử dụng.