Spring Data JPA @Modifying Annotation – Spring Data JPA @Modifying Annotation

最后修改: 2019年 2月 24日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

In this short tutorial, we’ll learn how to create update queries with the Spring Data JPA @Query annotation. We’ll achieve this by using the @Modifying annotation.

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

First, to refresh our memory, we can read how to make queries using Spring Data JPA. After that, we’ll deep dive into the use of the @Query and @Modifying annotations. Finally, we’ll discuss how to manage the state of our persistence context when using modifying queries.

首先,为了恢复我们的记忆,我们可以阅读如何使用 Spring Data JPA 进行查询。之后,我们将深入了解@Query@Modifying注解的使用。最后,我们将讨论在使用修改查询时如何管理我们的持久化上下文的状态。

2. Querying in Spring Data JPA

2.在Spring Data JPA中进行查询

First, let’s recap the three mechanisms that Spring Data JPA provides for querying data in a database:

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

  • Query methods
  • @Query annotation
  • Custom repository implementation

Let’s create a User class and a matching Spring Data JPA repository to illustrate these mechanisms:

让我们创建一个User类和一个匹配的Spring Data JPA资源库来说明这些机制。

@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> {}

The query methods mechanism allows us to manipulate the data by deriving the queries from the method names:

查询方法机制允许我们通过从方法名称推导出的查询来操作数据。

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

In this example, we can find a query that retrieves users by their names, or a query that removes users having a creation date after a certain date.

在这个例子中,我们可以找到一个按姓名检索用户的查询,或者一个删除创建日期在某一日期之后的用户的查询。

As for the @Query annotation, it provides us with the opportunity to write a specific JPQL or SQL query in the @Query annotation:

至于@Query注解,它为我们提供了在@Query注解中写一个特定的JPQL或SQL查询的机会

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

In this code snippet, we can see a query retrieving users having an @gmail.com email address.

在这个代码片段中,我们可以看到一个查询,检索具有@gmail.com电子邮件地址的用户。

The first mechanism enables us to retrieve or delete data. As for the second mechanism, it allows us to execute pretty much any query. However, for updating queries, we must add the @Modifying annotation. This will be the topic of this tutorial.

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

3. Using the @Modifying Annotation

3.使用@修改注释

The @Modifying annotation is used to enhance the @Query annotation so that we can execute not only SELECT queries, but also INSERT, UPDATE, DELETE, and even DDL queries.

@Modifying注解用于增强@Query注解,这样我们不仅可以执行SELECT查询,还可以执行INSERTUPDATEDELETE,甚至DDL查询。

Now let’s play with this annotation a little.

现在我们来玩一下这个注释。

First, let’s look at an example of a @Modifying UPDATE query:

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

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

Here we’re deactivating the users that haven’t logged in since a given date.

在这里,我们要停用自指定日期以来没有登录过的用户。

Let’s try another one, where we’ll delete deactivated users:

让我们再试一次,我们将删除停用的用户。

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

As we can see, this method returns an integer. It’s a feature of Spring Data JPA @Modifying queries that provides us with the number of updated entities.

我们可以看到,这个方法返回一个整数。这是Spring Data JPA @Modifying查询的一个特性,它为我们提供了更新的实体的数量。

We should note that executing a delete query with @Query works differently from Spring Data JPA’s deleteBy name-derived query methods. The latter first fetches the entities from the database, and then deletes them one by one. This means that the life-cycle method @PreRemove will be called on those entities. However, with the former, a single query is executed against the database.

我们应该注意到,用@Query执行删除查询的工作方式与Spring Data JPA的deleteBy名衍生查询方法不同。后者首先从数据库中获取实体,然后一个一个地删除它们。这意味着生命周期方法@PreRemove将被调用到这些实体上。然而,对于前者,一个单一的查询是针对数据库执行的。

Finally, let’s add a deleted column to our USERS table with a DDL query:

最后,让我们用DDL查询在我们的USERS表中添加一个deleted列。

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

Unfortunately, using modifying queries leaves the underlying persistence context outdated. However, it is possible to manage this situation. That’s the subject of the next section.

不幸的是,使用修改查询会使底层的持久化环境过时。然而,有可能管理这种情况。这就是下一节的主题。

3.1. Result of NOT Using the @Modifying Annotation

3.1.不使用@Modifying注释的结果

Let’s see what happens when we don’t put the @Modifying annotation on the delete query.

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

For this reason, we need to create yet another method:

由于这个原因,我们需要创建另一个方法。

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

Notice the missing annotation.

注意到缺失的注释。

When we execute the above method, we get an InvalidDataAccessApiUsage exception:

当我们执行上述方法时,我们得到一个InvalidDataAccessApiUsage异常。

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]
(...)

The error message is pretty clear; the query is Not supported for DML operations.

错误信息很清楚;查询不支持DML操作。

4. Managing the Persistence Context

4.管理持久性语境

If our modifying query changes entities contained in the persistence context, then this context becomes outdated. One way to manage this situation is to clear the persistence context. By doing that, we make sure that the persistence context will fetch the entities from the database next time.

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

However, we don’t have to explicitly call the clear() method on the EntityManager. We can just use the clearAutomatically property from the @Modifying annotation:

然而,我们不必明确地调用EntityManager上的clear()方法。我们只需使用@Modifying注解中的clearAutomatically属性

@Modifying(clearAutomatically = true)

This way, we make sure that the persistence context is cleared after our query execution.

这样,我们确保在我们的查询执行后,持久化上下文被清空。

However, if our persistence context contained unflushed changes, clearing it would mean dropping the unsaved changes. Fortunately, there’s another property of the annotation we can use in this case, flushAutomatically:

然而,如果我们的持久化上下文包含了未刷新的变化,清除它就意味着丢弃未保存的变化。幸运的是,在这种情况下,我们可以使用注解的另一个属性,flushAutomatically

@Modifying(flushAutomatically = true)

Now the EntityManager is flushed before our query is executed.

现在,在我们的查询执行之前,EntityManager被刷新。

5. Conclusion

5.总结

That concludes this brief article about the @Modifying annotation. We learned how to use this annotation to execute updating queries, like INSERT, UPDATE, DELETE, and even DDL. After that, we discussed how to manage the state of the persistence context with the clearAutomatically and flushAutomatically properties.

这就是关于@Modifying注解的简短文章的结论。我们学习了如何使用这个注解来执行更新查询,比如INSERT、UPDATE、DELETE,甚至DDL。之后,我们讨论了如何用clearAutomaticallyflushAutomatically属性管理持久化上下文的状态。

As usual, the full code for this article is available over on GitHub.

像往常一样,本文的完整代码可在GitHub上获得