1. 概述

在 Spring Data JPA 中,对于一些简单的查询逻辑,我们可以通过方法名来推导出对应的 SQL 查询语句,而无需手动编写 JPQL 或原生 SQL。

这种机制被称为 Derived Query Methods(派生查询方法),是 Spring Data JPA 提供的一种非常便捷的查询方式。本文将带你系统性地了解如何利用方法命名规则来构建查询逻辑。


2. 派生查询方法的命名结构

一个派生查询方法的命名通常由两部分组成,中间以 By 分隔:

  • Introducer(引导词):方法的起始动词,如 find, read, query, count, get 等。
  • Criteria(查询条件):紧跟 By 之后的部分,表示查询的条件表达式。

示例:

List<User> findByName(String name);
  • find 是 introducer
  • Name 是 criteria,表示按 name 查询

✅ Spring Data 支持多种 introducer,例如:

Introducer 含义
find 返回匹配结果列表
read 同 find
get 同 find
query 同 find
count 返回匹配结果数量

你也可以使用 Top, First, Distinct 等关键字来控制结果集:

List<User> findTop3ByAge();

这表示按年龄查询,返回最多 3 条记录。


3. 示例项目搭建

为了演示派生查询方法的使用,我们先定义一个简单的实体类:

@Table(name = "users")
@Entity
class User {
    @Id
    @GeneratedValue
    private Integer id;

    private String name;
    private Integer age;
    private ZonedDateTime birthDate;
    private Boolean active;

    // standard getters and setters
}

接着定义一个继承 JpaRepository 的 Repository 接口:

interface UserRepository extends JpaRepository<User, Integer> {}

我们将在该接口中添加各种派生查询方法。


4. 等值条件关键字

等值匹配是最常用的查询条件之一,Spring Data JPA 提供了多种写法来表达 =IS 操作符。

✅ 基本写法

List<User> findByName(String name);

✅ 增强可读性的写法

List<User> findByNameIs(String name);
List<User> findByNameEquals(String name);

❌ 不推荐的写法

List<User> findByNameNot(String name); // 不够直观

✅ 更推荐的写法(可读性更强)

List<User> findByNameIsNot(String name);

✅ 处理 null 值

当参数为 null 时,Spring Data JPA 会自动转换为 IS NULL

List<User> findByNameIsNull();
List<User> findByNameIsNotNull();

⚠️ 注意:IsNullIsNotNull 不需要参数。

✅ 布尔值匹配

对于 boolean 类型字段,可以使用 TrueFalse

List<User> findByActiveTrue();
List<User> findByActiveFalse();

5. 模糊匹配关键字

当我们需要根据模式匹配查询时,可以使用以下关键字:

关键字 SQL 等价表达式 示例
StartingWith LIKE 'value%' findByNameStartingWith("Tom")
EndingWith LIKE '%value' findByNameEndingWith("Tom")
Containing LIKE '%value%' findByNameContaining("Tom")

示例:

List<User> findByNameStartingWith(String prefix);
List<User> findByNameEndingWith(String suffix);
List<User> findByNameContaining(String infix);

✅ 自定义 LIKE 表达式

如果你需要更复杂的模式匹配,可以直接使用 Like

List<User> findByNameLike(String likePattern);

调用时传入完整的 LIKE 模式:

String likePattern = "a%b%c";
userRepository.findByNameLike(likePattern);

6. 比较操作关键字

除了等值和模糊匹配,我们还可以使用比较操作符进行查询。

关键字 SQL 等价表达式 示例
LessThan / Lt < value findByAgeLessThan(18)
LessThanEqual / Le <= value findByAgeLessThanEqual(18)
GreaterThan / Gt > value findByAgeGreaterThan(18)
GreaterThanEqual / Ge >= value findByAgeGreaterThanEqual(18)
Between BETWEEN low AND high findByAgeBetween(10, 20)
In IN (value1, value2, ...) findByAgeIn(Arrays.asList(10, 20))
Before / After < / > 时间 findByBirthDateBefore(ZonedDateTime.now())

示例:

List<User> findByAgeLessThan(Integer age);
List<User> findByAgeBetween(Integer startAge, Integer endAge);
List<User> findByBirthDateAfter(ZonedDateTime birthDate);

7. 多条件组合查询

我们可以通过 AndOr 关键字组合多个查询条件。

✅ 示例

List<User> findByNameOrAge(String name, Integer age);
List<User> findByNameOrAgeAndActive(String name, Integer age, Boolean active);

⚠️ 注意

  • 条件之间的优先级是:And 优先于 Or,类似于 Java 中的逻辑运算符。
  • 不建议写过长的方法名,否则难以维护。

✅ 建议复杂查询使用 @Query 注解替代:

@Query("SELECT u FROM User u WHERE u.name LIKE %:name% AND u.age > :age")
List<User> customQuery(@Param("name") String name, @Param("age") Integer age);

8. 排序结果

我们可以使用 OrderBy 来对查询结果进行排序。

✅ 示例

List<User> findByNameOrderByName(String name);
List<User> findByNameOrderByNameAsc(String name);
List<User> findByNameOrderByNameDesc(String name);

默认是升序排序(Asc),也可以显式指定降序(Desc)。


9. findOne vs findById 的变化

在 Spring Boot 2.x 中,CrudRepository 接口进行了重构:

  • 旧版(Spring Boot 1.x)使用 findOne(id)

    User user = userRepository.findOne(1);
    
  • 新版(Spring Boot 2.x)改为 findById(id)

    User user = userRepository.findById(1);
    

⚠️ 注意:findById() 方法已经在 CrudRepository 中定义好,无需在自定义 Repository 中重复声明。


10. 总结

Spring Data JPA 的派生查询方法通过方法命名规则,为我们提供了一种简洁、直观的方式来构建查询逻辑。

✅ 优点:

  • 无需手动编写 SQL 或 JPQL
  • 方法名即文档,易于维护
  • 支持丰富的条件表达式

⚠️ 缺点:

  • 对于复杂查询,方法名会变得冗长且难以维护

✅ 建议:

  • 简单查询使用派生方法
  • 复杂查询使用 @Query 注解或自定义 SQL

完整源码可在 GitHub 上查看。


原始标题:Derived Query Methods in Spring Data JPA Repositories