Written by: Phạm Minh Khải
Gmail: mkpham2000@gmail.com
Bài viết gốc: https://www.baeldung.com/the-persistence-layer-with-spring-data-jpa

1. Khái quát chung

Bài viết này sẽ tập trung vào việc giới thiệu Spring Data JPA vào một dự án Spring và cấu hình đầy đủ lớp bền vững (persistence layer).

2.Spring Data đã tạo DAO - Không còn thực hiện DAO

Lớp DAO thường bao gồm rất nhiều mã soạn sẵn có thể và cần được đơn giản hóa. Ưu điểm của việc đơn giản hóa như vậy là rất nhiều: giảm số lượng các thành phần mà bạn cần xác định và duy trì, tính nhất quán của các mẫu truy cập dữ liệu và tính nhất quán của cấu hình.

Spring Data đơn giản hóa điều này và có thể loại bỏ hoàn toàn việc triển khai DAO. Giao diện của DAO hiện là tạo tác duy nhất mà bạn cần xác định rõ ràng.

Để bắt đầu tận dụng mô hình lập trình với JPA, giao diện DAO cần mở rộng giao diện Kho lưu trữ cụ thể JPA, JpaRepository. Điều này sẽ cho phép Spring Data tìm thấy giao diện này và tự động tạo một triển khai cho nó.

Bằng cách mở rộng giao diện, bznj có thể sử dụng các phương pháp CRUD phù hợp nhất để truy cập dữ liệu tiêu chuẩn có sẵn trong một DAO tiêu chuẩn.

3. Phương pháp truy cập tùy chỉnh và truy vấn.

Bằng cách triển khai một trong các Repository interfaces, DAO sẽ có một số phương thức CRUD cơ bản (và truy vấn) được xác định và triển khai.

Để xác định các phương thức truy cập cụ thể hơn, Spring JPA hỗ trợ khá nhiều tùy chọn:

  • Chỉ cần xác định một phương thức mới trong giao diện
  • Cung cấp truy vấn JPQL query bằng cách sử dụng chú thích @Query annonation
  • Sử dụng nâng cao hơn Specification and Querydsl support trong Spring Data
  • Xác định truy vấn tùy chỉnh thông qua Truy vấn có tên JPA (JPA Named Queries)

Tùy chọn thứ ba, Specifications và Querydsl support, tương tự như Tiêu chí JPA (JPA Criteria), nhưng sử dụng API linh hoạt và thuận tiện hơn. Điều này làm cho toàn bộ hoạt động dễ đọc hơn và có thể sử dụng lại. Ưu điểm của API này sẽ trở nên rõ ràng hơn khi xử lý một số lượng lớn các truy vấn cố định, vì bạni có thể diễn đạt các truy vấn này một cách ngắn gọn hơn thông qua một số lượng nhỏ các khối có thể tái sử dụng.

Tùy chọn cuối cùng có nhược điểm là nó liên quan đến XML hoặc tạo gánh nặng cho lớp miền (domain class) với các truy vấn.

3.1. Truy vấn tùy chỉnh tự động (Automatic Custom Queries)

Khi Spring Data tạo một thực thi Repository mới, nó sẽ phân tích tất cả các phương thức được xác định bởi các giao diện interfaces và cố gắng tự động tạo các truy vấn queries từ tên phương thức. Mặc dù điều này có một số hạn chế, nhưng đó là một cách rất mạnh mẽ và tao nhã để xác định các phương thức truy cập tùy chỉnh mới với rất ít nỗ lực.
Hãy xem một ví dụ. Nếu thực thể entity có trường tên (và các phương thức getName và setName tiêu chuẩn của Java Bean), chúng ta sẽ xác định phương thức findByName trong giao diện DAO. Điều này sẽ tự động tạo truy vấn chính xác:

public interface IFooDAO extends JpaRepository<Foo, Long> {

    Foo findByName(String name);
}

Đây là một ví dụ tương đối đơn giản. Cơ chế tạo truy vấn hỗ trợ một tập hợp các từ khóa lớn hơn nhiều.

