跳到主要内容
版本:4.1.1

事务性

Doubao 中英对照 Transactionality

默认情况下,从 CrudRepository 继承的方法会继承 SimpleJpaRepository 的事务配置。对于读操作,事务配置的 readOnly 标志设为 true。其余方法均以普通的 @Transactional 配置,从而应用默认事务配置。由事务性仓库片段支撑的仓库方法,会从实际的片段方法继承事务属性。

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

示例 1. 为 CRUD 自定义事务配置

public interface UserRepository extends CrudRepository<User, Long> {

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

// Further query method declarations
}

这样做会使 findAll() 方法以 10 秒超时运行,且不设置 readOnly 标志。

另一种改变事务行为的方式是使用一个(通常)覆盖多个仓库的门面或服务实现。其目的是为非 CRUD 操作定义事务边界。以下示例展示了如何将这样的门面用于多个仓库:

示例 2. 使用门面向多个仓库调用定义事务

@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,才能使基于注解的门面配置生效。此示例假设您使用组件扫描。

请注意,从 JPA 的角度来看,对 save 的调用并非严格必需,但为了与 Spring Data 提供的仓库抽象保持一致,仍应保留该调用。

事务性查询方法​

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

示例 3. 在查询方法上使用 @Transactional

@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 的情况下运行。

备注

您可以对只读查询使用事务,并通过设置 readOnly 标志将其标记为只读。然而,这样做并不会检查您是否触发了修改性查询(尽管有些数据库会拒绝在只读事务中执行 INSERT 和 UPDATE 语句)。相反,readOnly 标志会作为提示传递给底层 JDBC 驱动程序,以进行性能优化。此外,Spring 还会对底层 JPA 提供方执行一些优化。例如,与 Hibernate 配合使用时,当您将事务配置为 readOnly 时,刷新模式会设为 MANUAL,这会使 Hibernate 跳过脏检查(对大型对象树而言是显著的性能提升)。

备注

尽管各示例讨论的是在仓库上使用 @Transactional,但我们通常建议在启动一个工作单元时声明事务边界,以确保适当的一致性和预期的事务参与。