Transaction and Spring Data JPA

本文档的大主题是 Spring Data JPA,所以自然要来谈谈 Spring Data JPA 和事务的一些关系。

一个重要的点是,从 SimpleJpaRepository 中继承的 CRUD 方法都被默认标记为了 @Transactional。对于只读操作,@TransactionalreadOnly 参数被设定为 true。所有其他方法都使用没有参数的 @Transactional 注解进行标记。

如果需要调整 CRUD 方法的事务性,需要在你自己的 Repository 接口中进行覆盖:

public interface UserRepository extends CrudRepository<User, Long> {
    @Override
    @Transactional(timeout = 10)
    public List<User> findAll();

    // Further query method declarations
}

现在,findAll() 方法有了一个 10 秒钟的超时时限,同时去掉了 readOnly flag。

另外一个调整事务行为的方法是在 Service 中进行:

@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);
        }
    }
}

该例使得 addRoleToAllUsers() 方法在事务中运行(参与一个已有事务或者新建一个事务(如果没有正在运行的事务))。这种情况下,Repository 中的事务设定都会被忽略,因为现在外部事务配置决定了真正的设定。

注意:从 JPA 的角度来讲,这里使用 save() 方法是没有必要的,因为 role 是用 Repository 取出的,且处于事务内,所以其被代理,任何对其的改变都会在事务结束后自动提交!但是,这里仍然使用了 save() 方法,主要是为了与 Spring Data 提供的 Repository 抽象保持一致。

自定义方法的事务性:

@Transactional(readOnly = true)
interface UserRepository extends JpaRepository<User, Long> {

    List<User> findByLastname(String lastname);

    @Transactional
    void deleteByEmail(String email);

    @Modifying
    @Transactional
    @Query("delete from User u where u.active = false")
    void deleteInactiveUsers();
}

技巧是在 Repository 上标记 @Transactional(readOnly = true),然后对 Repository 内部的非只读查询自定义操作再标记 @Transactional 即可。

最后更新于