对于最新的稳定版本,请使用 Spring Data Relational 4.0.4spring-doc.cadn.net.cn

事务性

CrudRepository 实例的方法默认具有事务性。 对于读取操作,事务配置 readOnly 标志被设置为 true。 其他所有方法均使用普通的 @Transactional 注解进行配置,以便应用默认的事务配置。 详细信息请参阅 SimpleJdbcRepository 的 Javadoc。 如果您需要调整在仓库接口中声明的某个方法的事务配置,请按如下方式在您的仓库接口中重新声明该方法:spring-doc.cadn.net.cn

CRUD 的自定义事务配置
interface UserRepository extends CrudRepository<User, Long> {

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

    // Further query method declarations
}

上述配置会使 findAll() 方法以 10 秒的超时时间运行,并且不启用 readOnly 标志。spring-doc.cadn.net.cn

另一种改变事务行为的方式是使用一个门面(facade)或服务实现,它通常涵盖多个仓库(repository)。 其目的是为非 CRUD 操作定义事务边界。 以下示例展示了如何创建这样的门面:spring-doc.cadn.net.cn

使用外观(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(…) 的调用在事务中运行(参与现有事务,或在没有事务运行时创建一个新事务)。 存储库的事务配置被忽略,因为外部的事务配置决定了实际要使用的存储库。 请注意,您必须显式激活 <tx:annotation-driven /> 或使用 @EnableTransactionManagement,才能使基于注解的外观(facade)配置生效。 请注意,前面的示例假定您使用了组件扫描。spring-doc.cadn.net.cn

事务性查询方法

要使您的查询方法具有事务性,请在您定义的仓库接口上使用 @Transactional,如下例所示:spring-doc.cadn.net.cn

在查询方法上使用 @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 标志被设置为 falsespring-doc.cadn.net.cn

强烈建议将查询方法设为事务性的。 这些方法可能会执行多个查询以填充一个实体。 如果没有共同的事务,Spring Data JDBC 会在不同的连接中执行这些查询。 这可能会给连接池带来过大的压力,甚至在多个方法持有某个连接的同时又请求新连接时,可能导致死锁。
将只读查询通过设置 readOnly 标志标记为只读,这无疑是合理的。 然而,这样做并不会检查你是否触发了修改数据的查询(尽管某些数据库会在只读事务中拒绝 INSERTUPDATE 语句)。 相反,readOnly 标志会被作为提示传递给底层的 JDBC 驱动程序,以进行性能优化。

JDBC 锁机制

Spring Data JDBC 支持在派生查询方法上使用锁。 要在仓库中的某个派生查询方法上启用锁定,需使用 @Lock 注解对其进行标注。 LockMode 类型的必需值提供了两个选项:PESSIMISTIC_READ 可确保你正在读取的数据不会被修改,而 PESSIMISTIC_WRITE 则获取一个用于修改数据的锁。 某些数据库并不区分这两种模式。 在这种情况下,两种模式都等同于 PESSIMISTIC_WRITEspring-doc.cadn.net.cn

在派生查询方法上使用 @Lock
interface UserRepository extends CrudRepository<User, Long> {

    @Lock(LockMode.PESSIMISTIC_READ)
    List<User> findByLastname(String lastname);
}

如上所示,方法 findByLastname(String lastname) 将以悲观读锁的方式执行。 如果使用的是 MySQL 方言的数据库,例如将生成以下查询:spring-doc.cadn.net.cn

MySQL 方言生成的 SQL 查询
Select * from user u where u.lastname = lastname LOCK IN SHARE MODE
@Lock 目前不支持基于字符串的查询。 使用 @Query 创建的查询方法将忽略 @Lock 提供的锁信息。 在基于字符串的查询上使用 @Lock 会在日志中产生警告。 未来版本将抛出异常。