Transactionality

默认情况下,从 CrudRepository 继承的方法从 SimpleJpaRepository 继承事务配置。对于读取操作,事务配置 readOnly 标志被设置为 true。所有其他内容都使用普通 @Transactional 进行配置,以便应用默认事务配置。受事务仓库片段支持的仓库方法从实际片段方法继承事务属性。

By default, methods inherited from CrudRepository inherit the transactional configuration from SimpleJpaRepository. For read operations, the transaction configuration readOnly flag is set to true. All others are configured with a plain @Transactional so that default transaction configuration applies. Repository methods that are backed by transactional repository fragments inherit the transactional attributes from the actual fragment method.

如果你需要调整存储库中声明的某个方法的事务配置,请在存储库接口中重新声明该方法,如下所示:

If you need to tweak transaction configuration for one of the methods declared in a repository, redeclare the method in your repository interface, as follows: .Custom transaction configuration for CRUD

public interface UserRepository extends CrudRepository<User, Long> {

  @Override
  @Transactional(timeout = 10)
  public List<User> findAll();

  // Further query method declarations
}

这样做会使 findAll() 方法在 10 秒内没有 readOnly 标志的情况下运行。

Doing so causes the findAll() method to run with a timeout of 10 seconds and without the readOnly flag.

更改事务行为的另一种方法是使用通常涵盖多个存储库的外观或服务实现。其目的是为非 CRUD 操作定义事务边界。以下示例展示了如何将此类外观用于多个存储库:

Another way to alter transactional behaviour is to use a facade or service implementation that (typically) covers more than one repository. Its purpose is to define transactional boundaries for non-CRUD operations. The following example shows how to use such a facade for more than one repository: .Using a facade to define transactions for multiple repository calls

@Service
public class UserManagementImpl implements UserManagement {

  private final UserRepository userRepository;
  private final RoleRepository roleRepository;

  public UserManagementImpl(UserRepository userRepository,
    RoleRepository roleRepository) {
    this.userRepository = userRepository;
    this.roleRepository = roleRepository;
  }

  @Transactional
  public void addRoleToAllUsers(String roleName) {

    Role role = roleRepository.findByName(roleName);

    for (User user : userRepository.findAll()) {
      user.addRole(role);
      userRepository.save(user);
    }
  }
}

此示例会使对 addRoleToAllUsers(…) 的调用在事务内部运行(参与现有的事务或在不存在运行事务时创建一个新的事务)。然后忽略存储库中的事务配置,因为外部事务配置决定了实际使用的配置。请注意,你必须激活 <tx:annotation-driven /> 或显式使用 @EnableTransactionManagement 才能使基于注释的外观配置生效。此示例假设你使用了组件扫描。

This example causes call to addRoleToAllUsers(…) to run inside a transaction (participating in an existing one or creating a new one if none are already running). The transaction configuration at the repositories is then neglected, as the outer transaction configuration determines the actual one used. Note that you must activate <tx:annotation-driven /> or use @EnableTransactionManagement explicitly to get annotation-based configuration of facades to work. This example assumes you use component scanning.

请注意,从 JPA 的角度来看,对 save 的调用并不是绝对必要的,但为了保持与 Spring Data 提供的存储库抽象的一致性,仍然需要进行该调用。

Note that the call to save is not strictly necessary from a JPA point of view, but should still be there in order to stay consistent to the repository abstraction offered by Spring Data.

Transactional query methods

默认情况下,声明的查询方法(包括默认方法)不会应用任何事务配置。要以事务方式运行这些方法,请在定义的存储库接口中使用 @Transactional,如以下示例所示:

Declared query methods (including default methods) do not get any transaction configuration applied by default. To run those methods transactionally, use @Transactional at the repository interface you define, as shown in the following example:

Example 1. Using @Transactional at query methods
@Transactional(readOnly = true)
interface UserRepository extends JpaRepository<User, Long> {

  List<User> findByLastname(String lastname);

  @Modifying
  @Transactional
  @Query("delete from User u where u.active = false")
  void deleteInactiveUsers();
}

通常,你会希望 readOnly 标志设置为 true,因为大多数查询方法仅读取数据。与此相反,deleteInactiveUsers() 使用了 @Modifying 注释并覆盖了事务配置。因此,该方法以 readOnly 标志设置为 false 的方式运行。

Typically, you want the readOnly flag to be set to true, as most of the query methods only read data. In contrast to that, deleteInactiveUsers() makes use of the @Modifying annotation and overrides the transaction configuration. Thus, the method runs with the readOnly flag set to false.

你可以将事务用于只读查询,并通过设置 readOnly 标志将其标记为只读查询。但是,这样做不会作为检查,以确保你不会触发操作查询(尽管一些数据库拒绝只读事务中的 INSERTUPDATE 语句)。而是将 readOnly 标志作为提示传播到基础 JDBC 驱动程序,以进行性能优化。此外,Spring 对底层 JPA 提供程序执行了一些优化。例如,当与 Hibernate 一起使用时,在你将事务配置为 readOnly 时,刷新模式将被设置为 NEVER,这将导致 Hibernate 跳过脏检查(对于大型对象树而言是一个显着的改进)。

You can use transactions for read-only queries and mark them as such by setting the readOnly flag. Doing so does not, however, act as a check that you do not trigger a manipulating query (although some databases reject INSERT and UPDATE statements inside a read-only transaction). The readOnly flag is instead propagated as a hint to the underlying JDBC driver for performance optimizations. Furthermore, Spring performs some optimizations on the underlying JPA provider. For example, when used with Hibernate, the flush mode is set to NEVER when you configure a transaction as readOnly, which causes Hibernate to skip dirty checks (a noticeable improvement on large object trees).