本文主要介绍 Spring Boot 中如何使用 Sping Data JPA,相关的环境及软件信息如下:Spring Boot 2.6.10。
1、Sping Data JPA 简介
Spring Data JPA 是 Spring Data 家族的一部分,它对基于 JPA 的数据访问提供了增强支持,让 JPA 更加易用。 它使得使用 Spring 构建的应用程序访问数据库变得更加容易。
编写应用程序的数据访问层是一件很麻烦的事, 必须编写太多样板代码来执行简单的查询以及分页和审计。 Spring Data JPA 旨在通过减工作量来显着改进数据访问层的实现。 作为开发人员,编写数据访问层接口,包括自定义查询方法,Spring 将自动提供实现。
1.1、Spring Data JPA 的特点
- Sophisticated support to build repositories based on Spring and JPA
- Support for Querydsl predicates and thus type-safe JPA queries
- Transparent auditing of domain class
- Pagination support, dynamic query execution, ability to integrate custom data access code
- Validation of @Query annotated queries at bootstrap time
- Support for XML based entity mapping
- JavaConfig based repository configuration by introducing @EnableJpaRepositories.
1.2、Spring Data JPA 与 Hibernate 的关系
Spring Data JPA 是 Spring 提供的一套对 JPA 操作更加高级的封装,底层依赖 Hibernate 的 JPA 实现。
2、JpaRepository 和 JpaSpecificationExecutor 接口
Spring Data JPA 提供了 JpaRepository 和 JpaSpecificationExecutor 接口,通过它们可以快速定义 DAO 接口,Spring Data JPA 会自动实现该接口。
2.1、JpaRepository
JpaRepository 接口内置了很多方法:

如果不满足业务需求,可以自定义方法。
2.1.1、使用 Query 注解
org.springframework.data.jpa.repository.Query 注解可以定义使用 JPQL 或 SQL 操作数据。 - /**
- * 通过 JPQL 查询
- */
- @Query("from Student where name=?1 and age=?2")
- Student query(String name, Integer age);
- /**
- * SQL 语句查询数据
- */
- @Query(value = "select * from a_student where name=? and age=?", nativeQuery = true)
- Student queryBySql(String name, Integer age);
复制代码 2.1.1、按照 Spring Data JPA 规范定义方法名称
Spring Data JPA 提供一些关键词,通过这些关键词和实体属性名称来组装方法名称。主题关键词:KeywordDescriptionfind…By, read…By, get…By, query…By, search…By, stream…By
General query method returning typically the repository type, a Collection or Streamable subtype or a result wrapper such as Page, GeoResults or any other store-specific result wrapper. Can be used as findBy…, findMyDomainTypeBy… or in combination with additional keywords.
exists…By
Exists projection, returning typically a boolean result.
count…By
Count projection returning a numeric result.
delete…By, remove…By
Delete query method returning either no result (void) or the delete count.
…First…, …Top…
Limit the query results to the first of results. This keyword can occur in any place of the subject between find (and the other keywords) and by.
…Distinct…
Use a distinct query to return only unique results. Consult the store-specific documentation whether that feature is supported. This keyword can occur in any place of the subject between find (and the other keywords) and by.
条件关键词:Logical keywordKeyword expressionsAND
And
OR
Or
AFTER
After, IsAfter
BEFORE
Before, IsBefore
CONTAINING
Containing, IsContaining, Contains
BETWEEN
Between, IsBetween
ENDING_WITH
EndingWith, IsEndingWith, EndsWith
EXISTS
Exists
FALSE
False, IsFalse
GREATER_THAN
GreaterThan, IsGreaterThan
GREATER_THAN_EQUALS
GreaterThanEqual, IsGreaterThanEqual
IN
In, IsIn
IS
Is, Equals, (or no keyword)
IS_EMPTY
IsEmpty, Empty
IS_NOT_EMPTY
IsNotEmpty, NotEmpty
IS_NOT_NULL
NotNull, IsNotNull
IS_NULL
Null, IsNull
LESS_THAN
LessThan, IsLessThan
LESS_THAN_EQUAL
LessThanEqual, IsLessThanEqual
LIKE
Like, IsLike
NEAR
Near, IsNear
NOT
Not, IsNot
NOT_IN
NotIn, IsNotIn
NOT_LIKE
NotLike, IsNotLike
REGEX
Regex, MatchesRegex, Matches
STARTING_WITH
StartingWith, IsStartingWith, StartsWith
TRUE
True, IsTrue
WITHIN
Within, IsWithin
详细说明可参考官网文档:https://docs.spring.io/spring-data/jpa/docs/2.6.6/reference/html/#repository-query-keywords。命名样例:方法名 | JPQL | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 | findByStartDateBetween | … where x.startDate between ?1 and ?2 | findByAgeLessThan | … where x.age < ?1 | findByAgeLessThanEqual | … where x.age ⇐ ?1 | 2.2、JpaSpecificationExecutor
JpaSpecificationExecutor 接口主要用来在 JpaRepository 接口无法满足要求时,可以使用 JPA 的标准 API 来操作数据。

