【问题标题】:Batch update records in GrailsGrails 中的批量更新记录
【发布时间】:2010-05-28 13:05:19
【问题描述】:

我必须根据单个操作为每个用户计算一个“总数”——也就是说,一个用户有许多操作,每个操作都有一个点。所以我需要基本上得到所有动作的所有分数的总和。

定期添加和减去操作。由于这可能是一项相当繁重的操作,因此每次需要时都执行“total”是不可行的。因此,我正在考虑为每个用户每天运行一次操作,并将用户的总积分存储为 int。

因为我有成千上万的用户,所以我正在尝试找出最好的方法来做到这一点。我应该基本上遍历所有用户并每次都调用 User.save() 吗,还是可以在 Grails / GORM 中使用某种批量更新机制?

【问题讨论】:

  • 你的问题很有趣。有时间我会尽量给你答复的。
  • 您不需要总和始终准确吗?为什么不即时计算用户的总数?
  • Mike - 我最初就是这样做的,但它对性能产生了相当大的影响——尤其是当我需要同时显示多个用户的总数时。在我的情况下,这是一个相当复杂的计算。

标签: grails groovy grails-orm


【解决方案1】:

您问题中的模型是您的实际模型,还是它的简化版本?如果您所做的只是 User hasMany Actions 并且每个 Action 都有一个您想要总结的点值(一个整数?),那么这确实是关系数据库擅长的事情。如果您的数据库上有正确的索引,我认为这将是一个非常快速的调用,只要您正在执行 groupBy 查询(或使用 grails 投影标准)。

这是一个示例方法,它将获取用户列表并返回一个数组,该数组将 user.id 与用户当前拥有的点数配对:

def calculateCurrentPoints(users) {
    Action.executeQuery('select a.user.id, sum(points) from Action a where a.user.id in (:userIds) group by a.user.id', [userIds: users.id])
}

如果需要,您可以轻松地将其转换为用户 ID 到点的映射,以便于查找:

def calculateCurrentPoints(users) {
    def result = Action.executeQuery('select a.user.id, sum(points) from Action a where a.user.id in (:userIds) group by a.user.id', [userIds: users.id])
    result.inject([:]) { map, userWithPoints -> 
        map[userWithPoints[0]] = userWithPoints[1]
        return map
    }
}

如果这真的比我想象的要慢,您可以使用与我上面类似的带有 HQL 的 executeUpdate 来更新每个用户的“totalPoints”字段(或随时将其作为服务的一部分进行更新)您向该用户添加一个新操作)。

【讨论】:

    【解决方案2】:

    调用User.save()实际上不会将更改写入数据库,只有在刷新Hibernate会话时才会写入更改(docs

    您可以通过访问SessionFactory 并在结果会话上调用flush 来手动刷新会话,如FAQ 所示。

    我想您可能希望使用批处理技术来加载用户,以确保您不会同时在内存中拥有数千个用户。

    顺便说一句,如果您想缓存该值,但在添加操作时自动更新它,您可以挂钩到活动记录的事件以更新计算值,这里是 example from Ruby

    【讨论】:

      猜你喜欢
      • 2021-12-13
      • 1970-01-01
      • 2011-01-15
      • 2018-09-14
      • 1970-01-01
      • 1970-01-01
      • 2019-01-02
      • 2023-03-05
      • 1970-01-01
      相关资源
      最近更新 更多