1. 概述
本文将探讨 Spring Data JPA 中的删除操作,包括如何通过 Repository 删除实体、使用派生删除查询、自定义删除语句,以及处理实体之间的关联关系时的删除行为。
如果你已经对 Spring Data JPA 有基本了解,并且在项目中使用过 CRUD 操作,那么这篇文章会帮助你更好地掌握删除操作的细节,避免踩坑。
2. 示例实体
根据 Spring Data JPA 官方文档,我们可以通过继承 CrudRepository
接口快速获得对实体的增删改查能力。
我们以 Book
实体为例:
@Entity
public class Book {
@Id
@GeneratedValue
private Long id;
private String title;
// 构造函数、getter、setter 省略
}
然后定义一个 BookRepository
:
@Repository
public interface BookRepository extends CrudRepository<Book, Long> {}
这样我们就拥有了对 Book
实体的基本操作能力。
3. 通过 Repository 删除
CrudRepository
提供了两个删除方法:
deleteById(ID id)
deleteAll()
我们来写一个单元测试验证删除行为:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class})
public class DeleteFromRepositoryUnitTest {
@Autowired
private BookRepository repository;
Book book1;
Book book2;
List<Book> books;
// 初始化数据
@Test
public void whenDeleteByIdFromRepository_thenDeletingShouldBeSuccessful() {
repository.deleteById(book1.getId());
assertThat(repository.count()).isEqualTo(1);
}
@Test
public void whenDeleteAllFromRepository_thenRepositoryShouldBeEmpty() {
repository.deleteAll();
assertThat(repository.count()).isEqualTo(0);
}
}
⚠️ 注意:这些方法在 JpaRepository
和 PagingAndSortingRepository
中也存在。
4. 派生删除查询
除了基本的删除方法,我们还可以通过方法名派生删除逻辑。
命名规则是:deleteBy + 条件字段名
。
比如我们想根据 title
删除书籍:
@Repository
public interface BookRepository extends CrudRepository<Book, Long> {
long deleteByTitle(String title);
}
返回值类型为 long
,表示删除了多少条记录。
测试代码如下:
@Test
@Transactional
public void whenDeleteFromDerivedQuery_thenDeletingShouldBeSuccessful() {
long deletedRecords = repository.deleteByTitle("The Hobbit");
assertThat(deletedRecords).isEqualTo(1);
}
✅ 注意:删除操作需要事务支持,所以必须加上 @Transactional
注解。
5. 自定义删除语句
当派生方法无法满足需求时,可以使用 @Query
和 @Modifying
自定义删除语句。
比如我们想删除特定标题的书籍:
@Modifying
@Query("delete from Book b where b.title=:title")
void deleteBooks(@Param("title") String title);
测试代码如下:
@Test
@Transactional
public void whenDeleteFromCustomQuery_thenDeletingShouldBeSuccessful() {
repository.deleteBooks("The Hobbit");
assertThat(repository.count()).isEqualTo(1);
}
⚠️ 两者对比:
@Query
是一次性执行删除语句deleteBy
是先查询出数据,再逐个删除
6. 删除与关联关系
当实体之间存在关联关系时,删除行为会受到级联设置的影响。
我们以 Category
和 Book
之间的 OneToMany
关系为例:
@Entity
public class Category {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "category", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Book> books;
// 构造函数、getter、setter 省略
}
同时修改 Book
实体:
@ManyToOne
private Category category;
并定义一个 CategoryRepository
:
@Repository
public interface CategoryRepository extends CrudRepository<Category, Long> {}
测试删除行为:
@Test
public void whenDeletingCategories_thenBooksShouldAlsoBeDeleted() {
categoryRepository.deleteAll();
assertThat(bookRepository.count()).isEqualTo(0);
assertThat(categoryRepository.count()).isEqualTo(0);
}
✅ 删除 Category
时,Book
也会被级联删除(因为我们设置了 cascade = CascadeType.ALL
)。
但反过来则不行:
@Test
public void whenDeletingBooks_thenCategoriesShouldAlsoBeDeleted() {
bookRepository.deleteAll();
assertThat(bookRepository.count()).isEqualTo(0);
assertThat(categoryRepository.count()).isEqualTo(2);
}
⚠️ 说明删除是单向的,除非你手动配置双向级联。
7. 总结
本文介绍了 Spring Data JPA 中几种常见的删除方式:
方法 | 说明 |
---|---|
deleteById / deleteAll |
最基础的删除方法 |
deleteByXxx |
派生删除查询,适合简单条件 |
@Query + @Modifying |
自定义删除语句,更灵活 |
级联删除 | 需配置 cascade 属性,影响关联实体行为 |
✅ 实际开发中,建议根据业务场景选择合适的删除方式,注意事务控制与级联设置,避免数据残留或误删。
所有代码示例已整理在 GitHub 上,地址为:https://github.com/eugenp/tutorials(路径:persistence-modules/spring-data-jpa-crud
)