Tổng Quan Về Spring Data JPA - Hướng Dẫn Java

Views: 20.008

Trong bài viết trước, mình đã giới thiệu với các bạn tổng quan về JPA Query Language để thao tác với các loại database sử dụng các entity. Mỗi khi các bạn cần thông tin gì từ database, các bạn sẽ sử dụng các câu lệnh SELECT hoặc UPDATE hoặc DELETE cùng với tên entity để build thành câu query, sau đó sẽ lấy đối tượng Query từ Entity Manager sử dụng câu query này, và cuối cùng là gọi một phương thức thích hợp tuỳ theo nhu cầu của các bạn từ đối tượng Query này để lấy kết quả mà các bạn mong muốn. Rất nhiều bước cần phải làm phải không các bạn? Nếu các bạn sử dụng JPA cùng với Spring framework trong dự án của mình thì hãy thử sử dụng Spring Data JPA nhé! Nó sẽ giúp các bạn giảm thiểu các công việc phải làm trong một số trường hợp. Cụ thể như thế nào? Trong bài viết này, chúng ta hãy cùng nhau tìm hiểu về Spring Data JPA các bạn nhé!

Đầu tiên, mình sẽ tạo mới một Maven project để làm ví dụ:

Tổng quan về Spring Data JPA

Chúng ta sẽ sử dụng Java 17 cho ví dụ này:

Java <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties>
1234 <properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target></properties>

Spring Data JPA dependency:

XHTML <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>3.1.1</version> </dependency>
12345 <dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-jpa</artifactId><version>3.1.1</version></dependency>

Ở đây, mình sẽ sử dụng JPA với implementation của Hibernate nên mình sẽ thêm Hibernate dependency như sau:

XHTML <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>6.2.6.Final</version> </dependency>
12345 <dependency><groupId>org.hibernate</groupId><artifactId>hibernate-core</artifactId><version>6.2.6.Final</version></dependency>

Project Lombok:

XHTML <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.28</version> <scope>provided</scope> </dependency>
123456 <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version><scope>provided</scope></dependency>

Trong bài viết này, mình sẽ sử dụng MySQL database:

XHTML <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency>
12345 <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency>

Để đơn giản, mình chỉ định nghĩa 1 table chứa thông tin sinh viên với 2 cột như sau:

MySQL CREATE TABLE `student` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
12345 CREATETABLE`student`(`id`bigint(20)NOT NULLAUTO_INCREMENT,`name`varchar(50)DEFAULTNULL,PRIMARY KEY(`id`))ENGINE=InnoDBAUTO_INCREMENT=2DEFAULTCHARSET=latin1;

Entity của table này:

Java package com.huongdanjava.springdatajpa; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.Table; import java.io.Serializable; import lombok.Getter; import lombok.Setter; @Table(name = "student") @Entity @Getter @Setter public class Student implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue private Long id; @Column private String name; }
123456789101112131415161718192021222324252627 packagecom.huongdanjava.springdatajpa; importjakarta.persistence.Column;importjakarta.persistence.Entity;importjakarta.persistence.GeneratedValue;importjakarta.persistence.Id;importjakarta.persistence.Table;importjava.io.Serializable; importlombok.Getter;importlombok.Setter; @Table(name="student")@Entity@Getter@SetterpublicclassStudentimplementsSerializable{ privatestaticfinallongserialVersionUID=1L; @Id@GeneratedValueprivateLongid; @ColumnprivateStringname;}

Để làm việc với JPA, chúng ta cần có một tập tin cấu hình persistence.xml cho nó. Các bạn hãy tạo mới tập tin persistence.xml nằm trong thư mục /src/main/resources/META-INF các bạn nhé!

Nội dung của tập tin này như sau:

