1. 简介

Spring Data JPA 提供了非常实用的特性,允许我们通过简单的命名约定定义派生方法(Derived Methods),用于从数据库中读取、更新或删除数据。这大大减少了数据访问层的模板代码,提升开发效率。

本篇文章将聚焦在 Spring Data JPA 中如何定义和使用派生的删除方法(deleteBy / removeBy),并结合实际代码示例进行说明。


2. 派生 deleteBy 方法

我们以一个简单的实体类 Fruit 为例,用于表示水果店中的水果信息,包含名称和颜色:

@Entity
public class Fruit {
    @Id
    private long id;
    private String name;
    private String color;
    // standard getters and setters
}

接着定义对应的 Repository 接口,继承自 JpaRepository,并在其中定义派生删除方法:

@Repository
public interface FruitRepository extends JpaRepository<Fruit, Long> {
    Long deleteByName(String name);
}

如上所示,deleteByName 方法会根据名称删除记录,并返回被删除记录的数量。

我们也可以定义类似如下的方法:

List<Fruit> deleteByColor(String color);

该方法会删除指定颜色的所有水果,并返回被删除的列表。

✅ 示例测试

我们可以先准备测试数据,写入 test-fruit-data.sql 文件:

insert into fruit(id,name,color) values (1,'apple','red');
insert into fruit(id,name,color) values (2,'custard apple','green');
insert into fruit(id,name,color) values (3,'mango','yellow');
insert into fruit(id,name,color) values (4,'guava','green');

然后编写测试方法:

@Transactional
@Test
@Sql(scripts = { "/test-fruit-data.sql" })
public void givenFruits_WhenDeletedByColor_ThenDeletedFruitsShouldReturn() {
     List<Fruit> fruits = fruitRepository.deleteByColor("green");

     assertEquals("number of fruits are not matching", 2, fruits.size());
     fruits.forEach(fruit -> assertEquals("It's not a green fruit", "green", fruit.getColor()));
}

⚠️ 注意:删除操作必须加上 @Transactional 注解,否则会抛异常。

另一个测试方法用于验证返回值为 Long 的情况:

@Transactional
@Test
@Sql(scripts = { "/test-fruit-data.sql" })
public void givenFruits_WhenDeletedByName_ThenDeletedFruitCountShouldReturn() {

    Long deletedFruitCount = fruitRepository.deleteByName("apple");

    assertEquals("deleted fruit count is not matching", 1, deletedFruitCount.intValue());
}

3. 派生 removeBy 方法

除了 deleteBy,Spring Data JPA 也支持使用 removeBy 来定义删除方法:

Long removeByName(String name);
List<Fruit> removeByColor(String color);

这两种方法在行为上是完全一致的,只是命名不同。

完整的 Repository 接口如下:

@Repository
public interface FruitRepository extends JpaRepository<Fruit, Long> {

    Long deleteByName(String name);
    List<Fruit> deleteByColor(String color);
    Long removeByName(String name);
    List<Fruit> removeByColor(String color);
}

测试方法与前面类似,不再赘述。


4. 派生删除方法 vs @Query

在某些场景中,使用派生方法会导致方法名过长,或者涉及多个表的 JOIN 操作,这时候更适合使用 @Query@Modifying 注解自定义删除逻辑。

例如,等价于前面派生方法的自定义删除方法可以这样写:

@Modifying
@Query("delete from Fruit f where f.name=:name or f.color=:color")
List<Integer> deleteFruits(@Param("name") String name, @Param("color") String color);

✅ 对比说明:

特性 deleteBy / removeBy @Query + @Modifying
查询方式 先查后删,逐条删除 直接执行一条 JPQL 删除语句
性能 较低(逐条操作) 更高效(单条 SQL)
返回值 可返回被删除对象列表 返回受影响行数
灵活性 有限 支持复杂查询逻辑

5. 总结

本文介绍了 Spring Data JPA 中的派生删除方法(deleteBy / removeBy),并通过实际代码演示了其使用方式和测试方法。同时也对比了派生方法与自定义 @Query 删除操作的异同,帮助你根据具体业务场景选择合适的方式。

✅ 推荐优先使用派生方法,简单场景下开发效率高;
❌ 对于复杂查询或性能敏感场景,建议使用 @Query 自定义 JPQL 删除语句。

完整示例代码可参考:GitHub 项目地址(示例已 mock)


原始标题:Spring Data JPA - Derived Delete Methods