如何在 Grails/GORM 中批量删除记录?

How do you bulk delete records in Grails/GORM?

提问人:Simon 提问时间:2/10/2010 更新时间:10/24/2013 访问量:36609

问:

我有一个表格,其中包含需要根据一组标准定期清除的记录。

我本来以为我可以使用条件生成器来删除记录,但这失败了,因为没有关于条件的方法......delete

def c = Agency.createCriteria()
c.delete
{
    eq("agency", "XXX")  
}

所以我想也许我先查询集合,然后删除它......

def c = Agency.createCriteria()
def deletions = c
{
    eq("agency", "XXX")  
}
deletions.delete

这也由于相同的原因、不同的对象而失败。

那么正确的方法是什么呢?我必须遍历调用每个项目的整个结果集,这似乎太过分了(反常)。delete()

我知道我可以形成一个查询以直接在 HQL 或 SQL 中执行,但这感觉也是错误的。条件生成器是否仅用于检索?

谢谢

Hibernate Grails Grails-ORM

评论


答:

19赞 Colin Harrington 2/10/2010 #1

用户指南中关于删除对象

请注意,Grails 没有提供 deleteAll 方法,因为删除数据是 气馁,通常可以避免 通过布尔标志/逻辑。

如果确实需要批量删除 可以使用 executeUpdate 的数据 批量 DML 语句的处理方法:

Customer.executeUpdate("delete Customer c where c.name = :oldName", [oldName:"Fred"])

评论

24赞 Simon 2/10/2010
是的,这就是我的做法,但是我不能使用标准有点疯狂。DELETE 是查询语义的有效部分,并得到所有 RDBMS 的支持,并且是完全有效的事情,尽管 Hibernate/GORM 作者可能怎么想。就我而言,将过期的记录留在表中是非常糟糕的做法。
1赞 Colin Harrington 3/15/2014
正如 JesperSM 所引用的: 现在 Grails 2+ 在 DetachedCriteria 上确实有一个 deleteAll()。
3赞 A.W. 4/17/2014
这行得通。但是,太糟糕了,它不会级联删除子表中的行。然后它给出一个 .在 中对此进行了测试。Integrity constraint violation2.3.7
1赞 Christof 10/2/2014
我们遇到了与@Guus相同的问题。在内部,JDBCPreparedStatement 用于执行更新,这显然不遵守 Hibernate 或其实体关系的级联规则。
0赞 Badri Paudel 3/1/2022
@Simon我知道这种类型的解决方案有效,但这对于解决方案删除大量数据有效吗?假设我想删除 10K 数据,这样可以吗?
13赞 sbglasius 2/10/2010 #2

如果你想避免 HQL,我建议使用 GORM list()、delete() 和 Groovy 的 spread 运算符:

def agencyList = Agency.createCriteria().list {
    eq("agency", "XXX")  
}
agencyList*.delete()

评论

1赞 Simon 2/11/2010
哇!那个流浪的*有什么作用?我完全尝试了这个,但没有 * 并得到编译器错误......
2赞 Colin Harrington 2/11/2010
这*。是 Groovy 扩展运算符 groovy.codehaus.org/Operators#Operators-SpreadOperator(.)在 Sbgrasius 的示例中,它对返回的集合中的项调用 delete() 方法。
0赞 khylo 3/15/2012
使用此方法时,我遇到了java.util.ConcurrentModificationException...因此,我停止使用扩展运算符,并按照 stackoverflow.com/questions/1816196/ 中的解决方案使用迭代器创建了一个while循环......
15赞 JesperSM 4/23/2012
这样做的问题是,您最终会获取整个集合,并线性删除每个集合。这意味着整个表都进入了堆,这对于玩具模型来说不是问题,但对于真正的批量数据来说可能是问题。
0赞 Dhaval dave 8/27/2015
也解决了我的问题:,当我们得到带有用途的列表时。FindAllBy*,要删除所有用户,需要用户*.delete()谢谢@sbglasius
50赞 JesperSM 4/23/2012 #3

在 Grails 2.0 中,你可以使用像这样分离的查询:

Agency.where { }.deleteAll()

请注意,您不会执行侦听器和其他内容,但它确实会执行到数据库,并且它与模拟域的东西兼容,如下所示:

void testWhatever() {
    mockDomain(Agency, [])
    saveABunchOfAgencies() // saves 10 of 'em
    assert Agency.count() == 10

    Agency.where { }.deleteAll()

    assert Agency.count() == 0   // Joy!
}

话虽如此,GORM 单元测试模拟有很多陷阱,但总体上非常整洁。

评论

0赞 Peter 5/24/2012
这似乎是最好的肯定。我正在使用它。
1赞 GreenGiant 2/11/2014
Grails 文档:grails.org/doc/latest/guide/single.html#whereQueries
0赞 Alexander Suraphel 7/16/2015
Jesper,我完全同意“GORM 单元测试模拟有一堆陷阱”。他们被记录在什么地方?
0赞 Tung 11/17/2018
正如我所尝试的那样,该解决方案运行良好且可靠。