Spring Data JPA @Modifying注解

2023/05/18

1. 简介

在这个简短的教程中,我们将学习如何使用Spring Data JPA @Query注解创建更新查询。我们将通过使用@Modifying注解来实现这一点。

首先,作为回顾目的,我们可以阅读如何使用Spring Data JPA进行查询。之后,我们将深入探讨@Query和@Modifying注解的使用。最后,我们将讨论在使用修改查询时如何管理持久性上下文的状态。

2. 在Spring Data JPA中查询

首先,让我们回顾一下Spring Data JPA为查询数据库中的数据提供的三种机制

  • 查询方法
  • @Query注解
  • 自定义Repository实现

让我们创建一个User类和一个匹配的Spring Data JPA 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.com电子邮件地址的用户。

第一种机制使我们能够检索或删除数据。至于第二种机制,它允许我们执行几乎任何查询。但是,对于更新查询,我们必须添加@Modifying注解。这将是本教程的主题。

3. 使用@Modifying注解

@Modifying注解用于增强@Query注解,以便我们不仅可以执行SELECT查询,还可以执行INSERT、UPDATE、DELETE甚至DDL查询

首先,让我们看一个@Modifying UPDATE查询的例子:

@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查询的一个功能,它为我们提供了更新实体的数量

我们应该注意,使用@Query执行删除查询与Spring Data JPA的deleteBy名称派生查询方法不同。后者首先从数据库中获取实体,然后逐个删除它们。这意味着将在这些实体上调用生命周期方法@PreRemove。但是,对于前者,只对数据库执行单个查询。

最后,让我们使用DDL查询将已删除的列添加到我们的USERS表中:

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

不幸的是,使用修改查询会使底层的持久性上下文过时。但是,可以管理这种情况。这是下一节的主题。

3.1 不使用@Modifying注解的结果

让我们看看当我们不在删除查询上添加@Modifying注解时会发生什么。

为此,我们需要创建另一种方法:

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

请注意我们没有添加@Modifying注解。

当我们执行上面的方法时,我们得到一个InvalidDataAccessApiUsage异常:

org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.hql.internal.QueryExecutionRequestException: 
Not supported for DML operations [delete cn.tuyucheng.taketoday.boot.domain.User u where u.active = false]
(...)

错误信息描述很清楚:DML操作不支持该查询。

4. 管理持久性上下文

如果我们的修改查询更改了持久性上下文中包含的实体,那么这个上下文就会过时。处理这种情况的一种方法是清除持久性上下文。通过这样做,我们确保持久性上下文下次将从数据库中获取实体。

但是,我们不必显式调用EntityManager上的clear()方法。我们可以只使用@Modifying注解中的clearAutomatically属性:

@Modifying(clearAutomatically = true)

这样,我们确保在执行查询后清除持久性上下文

但是,如果我们的持久性上下文包含未刷新的更改,则清除它意味着删除未保存的更改。幸运的是,在这种情况下我们可以使用注解的另一个属性flushAutomatically:

@Modifying(flushAutomatically = true)

现在,在执行查询之前,实体管理器将被刷新

5. 总结

关于@Modifying注解的这篇短文章到此结束。我们学习了如何使用@Modifying注解来执行更新查询,例如INSERT、UPDATE、DELETE,甚至DDL。之后,我们讨论了如何使用clearAutomatically和flushAutomatically属性来管理持久化上下文的状态。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章