从列表中连续分配值的批量更新的有效方法?

efficient way to do bulk updates where values are assigned serially from a list?

假设有一个域 A 具有 属性 p。

class A{
 Integer p
}

我有一个 A 列表,即

def lis = A.list()

然后我有一个数字列表

def num = [4, 1, 22, ......]

在 grails 中进行批量更新的最有效方法是什么,其中 A 的每个对象都从 num 中连续分配一个数字。

一种方法可能是

for(int i=0; i<lis.size(); i++){
  lis[i].p = num[i]
  lis[i].save(flush: true)
}

但我认为这个解决方案效率不高。这可以使用 HQL 或其他有效方法来实现吗?感谢您的帮助!谢谢!

虽然我同意您的解决方案可能效率低下,但这主要是因为您在每次保存时都在刷新。因此,您可以通过使用 transaction; 获得性能提升。提交时自动导致刷新:

A.withTransaction { status ->
    ...

    for(int i=0; i<lis.size(); i++) {
        lis[i].p = num[i]
        lis[i].save()
    }
}

当然,如果能使用@Transactional注解就更好了

是的,您可以使用 HQL,但这里的根本问题是您的数字列表是任意的,因此您需要多个 HQL 查询;每次更新一个。

首先尝试事务方法,因为它最容易设置。

如果您的 A 和数字列表是需要处理的大量数据(如果 lis.size 例如等于 10 000),那么您应该这样做:

  A.withNewTransaction { status -> // begin a new hibernate session

    int stepForFlush = 100
    int totalLisSize = A.count()
    def lis

    for(int k=0; k < totalLisSize; k+=stepForFlush) {

      lis = A.list(max: stepForFlush, offset: k) // load only 100 elements in the current hibernate session
      ...

      for(int i=0; i<lis.size(); i++) {
        lis[i].p = num[k+i]
        lis[i].save()
      }
      A.withSession { session ->
        session.flush() // flush changes to database
        session.clear() // clear the hibernate session, the 100 elements are no more attached to the hibernate session
                        // Then they are now eligible to garbage collection
                        // you ensure not maintaining in memory all the elements you are treating
      }
    } // next iteration, k+=100


  } // Transaction is closed then transaction is commited = a commit is executed to database,
    // and then all changes that has been flush previously are committed.

注:

在此解决方案中,您不会将所有 A 元素加载到内存中,当您的 A.list().size() 非常大时它会有所帮助。