Grails和Oracle的“列表中的最大表达式数为1000”错误

时间:2012-02-17 17:06:40

标签: oracle grails gorm

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

  • 方向
  • 建筑工地
  • 合同
  • 检验
  • 不合格

用户可见的数据将根据其访问权限进行过滤,具体取决于用户角色,可以位于“方向”,“组”或“建筑工地”级别。

我们通过为BuildingSite域类创建listWithSecurity方法轻松实现了这一点,我们在大多数系统中使用而不是列表。我们为Contract创建了另一个listWithSecurity方法。它基本上是一个Contract.findAllByContractIn(BuildingSite.listWithSecurity)。等等其他课程。这样做的好处是可以在BuildingSite.listWithsecurity中保留所有实际的访问逻辑。

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

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

def c = NonConformity.createCriteria()
def listToReturn = c.list(max:params.max, offset: params.offset?.toInteger() ?: 0)
{
    inspection {
        contract {
            buildingSite {
                group {
                    'in'("direction",listOfOneOrTwoDirections)
                }
            }
        }
    }
}

我希望Grails生成一个带有连接的单个查询,这个连接可以避免ora-01795错误,但它似乎是为每个级别调用一个单独的查询,并将结果作为文字传递回Oracle中查询另一个层次。换句话说,它完全正是我所做的,所以我得到了同样的错误。

实际上,它可能正在优化一下。它似乎解决了这个问题,但只针对一个层面。在前面的例子中,我不会因1001次检查而得到错误,但我会得到1001份合同或建筑工地。

我还尝试使用findAll和单个HQL where语句做同样的事情,我在一个查询中通过单一方向获取nonConformities。一样。它解决了第一级,但我得到了其他级别的相同错误。

我确实设法通过将我的'in'标准分成'或'内的许多'in'来修补它,因此没有一个文字列表长度超过1000,但这是非常难看的代码。单个findAllBy [...] In变为超过10行代码。从长远来看,它可能会导致性能问题,因为我们遇到了大量参数的查询问题。

有没有人以更优雅和有效的方式遇到并解决了这个问题?

2 个答案:

答案 0 :(得分:2)

这不会赢得任何效率奖励,但我认为如果您只是明确需要查询超过1000个项目的列表,我会发布它作为一个选项,没有更有效的选项可用/适当。 (此stackoverflow问题位于“grails oracle 1000”的Google搜索结果顶部)

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

而不是:

    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) } }
    }

很遗憾当您尝试执行>的inList时,Hibernate或Grails不会在幕后为您执行此操作1000项,你使用的是Oracle方言。

我同意关于重构您的设计的主题的许多讨论,最终不会有1000多个项目列表,但无论如何,上述代码都可以完成这项工作。

答案 1 :(得分:0)

与Juergen的评论一样,我通过创建一个数据库视图来解决类似的问题,该视图在最细微的级别(在您的情况下为建筑工地?)中展平用户/角色访问规则。至少,这个视图可能只包含两列:建筑工地ID和用户/组名称。因此,在用户具有方向级访问权限的情况下,他/她将在安全性视图中具有许多行 - 用户被允许访问的方向的每个子建筑工地一行。

然后,创建一个只读GORM类,将其映射到您的安全视图,将其连接到其他域类,并使用视图的用户/角色字段进行过滤。运气好的话,你可以完全用GORM完成这个(这里有一些提示:http://grails.1312388.n4.nabble.com/Grails-Domain-Class-and-Database-View-td3681188.html

然而,你可能需要对Hibernate有一些乐趣:http://grails.org/doc/latest/guide/hibernate.html