跳到主要内容

事务性

DeepSeek V3 中英对照 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
}
java

这样做会导致 findAll() 方法以 10 秒的超时运行,并且没有 readOnly 标志。

另一种改变事务行为的方式是使用一个门面(facade)或服务实现,它通常涵盖多个仓库。其目的是为非 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);
}
}
}
java

此示例会导致 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();
}
java

通常情况下,你会希望将 readOnly 标志设置为 true,因为大多数查询方法只读取数据。相比之下,deleteInactiveUsers() 使用了 @Modifying 注解,并覆盖了事务配置。因此,该方法运行时会将 readOnly 标志设置为 false

备注

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