事务性
`CrudRepository` 实例的方法默认是事务性的。对于读取操作,事务配置的 `readOnly` 标志设置为 `true`。所有其他操作都配置了普通的 `@Transactional` 注解,因此应用默认的事务配置。详情请参阅 SimpleJdbcRepository
的 Javadoc。如果您需要调整 Repository 中某个方法的事务配置,请在您的 Repository 接口中重新声明该方法,如下所示
interface UserRepository extends CrudRepository<User, Long> {
@Override
@Transactional(timeout = 10)
List<User> findAll();
// Further query method declarations
}
上述配置使 `findAll()` 方法以 10 秒的超时时间运行,并且没有 `readOnly` 标志。
另一种改变事务行为的方式是使用通常覆盖多个 Repository 的 Facade(门面)或服务实现。其目的是为非 CRUD 操作定义事务边界。以下示例展示了如何创建这样的 Facade
@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(…)` 的调用在事务中运行(参与现有事务或在没有事务运行时创建新事务)。Repository 的事务配置被忽略,因为外部事务配置决定了实际使用的 Repository。请注意,您必须显式激活 `<tx:annotation-driven />` 或使用 `@EnableTransactionManagement` 才能使基于注解的 Facade 配置生效。请注意,前面的示例假设您使用组件扫描。
事务性查询方法
为了使您的查询方法具有事务性,请在您定义的 Repository 接口上使用 `@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 支持对派生查询方法进行锁定。要在 Repository 中的给定派生查询方法上启用锁定,您需要使用 `@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 Dialect 的数据库,例如,将生成以下查询
Select * from user u where u.lastname = lastname LOCK IN SHARE MODE