Trong trường hợp trình phân tích cú pháp không thể khớp thuộc tính với trường đối tượng miền, chúng tôi sẽ thấy ngoại lệ sau:

java.lang.IllegalArgumentException: No property nam found for type class com.baeldung.spring.data.persistence.model.Foo

3.2. Truy vấn tùy chỉnh thủ công (Manual Custom Queries)

Hãy xem xét một truy vấn tùy chỉnh mà chúng ta sẽ xác định thông qua chú thích @Query annotation:

@Query("SELECT f FROM Foo f WHERE LOWER(f.name) = LOWER(:name)")
Foo retrieveByName(@Param("name") String name);

Để kiểm soát chi tiết hơn việc tạo các truy vấn, chẳng hạn như sử dụng các tham số đã đặt tên hoặc sửa đổi các truy vấn hiện có, tham chiếu là một nơi tốt để bắt đầu.

4. Cấu hình giao dịch (Transaction Configuration )

Việc triển khai thực tế của DAO do Spring quản lý thực sự bị ẩn vì không trực tiếp làm việc với nó. Tuy nhiên, đó là một cách triển khai đơn giản, SimpleJpaRepository, định nghĩa ngữ nghĩa giao dịch bằng cách sử dụng chú thích annotations.

Nói một cách rõ ràng hơn, điều này sử dụng @Transactional annotation chỉ đọc ở cấp độ lớp, sau đó được ghi đè cho các phương thức không chỉ đọc. Phần còn lại của ngữ nghĩa giao dịch là mặc định, nhưng chúng có thể dễ dàng bị ghi đè theo cách thủ công cho mỗi phương thức.

4.1. Bản dịch ngoại lệ là còn sống và tốt (Exception Translation Is Alive and Well)

Vì Spring Data JPA không phụ thuộc vào các mẫu ORM cũ (JpaTemplate, HibernateTemplate) và chúng đã bị xóa kể từ Spring 5, bạn liệu vẫn sẽ dịch các ngoại lệ JPA của mình sang hệ thống phân cấp DataAccessException của Spring không?

Tất nhiên, câu trả lời là có. Dịch ngoại lệ vẫn được bật bằng cách sử dụng chú thích @Repository trên DAO. Chú thích này cho phép bộ xử lý hậu đậu Spring thông báo cho tất cả các bean @Repository với tất cả các phiên bản PersistenceExceptionTranslator được tìm thấy trong vùng chứa và cung cấp bản dịch ngoại lệ giống như trước đây.

Hãy xác minh bản dịch ngoại lệ bằng kiểm tra tích hợp:

@Test(expected = DataIntegrityViolationException.class)
public void givenFooHasNoName_whenInvalidEntityIsCreated_thenDataException() {
    service.create(new Foo());
}

Bản dịch ngoại lệ được thực hiện thông qua các proxies. Để Spring có thể tạo proxy xung quanh các lớp DAO, chúng không được khai báo cuối cùng.

5. Cấu hình Spring Data JPA Repository

Để kích hoạt hỗ trợ kho lưu trữ Spring JPA, bạn có thể sử dụng chú thích @EnableJpaRepositories và chỉ định gói chứa các giao diện DAO:

@EnableJpaRepositories (basePackages = "com.baeldung.spring.data.persistence.repository")
public class PersistenceConfig {
    ...
}

Chúng ta có thể làm tương tự với cấu hình XML:

<jpa:repositories base-package="com.baeldung.spring.data.persistence.repository" />

6. Cấu hình Java hoặc XML

Spring Data cũng tận dụng sự hỗ trợ của Spring cho JPA annonation @PersistenceContext. Nó sử dụng điều này để chuyển EntityManager vào Spring factory bean chịu trách nhiệm tạo các triển khai DAO thực tế, JpaRepositoryFactoryBean.

Ngoài cấu hình đã được thảo luận, bạn cũng cần bao gồm Cấu hình XML Spring Data nếu bạn đang sử dụng XML:

