【问题标题】:Perform JSR 303 validation in transactional service call在事务服务调用中执行 JSR 303 验证
【发布时间】:2015-11-22 06:54:36
【问题描述】:

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

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

  1. 用户发布注册表单

  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 方法没有任何注释。

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

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

我在服务中添加了TransactionSynchronizationManager.isActualTransactionActive() 调用,以确保事务实际处于活动状态。所以这部分似乎有效。

【问题讨论】:

    标签: spring postgresql transactions bean-validation jooq


    【解决方案1】:

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

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

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

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

    请告诉我处理这种标准情况的更好方法。

    【讨论】:

      猜你喜欢
      • 2015-01-10
      • 2012-05-12
      • 2018-08-24
      • 1970-01-01
      • 1970-01-01
      • 2011-10-30
      • 2018-08-15
      • 1970-01-01
      • 2012-06-29
      相关资源
      最近更新 更多