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
是 introducerName
是 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();
⚠️ 注意:IsNull
和 IsNotNull
不需要参数。
✅ 布尔值匹配
对于 boolean 类型字段,可以使用 True
和 False
:
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. 多条件组合查询
我们可以通过 And
和 Or
关键字组合多个查询条件。
✅ 示例
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 上查看。