XHTML <?xml version="1.0" encoding="UTF-8" standalone="no"?> <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> <persistence-unit name="springdataPU" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <class>com.huongdanjava.springdatajpa.Student</class> <exclude-unlisted-classes/> <properties> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.format_sql" value="false"/> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/> <property name="hibernate.connection.driver_class" value="com.mysql.cj.jdbc.Driver"/> <property name="javax.persistence.validation.mode" value="none"/> </properties> </persistence-unit> </persistence>
123456789101112131415161718 <?xml version="1.0"encoding="UTF-8"standalone="no"?><persistence version="2.1"xmlns="http://xmlns.jcp.org/xml/ns/persistence"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"><persistence-unit name="springdataPU"transaction-type="RESOURCE_LOCAL"><provider>org.hibernate.jpa.HibernatePersistenceProvider</provider><class>com.huongdanjava.springdatajpa.Student</class><exclude-unlisted-classes/><properties><property name="hibernate.show_sql"value="true"/><property name="hibernate.format_sql"value="false"/><property name="hibernate.dialect"value="org.hibernate.dialect.MySQLDialect"/><property name="hibernate.connection.driver_class"value="com.mysql.cj.jdbc.Driver"/><property name="javax.persistence.validation.mode"value="none"/></properties></persistence-unit></persistence>

Khai báo EntityManagerFactory trong Spring container:

XHTML <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="persistenceUnitName" value="springdataPU" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" /> </property> </bean>
1234567 <bean id="entityManagerFactory"class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"><property name="dataSource"ref="dataSource" /><property name="persistenceUnitName"value="springdataPU" /><property name="jpaVendorAdapter"><bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" /></property></bean>

với datasource:

XHTML <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/jpa_example" /> <property name="username" value="root" /> <property name="password" value="123456" /> </bean>
123456 <bean id="dataSource"class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName"value="com.mysql.cj.jdbc.Driver" /><property name="url"value="jdbc:mysql://localhost:3306/jpa_example" /><property name="username"value="root" /><property name="password"value="123456" /></bean>

OK, vậy là đã chuẩn bị mọi thứ xong rồi đó các bạn:

Tổng quan về Spring Data JPA

Các bạn có thể tham khảo thêm bài viết về JPA và Spring framework tại đây nhé!

Giờ chúng ta đi vào chủ đề chính của bài viết này thôi.

Điều đầu tiên, các bạn cần phải biết là Spring Data JPA là một module nhỏ trong một project lớn gọi là Spring Data project. Mục đích của Spring Data project là giảm thiểu các đoạn code lặp đi lặp lại liên quan đến phần thao tác với các hệ thống quản trị data khi phát triển các ứng dụng có sử dụng Spring framework. Ngoài Spring Data JPA hỗ trợ cho JPA giảm thiểu code để truy cập và thao tác với các hệ thống quản trị cơ cở dữ liệu, chúng ta còn có Spring Data JDBC (cũng giống như Spring Data JPA), Spring Data LDAP (hỗ trợ Spring LDAP), Spring Data MongoDB (hỗ trợ cho MongoDB),… Các bạn có thể tìm thấy đầy đủ các module của Spring Data tại đây.

Để đạt được mục đích giảm thiểu code như mình đã nói, Spring Data định nghĩa một interface chính tên là Repository nằm trong module Spring Data Common, module này sẽ được sử dụng cho tất cả các module còn lại trong Spring Data project. Nội dung của interface này đơn giản như sau:

Java package org.springframework.data.repository; import org.springframework.stereotype.Indexed; @Indexed public interface Repository<T, ID> { }
12345678 packageorg.springframework.data.repository; importorg.springframework.stereotype.Indexed; @IndexedpublicinterfaceRepository<T,ID>{ }

Interface này sử dụng 2 generic type:

  • T là domain class mà repository sẽ quản lý
  • ID là kiểu dữ liệu của ID của domain class mà repository quản lý.

Vì interface này đơn giản như vậy nên chắc chắn nó phải có những interface khác extend nó để thể hiện Spring Data project làm được những gì phải không các bạn? 😀

Ở đây, chúng ta sẽ có nhiều interface khác extend từ interface repository tuỳ thuộc vào module mà chúng ta sử dụng nhưng do chúng ta đang thảo luận về Spring Data JPA nên mình sẽ chỉ liệt kê ở đây một interface duy nhất extend interface Repository mà Spring Data JPA đang sử dụng. Đó chính là interface CrudRepository.