3、Sping Data JPA 使用
这里演示下 Spring Data JPA 的基本使用,工程目录结构如下:

3.1、引入依赖
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.6.10</version>
- <relativePath />
- </parent>
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-jpa</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>8.0.29</version>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- <version>1.18.24</version>
- <scope>provided</scope>
- </dependency>
- </dependencies>
复制代码 3.2、创建实体类
- package com.abc.demojpa.entity;
- import lombok.Data;
- import lombok.NoArgsConstructor;
- import org.hibernate.annotations.CreationTimestamp;
- import org.hibernate.annotations.UpdateTimestamp;
- import javax.persistence.*;
- import java.time.LocalDateTime;
- @NoArgsConstructor
- @Data
- @Entity
- @Table(name = "a_student")
- public class Student {
- @Id
- @GeneratedValue(strategy = GenerationType.AUTO)
- private Long id;
- @CreationTimestamp
- @Column(name = "create_time")
- private LocalDateTime createTime;
- @UpdateTimestamp
- @Column(name = "modify_time")
- private LocalDateTime modifyTime;
- private String name;
- private Integer age;
- @Column(name = "home_address")
- private String homeAddress;
- }
复制代码 3.3、编写配置文件(application.yml)
- spring:
- datasource:
- driver-class-name: com.mysql.cj.jdbc.Driver
- url: jdbc:mysql://10.49.196.10:3306/test?useUnicode=true&characterEncoding=UTF-8
- username: root
- password: 123456
- jpa:
- hibernate:
- ddl-auto: update
复制代码 3.4、定义 DAO 接口
- package com.abc.demojpa.dao;
- import com.abc.demojpa.entity.Student;
- import org.springframework.data.jpa.repository.JpaRepository;
- import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
- import org.springframework.data.jpa.repository.Modifying;
- import org.springframework.data.jpa.repository.Query;
- import org.springframework.data.repository.query.Param;
- import java.util.List;
- public interface IStudentRepository extends JpaRepository<Student, Long>, JpaSpecificationExecutor<Student> {
- /**
- * 根据姓名和年龄查询
- */
- List<Student> findByNameAndAge(String name, Integer age);
- /**
- * 根据姓名和年龄查询前5条
- */
- List<Student> findTop5ByNameAndAge(String name, Integer age);
- /**
- * 根据姓名和年龄查询并按id排序
- */
- List<Student> findByNameAndAgeOrderByIdAsc(String name, Integer age);
- /**
- * 根据姓名和年龄查询第一条
- */
- Student findFirstByNameAndAge(String name, Integer age);
- /**
- * 通过 JPQL 查询
- */
- @Query("from Student where name=?1 and age=?2")
- Student query(String name, Integer age);
- /**
- * 通过 JPQL 查询
- */
- @Query("from Student where name=:name and age=:age")
- Student query2(@Param("name") String name, @Param("age") Integer age);
- /**
- * 通过 JPQL 更新数据
- */
- @Query("update Student set homeAddress=?1 where id=?2")
- @Modifying
- void updateHomeAddress(String homeAddress, Long id);
- /**
- * SQL 语句查询数据
- */
- @Query(value = "select * from a_student where name=? and age=?", nativeQuery = true)
- Student queryBySql(String name, Integer age);
- }
复制代码 3.5、编写启动类
- package com.abc.demojpa;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
- @EnableJpaAuditing
- @SpringBootApplication
- public class DemoJpaApplication {
- public static void main(String[] args) {
- SpringApplication.run(DemoJpaApplication.class, args);
- }
- }
复制代码 3.6、编写测试用例
3.6.1、增加数据
- /**
- * 增加数据
- * Spring Boot 事务测试时默认会回滚操作避免产生测试数据,如果不需要回滚可使用 @Commit 注解
- */
- @Test
- @Transactional
- @Commit
- public void add() {
- Student student = new Student();
- student.setName("小明");
- student.setAge(15);
- student.setHomeAddress("江苏");
- Student student2 = new Student();
- student2.setName("小红");
- student2.setAge(18);
- student2.setHomeAddress("广东");
- studentRepository.save(student);
- studentRepository.save(student2);
- }
复制代码 3.6.2、修改数据
- @Transactional
- @Commit
- @Test
- public void modify() {
- Student student = new Student();
- student.setId(3L);
- student.setName("小明2");
- student.setAge(15);
- student.setHomeAddress("江苏");
- //设置了 id 表示更新数据
- studentRepository.save(student);
- //调用自定义的更新方法
- studentRepository.updateHomeAddress("上海", 12L);
- }
复制代码 3.6.3、查询数据
- @Test
- public void query() {
- //根据 id 查询
- Optional<Student> optional = studentRepository.findById(12L);
- logger.info("student={}", optional.get());
- //查询所有
- List<Student> list = studentRepository.findAll();
- logger.info("list={}", list);
- //分页及排序查询
- List<Sort.Order> orders = new ArrayList<>();
- orders.add(new Sort.Order(Sort.Direction.DESC, "id"));
- orders.add(new Sort.Order(Sort.Direction.ASC, "name"));
- Page<Student> page = studentRepository.findAll(PageRequest.of(0, 10, Sort.by(orders)));
- logger.info("page.getTotalElements={},page.getTotalPages()={},page.toList()={}", page.getTotalElements(), page.getTotalPages(), page.toList());
- //通过 JPA 标准 API 查询,可以用来动态拼接查询条件
- Specification<Student> specification = new Specification<Student>() {
- @Override
- public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
- List<Predicate> list = new ArrayList<>();
- list.add(criteriaBuilder.equal(root.get("name"), "小红"));
- list.add(criteriaBuilder.ge(root.get("age"), 10));
- return criteriaBuilder.and(list.toArray(new Predicate[]{}));
- }
- };
- list = studentRepository.findAll(specification);
- logger.info("list={}", list);
- list = studentRepository.findByNameAndAge("小红", 18);
- logger.info("list={}", list);
- list = studentRepository.findTop5ByNameAndAge("小红", 18);
- logger.info("list={}", list);
- list = studentRepository.findByNameAndAgeOrderByIdAsc("小红", 18);
- logger.info("list={}", list);
- Student student = studentRepository.findFirstByNameAndAge("小红", 18);
- logger.info("student={}", student);
- student = studentRepository.query("小红", 18);
- logger.info("student={}", student);
- student = studentRepository.query2("小红", 18);
- logger.info("student={}", student);
- student = studentRepository.queryBySql("小红", 18);
- logger.info("student={}", student);
- }
复制代码 3.6.4、删除数据
- @Test
- public void remove() {
- //根据实体删除
- Student student = new Student();
- student.setId(3L);
- studentRepository.delete(student);
- List<Student> students = new ArrayList<>();
- Student student2 = new Student();
- student.setId(4L);
- students.add(student);
- students.add(student2);
- studentRepository.deleteAll(students);
- //根据 id 删除
- studentRepository.deleteById(5L);
- List<Long> ids = new ArrayList<Long>(){{
- add(6L);
- add(7L);
- }};
- studentRepository.deleteAllByIdInBatch(ids);
- }
复制代码 3.6.5、完整代码
  - package com.abc.demojpa.service;
