从列表中连续分配值的批量更新的有效方法?
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() 非常大时它会有所帮助。
假设有一个域 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() 非常大时它会有所帮助。