1. 简介

在本篇教程中,我们将学习如何通过 Spring Data JPA 的 @Query 注解来创建更新操作(UPDATE、DELETE 等)查询,并重点介绍配合使用的 @Modifying 注解。

如果你对 Spring Data JPA 查询还不太熟悉,可以先回顾一下 如何使用 Spring Data JPA 编写查询。之后我们会深入讲解 @Query@Modifying 注解的使用方式,最后还会聊聊在执行修改操作后如何管理持久化上下文的状态。

2. Spring Data JPA 中的查询机制

首先我们简单回顾一下 Spring Data JPA 提供的三种主要查询机制:

  • ✅ 查询方法(Query Methods)
  • ✅ 使用 @Query 注解自定义查询
  • ✅ 自定义 Repository 实现

为了演示这些机制,我们先定义一个简单的实体类 User 及其对应的 Repository 接口:

@Entity
@Table(name = "users", schema = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
    private LocalDate creationDate;
    private LocalDate lastLoginDate;
    private boolean active;
    private String email;

}
public interface UserRepository extends JpaRepository<User, Integer> {}

查询方法机制

通过命名派生查询的方法非常直观:

List<User> findAllByName(String name);
void deleteAllByCreationDateAfter(LocalDate date);

第一个方法会根据用户名查找用户;第二个则是删除创建时间晚于指定日期的所有用户。

使用 @Query 注解

对于更复杂的逻辑,我们可以使用 @Query 注解编写 JPQL 或原生 SQL 查询语句:

@Query("select u from User u where u.email like '%@gmail.com'")
List<User> findUsersWithGmailAddress();

这段代码的作用是找出所有使用 Gmail 邮箱的用户。

前两种机制都能满足大部分读取和删除场景,但当我们需要执行 更新(UPDATE)、插入(INSERT)、删除(DELETE)甚至是 DDL 操作时,就需要引入 @Modifying 注解了。

3. 使用 @Modifying 注解

@Modifying 注解的作用就是增强 @Query 注解的能力,使其不仅支持 SELECT 查询,还支持 INSERT、UPDATE、DELETE 和 DDL 操作。

来看几个例子:

示例一:更新用户状态

@Modifying
@Query("update User u set u.active = false where u.lastLoginDate < :date")
void deactivateUsersNotLoggedInSince(@Param("date") LocalDate date);

该方法将所有最后一次登录时间早于给定日期的用户设为非活跃状态。

示例二:批量删除非活跃用户

@Modifying
@Query("delete User u where u.active = false")
int deleteDeactivatedUsers();

这个方法返回被删除的记录数,这是 Spring Data JPA 在执行 @Modifying 查询时提供的特性之一。

⚠️ 注意:这种方式与通过方法名派生的 deleteByXXX() 方法不同。后者会先从数据库加载实体再逐个删除,因此会触发实体生命周期中的 @PreRemove 回调。而上面这种方式只是直接执行一条 SQL 删除语句。

示例三:添加新字段(DDL)

@Modifying
@Query(value = "alter table USERS.USERS add column deleted int(1) not null default 0", nativeQuery = true)
void addDeletedColumn();

这是一个原生 SQL 的 DDL 操作示例,用于给表添加一个 deleted 字段。

❌ 不加 @Modifying 会发生什么?

如果我们尝试在不加 @Modifying 的情况下执行修改操作,比如:

@Query("delete User u where u.active = false")
int deleteDeactivatedUsersWithNoModifyingAnnotation();

运行时会抛出异常:

org.springframework.dao.InvalidDataAccessApiUsageException: 
org.hibernate.hql.internal.QueryExecutionRequestException: 
Not supported for DML operations [delete com.baeldung.boot.domain.User u where u.active = false]

错误信息很明确:当前操作不支持 DML 操作。

4. 管理持久化上下文状态

当我们在执行修改操作时,如果这些实体已经在持久化上下文中存在,那么此时的上下文就可能已经过期了。

清理上下文:clearAutomatically = true

为了避免这个问题,我们可以在 @Modifying 注解上加上 clearAutomatically = true 属性:

@Modifying(clearAutomatically = true)

✅ 这样每次执行完查询后都会自动清理持久化上下文,确保下次获取的是最新的数据。

刷新上下文:flushAutomatically = true

但如果上下文中还有未提交的更改,直接清理会导致这些更改丢失。这时可以结合使用 flushAutomatically = true

@Modifying(flushAutomatically = true, clearAutomatically = true)

✅ 这样会在执行查询前自动刷新上下文,确保之前的数据变更也被同步到数据库中。

5. 总结

本文简要介绍了 Spring Data JPA 中的 @Modifying 注解及其典型应用场景。我们学会了如何使用它来执行更新操作(包括 UPDATE、DELETE 和 DDL),并了解了如何通过 clearAutomaticallyflushAutomatically 属性来维护持久化上下文的一致性。

完整代码可以在 GitHub 上找到:https://github.com/eugenp/tutorials/tree/master/persistence-modules/spring-data-jpa-simple


原始标题:Spring Data JPA @Modifying Annotation