Interface CrudRepository với ý nghĩa create, read, update, delete cho phép chúng ta thực hiện các thao tác cơ bản đến với các hệ thống data, hệ thống data ở đây các bạn phải hiểu theo nghĩa rộng nghĩa là nó không chỉ là database đâu nhé các bạn.

Để hỗ trợ việc phân trang và sắp xếp cho Spring Data JPA, chúng ta còn phải nói đến một interface khác là PagingAndSortingRepository.

Tất cả các interface mà mình vừa kể trên đều nằm trong module Spring Data Common nhé các bạn.

Trong Spring Data JPA, ở đây, chúng ta chỉ có duy nhất một interface là JpaRepository kế thừa interface PagingAndSortingRepository. Với việc extend từ interface PagingAndSortingRepository, các bạn cũng có thể hình dung là Spring Data JPA có thể giúp chúng ta giảm thiểu code cho các thao tác nào liên quan đến database rồi phải không các bạn? Có thể kể ra đây là: create, read, update, delete, paging và sort. 😀

Các bạn có thể thấy rõ cấu trúc extend cho những interface trên bằng hình ảnh sau:

Tổng quan về Spring Data JPA

OK, vậy giờ hãy cùng mình làm một ví dụ với interface JpaRepository xem nó hoạt động như thế nào nhé các bạn.

Bây giờ mình sẽ tạo mới một interface tên là HelloRepository extend từ Jpa Repository với nội dung như sau:

Java package com.huongdanjava.springdatajpa; import org.springframework.data.jpa.repository.JpaRepository; public interface HelloRepository extends JpaRepository<Student, Long> { }
1234567 packagecom.huongdanjava.springdatajpa; importorg.springframework.data.jpa.repository.JpaRepository; publicinterfaceHelloRepository extendsJpaRepository<Student,Long>{ }

Domain class ở đây chính là tên entity Student còn Long chính là kiểu dữ liệu của properties trong entity Student mapping với column primary key trong table student nha các bạn.

Giờ chúng ta sẽ khai báo interface này trong Spring container như sau:

XHTML <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <jpa:repositories base-package="com.huongdanjava.springdatajpa"/> <context:annotation-config/> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="persistenceUnitName" value="springdataPU"/> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/> </property> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/jpa_example"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean> </beans>
1234567891011121314151617181920212223242526272829 <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:jpa="http://www.springframework.org/schema/data/jpa"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <jpa:repositories base-package="com.huongdanjava.springdatajpa"/> <context:annotation-config/> <bean id="entityManagerFactory"class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"><property name="dataSource"ref="dataSource"/><property name="persistenceUnitName"value="springdataPU"/><property name="jpaVendorAdapter"><bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/></property></bean> <bean id="dataSource"class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName"value="com.mysql.cj.jdbc.Driver"/><property name="url"value="jdbc:mysql://localhost:3306/jpa_example"/><property name="username"value="root"/><property name="password"value="123456"/></bean> </beans>

Ở đây, mình sử dụng namespace jpa:repositories với base package là com.huongdanjava.springdatajpa để scan HelloRepository interface. Với khai báo như trên, Spring Data JPA sẽ tự động lấy đối tượng implement cho interface HelloRepository là SimpleJpaRepository như các bạn thấy trong hình ảnh ở trên để khởi tạo trong Spring container.

Một điểm mà các bạn lưu ý là chúng ta cần khai báo tag <context:annotation-config/> như mình đã làm ở trên để Spring cấu hình đối tượng của class EntityPathResolver khi khởi tạo bean cho class JpaRepositoryFactoryBean nhé! Nếu không thì sẽ bị lỗi “EntityPathResolver must not be null”. Các bạn có thể xem phương thức setEntityPathResolver() với annotation @Autowired trong class JpaRepositoryFactoryBean để hiểu rõ thêm chỗ này.