- import com.abc.demojpa.dao.IStudentRepository;
- import com.abc.demojpa.entity.Student;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.test.context.SpringBootTest;
- import org.springframework.data.domain.Page;
- import org.springframework.data.domain.PageRequest;
- import org.springframework.data.domain.Sort;
- import org.springframework.data.jpa.domain.Specification;
- import org.springframework.test.annotation.Commit;
- import org.springframework.test.context.junit4.SpringRunner;
- import org.springframework.transaction.annotation.Transactional;
- import javax.persistence.criteria.CriteriaBuilder;
- import javax.persistence.criteria.CriteriaQuery;
- import javax.persistence.criteria.Predicate;
- import javax.persistence.criteria.Root;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Optional;
- @RunWith(SpringRunner.class)
- @SpringBootTest
- public class TestService {
- private static final Logger logger = LoggerFactory.getLogger(TestService.class);
- @Autowired
- private IStudentRepository studentRepository;
- /**
- * 增加数据
- * Spring Boot 事务测试时默认会回滚操作避免产生测试数据,如果不需要回滚可使用 @Commit 注解
- */
- @Test
- @Transactional
- @Commit
- public void add() {
- Student student = new Student();
- student.setName("小明");
- student.setAge(15);
- student.setHomeAddress("江苏");
- Student student2 = new Student();
- student2.setName("小红");
- student2.setAge(18);
- student2.setHomeAddress("广东");
- studentRepository.save(student);
- studentRepository.save(student2);
- }
- /**
- * 修改数据
- */
- @Transactional
- @Commit
- @Test
- public void modify() {
- Student student = new Student();
- student.setId(3L);
- student.setName("小明2");
- student.setAge(15);
- student.setHomeAddress("江苏");
- //设置了 id 表示更新数据
- studentRepository.save(student);
- //调用自定义的更新方法
- studentRepository.updateHomeAddress("上海", 12L);
- }
- /**
- * 查询数据
- */
- @Test
- public void query() {
- //根据 id 查询
- Optional<Student> optional = studentRepository.findById(12L);
- logger.info("student={}", optional.get());
- //查询所有
- List<Student> list = studentRepository.findAll();
- logger.info("list={}", list);
- //分页及排序查询
- List<Sort.Order> orders = new ArrayList<>();
- orders.add(new Sort.Order(Sort.Direction.DESC, "id"));
- orders.add(new Sort.Order(Sort.Direction.ASC, "name"));
- Page<Student> page = studentRepository.findAll(PageRequest.of(0, 10, Sort.by(orders)));
- logger.info("page.getTotalElements={},page.getTotalPages()={},page.toList()={}", page.getTotalElements(), page.getTotalPages(), page.toList());
- //通过 JPA 标准 API 查询,可以用来动态拼接查询条件
- Specification<Student> specification = new Specification<Student>() {
- @Override
- public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
- List<Predicate> list = new ArrayList<>();
- list.add(criteriaBuilder.equal(root.get("name"), "小红"));
- list.add(criteriaBuilder.ge(root.get("age"), 10));
- return criteriaBuilder.and(list.toArray(new Predicate[]{}));
- }
- };
- list = studentRepository.findAll(specification);
- logger.info("list={}", list);
- list = studentRepository.findByNameAndAge("小红", 18);
- logger.info("list={}", list);
- list = studentRepository.findTop5ByNameAndAge("小红", 18);
- logger.info("list={}", list);
- list = studentRepository.findByNameAndAgeOrderByIdAsc("小红", 18);
- logger.info("list={}", list);
- Student student = studentRepository.findFirstByNameAndAge("小红", 18);
- logger.info("student={}", student);
- student = studentRepository.query("小红", 18);
- logger.info("student={}", student);
- student = studentRepository.query2("小红", 18);
- logger.info("student={}", student);
- student = studentRepository.queryBySql("小红", 18);
- logger.info("student={}", student);
- }
- /**
- * 删除数据
- */
- @Test
- public void remove() {
- //根据实体删除
- Student student = new Student();
- student.setId(3L);
- studentRepository.delete(student);
- List<Student> students = new ArrayList<>();
- Student student2 = new Student();
- student.setId(4L);
- students.add(student);
- students.add(student2);
- studentRepository.deleteAll(students);
- //根据 id 删除
- studentRepository.deleteById(5L);
- List<Long> ids = new ArrayList<Long>(){{
- add(6L);
- add(7L);
- }};
- studentRepository.deleteAllByIdInBatch(ids);
- }
- }
复制代码 TestService.java
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |