|
对于最新的稳定版本,请使用 Spring Data Relational 4.0.4! |
事务性
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 标志。
另一种改变事务行为的方式是使用一个门面(facade)或服务实现,它通常涵盖多个仓库(repository)。 其目的是为非 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,才能使基于注解的外观(facade)配置生效。
请注意,前面的示例假定您使用了组件扫描。
事务性查询方法
要使您的查询方法具有事务性,请在您定义的仓库接口上使用 @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
@Lock 目前不支持基于字符串的查询。
使用 @Query 创建的查询方法将忽略 @Lock 提供的锁信息。
在基于字符串的查询上使用 @Lock 会在日志中产生警告。
未来版本将抛出异常。 |