在事务服务调用中执行 JSR 303 验证

Perform JSR 303 validation in transactional service call

我正在使用 play 框架开发 Web 应用程序,该应用程序使用 JOOQ 和 spring 事务访问 postgres 数据库。

目前我正在实施按以下方式构建的用户注册:

  1. 用户post注册表单

  2. 请求被路由到控制器操作,该操作将所有参数(如电子邮件、密码等)映射到 DTO 上。 DTO 的不同字段使用 JSR 303 约束进行注释。

    电子邮件字段使用约束验证器进行注释,以确保同一地址不会被添加两次。此验证器具有对 UserRepository 的自动装配引用,因此它可以调用它的 isExistingEmail 方法。

  3. 调用了用户服务的注册方法,大致如下:

    @Transactional(isolation = Isolation.SERIALIZABLE)
    public User signupUser(UserDto userDto) {
        validator.validate(userDto);
        userRepository.add(userDto);
        return tutor;
    }
    

如果出现验证错误,服务内部的 validator.validate(userDto) 调用将抛出 RuntimeException。

请注意,存储库的 add 方法带有 @Transactional(propagation = Propagation.MANDATORY) 注释,而 isExistingEmail 方法没有任何注释。

我的问题是,当我连续两次 post 注册表单时,我从数据库收到一个唯一约束错误,因为两次 userRepository.isExistingEmail 都调用 returns false。但是,我希望第二次注册调用不允许将用户添加到存储库,因为我将事务的隔离级别设置为可序列化。

这是预期的行为还是可能存在 JOOQ/spring 事务配置问题?

我在服务中添加了一个 TransactionSynchronizationManager.isActualTransactionActive() 调用以确保交易实际上是活跃的。所以这部分似乎有效。

经过更多研究并阅读了 postgres 手册中关于事务隔离的 documentation,我开始意识到我对 spring 托管事务的理解还很欠缺。

将隔离级别设置为 SERIALIZABLE 时,postgres 不会真正阻止任何并发事务。相反,它将使用谓词锁来监视已提交的事务是否会产生与实际不同的结果 运行 一个接一个并发事务。

如果第二个事务尝试提交时数据状态无效,则底层数据库驱动程序只会抛出异常。我能够验证此行为并通过暂时删除我的电子邮件字段的唯一约束来强制序列化失败。

我的最终解决方案是将隔离级别降低到 READ_COMMITTED 并在调用 userRepository.add(userDto) 时处理违反唯一约束的异常,因为 SERIALIZABLE 隔离级别并不是真正必要的处理这个特定的用例。

请让我知道处理这种标准情况的任何更好的方法。