1. 概述

在本教程中,我们将展示如何在 Spring Data JPA 中处理查询方法中的 null 参数

有些时候我们希望在查询中将 null 参数理解为 IS NULL 条件,用于查找字段为 null 的记录;而另一些时候,我们希望忽略 null 参数,不在 WHERE 子句中包含该条件

下面我们会分别演示这两种方式的实现。


2. 快速示例

假设我们有一个 Customer 实体类:

@Entity
public class Customer {

    @Id
    @GeneratedValue
    private long id;
    private String name;
    private String email;

    public Customer(String name, String email) {
        this.name = name;
        this.email = email;
    }

    // getters/setters
}

同时,我们有一个 JPA Repository:

public interface CustomerRepository extends JpaRepository<Customer, Long> { 

   // method1
   // method2
}

我们的目标是根据 nameemail 查询客户信息。我们会实现两个方法:

✅ 一个将 null 参数视为 IS NULL
✅ 另一个则忽略 null 参数,不将其加入查询条件中


3. Null 参数的处理方式

3.1. 使用 IS NULL 查询

Spring Data JPA 默认会将 null 参数解释为 IS NULL。这意味着如果你传入的参数为 null,生成的 JPQL 查询中会自动加上 IS NULL 条件。

我们只需定义如下方法:

List<Customer> findByNameAndEmail(String name, String email);

例如,传入 name = "D"email = null 时,生成的 JPQL 条件会是:

customer0_.email is null

测试代码如下:

@Before
public void before() {
    entityManager.persist(new Customer("A", "[email protected]"));
    entityManager.persist(new Customer("D", null));
    entityManager.persist(new Customer("D", "[email protected]"));
}

@Test
public void testFindByNullEmail() {
    List<Customer> customers = repository.findByNameAndEmail("D", null);
    assertEquals(1, customers.size());

    Customer actual = customers.get(0);
    assertEquals(null, actual.getEmail());
    assertEquals("D", actual.getName());
}

✅ 该方法简单粗暴,适合 null 有明确语义的场景。


3.2. 通过多个查询方法忽略 null 参数

如果我们想忽略某个参数(如 email),最直接的方式是添加一个只接受部分参数的方法:

List<Customer> findByName(String name);

⚠️ 但这种方式在参数组合多的情况下会变得难以维护。比如你有 4 个可选字段,组合就会爆炸式增长,导致代码冗余严重。


3.3. 使用 @Query 注解忽略 null 参数

更灵活的方式是使用 @Query 注解,手动控制 JPQL 查询语句,实现动态查询逻辑。

@Query("SELECT c FROM Customer c WHERE (:name is null or c.name = :name) and (:email is null or c.email = :email)")
List<Customer> findCustomerByNameAndEmail(
    @Param("name") String name,
    @Param("email") String email);

这段 JPQL 的逻辑是:

  • 如果 name 为 null,则 :name is null or c.name = :name 恒为 true
  • 同理,如果 email 为 null,该条件也恒为 true

测试代码如下:

List<Customer> customers = repository.findCustomerByNameAndEmail("D", null);
assertEquals(2, customers.size());

✅ 这样我们就能忽略 null 参数,查出所有 name 为 "D" 的记录,不管 email 是否为 null。

⚠️ 但是要注意,这种方式依赖数据库优化器来跳过不必要的条件,在大数据量或复杂查询下可能会有性能问题


4. 总结与展望

我们介绍了两种处理 null 参数的方式:

方式 行为 示例
默认行为 null 被解释为 IS NULL findByNameAndEmail("D", null)
自定义 JPQL 忽略 null 参数 使用 @Query 注解

✅ 默认行为适用于 null 有业务含义的场景,比如查找 email 为空的用户。
✅ 自定义 JPQL 更灵活,适合参数可选、组合多的场景。

未来可能会支持 @NullMeans 注解(目前还在提案中),可以更优雅地控制 null 的语义:

@NullMeans(NullHandling.IS_NULL)
List<Customer> findByName(String name);

完整代码示例可以在 GitHub 上查看:Spring Data JPA Filtering 示例


原始标题:Spring Data JPA and Null Parameters