事务性
CrudRepository
实例的方法默认情况下是事务性的。对于读取操作,事务配置的 readOnly
标志设置为 true
。所有其他操作都配置了普通的 @Transactional
注解,以便应用默认的事务配置。有关详细信息,请参阅 SimpleJdbcRepository
的 Javadoc。如果需要调整存储库中声明的方法之一的事务配置,请在存储库接口中重新声明该方法,如下所示
interface UserRepository extends CrudRepository<User, Long> {
@Override
@Transactional(timeout = 10)
List<User> findAll();
// Further query method declarations
}
上述操作会导致 findAll()
方法在 10 秒的超时时间内运行,并且不使用 readOnly
标志。
另一种更改事务行为的方法是使用外观或服务实现,这些实现通常涵盖多个存储库。其目的是为非 CRUD 操作定义事务边界。以下示例演示了如何创建此类外观
@Service
public class UserManagementImpl implements UserManagement {
private final UserRepository userRepository;
private final RoleRepository roleRepository;
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
使外观的基于注解的配置生效。请注意,上述示例假设您使用组件扫描。
事务性查询方法
要使查询方法具有事务性,请在定义的存储库接口中使用 @Transactional
,如下例所示
@Transactional(readOnly = true)
interface UserRepository extends CrudRepository<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
。
强烈建议将查询方法设为事务性。这些方法可能执行多个查询以填充实体。如果没有公共事务,Spring Data JDBC 将在不同的连接中执行查询。这可能会给连接池带来过大的压力,甚至可能在多个方法请求新的连接同时持有连接时导致死锁。 |
通过设置 readOnly 标志将只读查询标记为只读绝对是合理的。但是,这并不能检查您是否没有触发操作查询(尽管某些数据库在只读事务中拒绝 INSERT 和 UPDATE 语句)。相反,readOnly 标志作为提示传播到底层 JDBC 驱动程序以进行性能优化。 |
JDBC 锁定
Spring Data JDBC 支持在派生查询方法上进行锁定。要在存储库中的给定派生查询方法上启用锁定,您可以使用 @Lock
对其进行注释。类型为 LockMode
的必需值提供了两个值:PESSIMISTIC_READ
保证您正在读取的数据不会被修改,以及 PESSIMISTIC_WRITE
获取锁定以修改数据。某些数据库不区分这两个值。在这种情况下,两种模式都等同于 PESSIMISTIC_WRITE
。
interface UserRepository extends CrudRepository<User, Long> {
@Lock(LockMode.PESSIMISTIC_READ)
List<User> findByLastname(String lastname);
}
如上所示,方法 findByLastname(String lastname)
将使用悲观读锁执行。如果您使用的是带有 MySQL 方言的数据库,这将导致以下查询:
Select * from user u where u.lastname = lastname LOCK IN SHARE MODE