@Configuration
@EnableTransactionManagement
@ImportResource("classpath*:*springDataConfig.xml")
public class PersistenceJPAConfig {
    ...
}

7. Maven Dependency

Ngoài cấu hình Maven cho JPA, chúng ta sẽ thêm spring-data-jpa dependency:

<dependency>
   <groupId>org.springframework.data</groupId>
   <artifactId>spring-data-jpa</artifactId>
   <version>2.4.0</version>
</dependency>

8. Sử dụng Spring Boot

Bạn cũng có thể sử dụng Spring Boot Starter Data JPA dependency sẽ tự động định cấu hình DataSource.

Cần đảm bảo rằng cơ sở dữ liệu bạn muốn sử dụng có trong classpath. Trong ví dụ nay, chúng ta đã thêm cơ sở dữ liệu H2 trong bộ nhớ:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
   <version>2.6.1</version>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.200</version>
</dependency>

Chỉ bằng cách thực hiện các phụ thuộc này, ứng dụng sẽ bắt đầu hoạt động và có thể sử dụng nó cho các hoạt động cơ sở dữ liệu khác.

Cấu hình rõ ràng cho một ứng dụng Spring tiêu chuẩn hiện được bao gồm như một phần của cấu hình tự động Spring Boot.

Tất nhiên, bạn có thể sửa đổi cấu hình tự động bằng cách thêm cấu hình rõ ràng tùy chỉnh.

Spring Boot cung cấp một cách dễ dàng để thực hiện việc này bằng cách sử dụng các thuộc tính trong tệp application.properties. Hãy xem một ví dụ về việc thay đổi URL kết nối và thông tin đăng nhập:

spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa

9.Các công cụ hữu ích cho Spring Data JPA

Spring Data JPA được hỗ trợ trong tất cả các IDE Java chính. Hãy xem những công cụ hữu ích nào có sẵn cho Eclipse và IntelliJ IDEA.

Nếu bạn sử dụng Eclipse làm IDE của mình, bạn có thể cài đặt plugin Dali Java Persistence Tools. Điều này cung cấp các sơ đồ ER cho các thực thể JPA, tạo DDL để khởi tạo giản đồ và các khả năng thiết kế ngược cơ bản. Ngoài ra, bạn có thể sử dụng Eclipse Spring Tool Suite (STS). Nó sẽ giúp xác thực tên phương thức truy vấn trong kho lưu trữ Spring Data JPA.

Trong trường hợp bạn sử dụng IntelliJ IDEA, có hai tùy chọn.

IntelliJ IDEA Ultimate cho phép các sơ đồ ER, một bảng điều khiển JPA để kiểm tra các câu lệnh JPQL và các kiểm tra có giá trị. Tuy nhiên, các tính năng này không có sẵn trong Phiên bản Cộng đồng.

Để tăng năng suất trong IntelliJ, bạn có thể cài đặt plugin JPA Buddy, plugin này cung cấp nhiều tính năng, bao gồm tạo các thực thể JPA, kho lưu trữ Spring Data JPA, DTO, tập lệnh DDL khởi tạo, di chuyển phiên bản Flyway, thay đổi Liquibase, v.v. Ngoài ra, JPA Buddy cung cấp một công cụ tiên tiến cho kỹ thuật đảo ngược.

Cuối cùng, plugin JPA Buddy hoạt động với cả phiên bản Community và Ultimate.

10. Kết luận

Trong bài viết này, chúng ta đã đề cập đến cấu hình và triển khai của persistence layer với Spring 5, JPA 2 và Spring Data JPA (một phần của dự án Spring Data umbrella) bằng cách sử dụng cả cấu hình dựa trên XML và Java.

Chúng ta đã thảo luận về các cách để xác định các truy vấn tùy chỉnh nâng cao hơn, cũng như ngữ nghĩa giao dịch và cấu hình với không gian tên jpa mới. Kết quả cuối cùng là một quyền truy cập dữ liệu mới và thanh lịch với Spring, hầu như không có công việc triển khai thực tế nào.