Vì đối tượng implement SimpleJpaRepository sử dụng transaction nên các bạn cần phải khai báo thêm bean transactionManager vào Spring container các bạn nhé.

XHTML <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <jpa:repositories base-package="com.huongdanjava.springdatajpa"/> <context:annotation-config/> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="persistenceUnitName" value="springdataPU"/> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/> </property> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/jpa_example"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean> <tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> </beans>
1234567891011121314151617181920212223242526272829303132333435363738 <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:jpa="http://www.springframework.org/schema/data/jpa"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <jpa:repositories base-package="com.huongdanjava.springdatajpa"/> <context:annotation-config/> <bean id="entityManagerFactory"class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"><property name="dataSource"ref="dataSource"/><property name="persistenceUnitName"value="springdataPU"/><property name="jpaVendorAdapter"><bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/></property></bean> <bean id="dataSource"class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName"value="com.mysql.cj.jdbc.Driver"/><property name="url"value="jdbc:mysql://localhost:3306/jpa_example"/><property name="username"value="root"/><property name="password"value="123456"/></bean> <tx:annotation-driven proxy-target-class="true"transaction-manager="transactionManager"/> <bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource"ref="dataSource"/></bean> </beans>

OK, đến đây là chúng ta đã hoàn thành những phần cấu hình cần thiết. Giờ chạy ví dụ thử nhé các bạn.

Giả sử trong database mình đang có những dữ liệu sau đây:

Tổng quan về Spring Data JPA

thì khi chạy ví dụ sau đây:

Java package com.huongdanjava.springdatajpa; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Application { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml"); HelloRepository helloRepository = (HelloRepository) ac.getBean("helloRepository"); Student student = helloRepository.findById(new Long(2)).get(); System.out.println(student.getName()); } }
12345678910111213141516 packagecom.huongdanjava.springdatajpa; importorg.springframework.context.ApplicationContext;importorg.springframework.context.support.ClassPathXmlApplicationContext; publicclassApplication{ publicstaticvoidmain(String[]args){ApplicationContext ac=newClassPathXmlApplicationContext("spring.xml"); HelloRepository helloRepository=(HelloRepository)ac.getBean("helloRepository"); Student student=helloRepository.findById(newLong(2)).get();System.out.println(student.getName());}}

Kết quả sẽ là:

Tổng quan về Spring Data JPA

Giả sử giờ chúng ta thêm một phương thức trong interface HelloRepository có nội dung như sau:

Java package com.huongdanjava.springdatajpa; import org.springframework.data.jpa.repository.JpaRepository; public interface HelloRepository extends JpaRepository<Student, Long> { public Student findByName(String name); }
123456789 packagecom.huongdanjava.springdatajpa; importorg.springframework.data.jpa.repository.JpaRepository; publicinterfaceHelloRepository extendsJpaRepository<Student,Long>{ publicStudent findByName(Stringname); }

thì khi chạy ví dụ sau:

Java package com.huongdanjava.springdatajpa; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Application { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml"); HelloRepository helloRepository = (HelloRepository) ac.getBean("helloRepository"); Student student = helloRepository.findByName("Phong"); System.out.println(student.getId()); } }
1234567891011121314151617 packagecom.huongdanjava.springdatajpa; importorg.springframework.context.ApplicationContext;importorg.springframework.context.support.ClassPathXmlApplicationContext; publicclassApplication{ publicstaticvoidmain(String[]args){ApplicationContext ac=newClassPathXmlApplicationContext("spring.xml"); HelloRepository helloRepository=(HelloRepository)ac.getBean("helloRepository"); Student student=helloRepository.findByName("Phong"); System.out.println(student.getId());}}

kết quả sẽ là:

Tổng quan về Spring Data JPA

Như các bạn thấy, mặc dù không có bất kỳ class nào implement interface HelloRepository nhưng chúng ta vẫn truy vấn table student theo column name được. Giảm được rất nhiều code mà chúng ta cần phải làm phải không các bạn?

4/5 - (4 bình chọn)

Từ khóa » Jpa Spring Boot Là Gì