【问题标题】:Grails / HIbernate Postgresql cannot query previously created objectGrails / HIbernate Postgresql 无法查询以前创建的对象
【发布时间】:2021-08-04 12:40:10
【问题描述】:

我们将 Grails 4 与 Hibernate 5 和 Postgresql 一起使用,我遇到了一个奇怪的问题,我不知道如何解决。

我创建了一个测试,它创建了一个 Family(一个包含大约 20 个 INSERT 和 UPDATE 查询的大型对象图),然后它尝试使用它的 id 来检索它。

所以有一个FamilyController扩展grails.rest.RestfulControllerstatic responseFormats = ['json']和以下方法:

    @Override
    protected Family createResource() {

        def instance = new NewFamilyCommand()

        bindData(instance, getObjectToBind())
        instance.validate()
        //Check for binding errors
        if(instance.hasErrors()) {
            throw new ValidationException('Unable to bind new family', instance.errors)
        }

        //Send the command to the service, which will return us an actual Family domain object
        return familyService.addFamilyToUser(instance)
    }

    @Override
    protected Family queryForResource(Serializable id) {
        def family = familyService.safeGetFamily(Long.parseLong(id))
        ...
        return family
    }

使用 Cypress 测试在循环中运行它,并且大部分时间都可以正常工作。

问题是通过返回的id查询Family时不时(好像是50的倍数,巧合的是Tomcat中配置的maxConnections的个数)找不到。

这是数据源的 Tomcat 配置(Grails 推荐的默认配置):

dataSource:
    pooled: true
    jmxExport: true
    driverClassName: org.postgresql.Driver
    dialect: "net.kaleidos.hibernate.PostgresqlExtensionsDialect"
    username: "test"
    password: "test"
    properties:
        initialSize: 2
        maxActive: 50
        minIdle: 2
        maxIdle: 2
        maxWait: 10000
        maxAge: 10 * 60000
        timeBetweenEvictionRunsMillis: 5000
        minEvictableIdleTimeMillis: 60000
        validationQuery: "SELECT 1"
        validationQueryTimeout: 3
        validationInterval: 15000
        testOnBorrow: true
        testWhileIdle: true
        testOnReturn: false
        jdbcInterceptors: "ConnectionState;StatementCache(max=200)"
        defaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTED
        removeAbandoned: true
        removeAbandonedTimeout: 300

Postgresql 在 docker 容器中运行,此数据库有 2 个 PIDS,比如 7374

我查看了 Postgresql 日志,发现成功测试和失败测试之间存在差异。

在成功的场景中,家庭创建和家庭检索都在同一个 PID 中运行。

在失败的情况下,家庭创建由 pid 74 执行,家庭检索由 PID 73 执行。

更奇怪的是 PID 73 大部分时间都是空闲的,它围绕 Family 50 的创建运行它的第一个查询(大概是在开始重用连接时),然后在 Family 101 开始使用家族检索查询,其事务开始于 PID 74 家族创建事务提交之前(至少在 Postgres 日志中显示,但可能日志没有按时间顺序打印)。

在测试失败后立即检查数据库,我看到家庭保存在数据库中,如果我在查询结果之前添加一点等待时间,测试也会通过。

我想知道这怎么可能,我假设 Postgresl 仅在事务提交后才返回 ID,那么为什么其他 PID 看不到呢?

在解决此问题方面的任何帮助将不胜感激。

更新:似乎与 Hibernate.FLUSH_MODE 有关。如果我在familyService.addFamilyToUser(instance) 中设置sessionFactory.currentSession.setFlushMode(FlushMode.MANUAL),问题就会消失,并且语句会正确排序。即使 Postgres 没有提交所有插入/更新语句,Hibernate 似乎过早地刷新会话并返回家庭 ID。

【问题讨论】:

  • "循环运行,大部分时间运行良好。" - 这是否意味着您有一些外部进程重复向应用发送请求,一个要创建,然后一个要检索(2 个单独的 HTTP 请求)?
  • 是的,这是 Cypress 测试
  • 控制器操作的返回类型为Family 有点不寻常。您能否展示您的 Family create()Family get(Long id) 操作中的内容?
  • @JeffScottBrown 用我们控制器的代码更新了问题
  • “使用我们控制器中的代码更新了问题” - 看起来您删除了对将 Family 声明为返回类型的控制器操作的引用。您是在使用从 RestfulController 继承的 getcreate,还是您自己的实现具有原始问题中表达的返回类型?

标签: postgresql hibernate grails


【解决方案1】:

看起来这是我们的错,我们搞砸了 DirtyCheckable#hasChanged 实现,代码在初始插入后生成了很多 UPDATE 语句,这导致会话被多次刷新,底层 Posgresql 事务被与 Hibernate 事务不同步

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多