';列表中表达式的最大数量为1000';Grails和Oracle的错误

';列表中表达式的最大数量为1000';Grails和Oracle的错误,oracle,grails,gorm,Oracle,Grails,Gorm,我正在将Grails与Oracle数据库一起使用。我的应用程序中的大多数数据都是如下层次结构的一部分(每个项目包含以下内容): 方向 团体 建筑工地 合同 检查 不合规 对用户可见的数据根据其访问进行过滤,根据用户角色,访问可以是方向、组或建筑场地级别 通过为BuildingSite域类创建一个listWithSecurity方法,我们可以很容易地实现这一点,而不是在系统的大部分部分使用list。我们为合同创建了另一个listWithSecurity方法。它基本上是一个Contract.fi

我正在将Grails与Oracle数据库一起使用。我的应用程序中的大多数数据都是如下层次结构的一部分(每个项目包含以下内容):

  • 方向
  • 团体
  • 建筑工地
  • 合同
  • 检查
  • 不合规
对用户可见的数据根据其访问进行过滤,根据用户角色,访问可以是方向、组或建筑场地级别

通过为BuildingSite域类创建一个listWithSecurity方法,我们可以很容易地实现这一点,而不是在系统的大部分部分使用list。我们为合同创建了另一个listWithSecurity方法。它基本上是一个Contract.findAllByContractIn(BuildingSite.listWithSecurity)。其他课程也是如此。这样做的好处是,BuildingSite.listWithsecurity中保留了所有实际访问逻辑

当我们开始在系统中获取真实数据时,问题就出现了。我们很快遇到了“ora-01795列表中表达式的最大数量为1000”错误。公平地说,传递超过1000个文本的列表并不是最有效的方法,因此我尝试了其他方法,尽管这意味着我必须将安全逻辑传递给每个控制器

显而易见的方法似乎是使用这样的标准(为了简单起见,我在这里只放了方向级访问):

我希望Grails生成一个带有连接的查询,这样可以避免ora-01795错误,但它似乎在为每个级别调用一个单独的查询,并以“in”的形式将结果作为文本传递回Oracle以查询另一个级别。换句话说,它完全符合我所做的,所以我得到了相同的错误

实际上,它可能有点优化。它似乎在解决问题,但只解决了一个层面。在上一个示例中,1001次检查不会出现错误,但1001次合同或建筑工地会出现错误

我还尝试用findAll和一个HQL-where语句做基本相同的事情,我向该语句传递了一个方向,以在一个查询中获得不符合项。同样的事情。它解决了第一个层次,但我得到了其他层次相同的错误

我通过将“in”条件拆分为“in”或“in”中的许多“in”来修补它,因此没有一个文本列表的长度超过1000,但这是非常难看的代码。一个findAllBy[…]In变成超过10行代码。从长远来看,它可能会导致性能问题,因为我们一直在使用大量参数进行查询


是否有人以更优雅、更高效的方式遇到并解决了这个问题?

与Juergen的评论相同,我通过创建一个DB视图来处理类似的问题,该视图将用户/角色访问规则在其最精细的级别(在您的情况下构建站点?)进行了最低程度的扁平化,此视图可能只包含两列:建筑场地ID和用户/组名称。因此,在用户具有方向级访问权限的情况下,他/她在安全视图中会有许多行—一行用于允许用户访问的方向的每个子建筑场地

然后,创建一个只读GORM类映射到您的安全视图,将其连接到其他域类,并使用视图的用户/角色字段进行过滤。如果运气好的话,您完全可以在GORM中完成这项工作(这里有一些提示:)


不过,您可能需要从Hibernate中获得一些乐趣:

这不会赢得任何效率奖励,但我认为如果您只是简单地需要查询1000多个项目的列表,那么我会将其作为一个选项发布,没有一个更有效的选项可用/合适。(这个stackoverflow问题位于谷歌搜索“grails oracle 1000”结果的顶部)

在grails标准中,您可以使用Groovy的collate()方法来分解列表

与此相反:

    def result = MyDomain.createCriteria().list {
        'in'('id', idList)
    }
…将引发此异常:

could not execute query
org.hibernate.exception.SQLGrammarException: could not execute query
    at grails.orm.HibernateCriteriaBuilder.invokeMethod(HibernateCriteriaBuilder.java:1616)
    at TempIntegrationSpec.oracle 1000 expression max in a list(TempIntegrationSpec.groovy:21)
Caused by: java.sql.SQLSyntaxErrorException: ORA-01795: maximum number of expressions in a list is 1000
    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:440)
你会得到这样的结果:

    def result = MyDomain.createCriteria().list {
        or { idList.collate(1000).each { 'in'('id', it) } }
    }
不幸的是,当您尝试使用Oracle方言创建一个超过1000项的inList时,Hibernate或Grails并没有在幕后为您做到这一点

我同意关于重构设计以不产生1000多个项目列表这一主题的许多讨论,但无论如何,上面的代码将完成这项工作。

编写自己的查询/sql并将它们放入存储过程或视图中可能是一种选择。如果您知道或者有一个期望,那么grails应该在您的案例中生成什么样的查询,然后尝试一下。看起来,这些查询将是应用程序/系统中非常重要的一部分。
    def result = MyDomain.createCriteria().list {
        or { idList.collate(1000).each { 'in'('id', it) } }
    }