【问题标题】:'Maximum number of expressions in a list is 1000' error with Grails and OracleGrails 和 Oracle 出现“列表中的最大表达式数为 1000”错误
【发布时间】:2012-02-17 17:06:40
【问题描述】:

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

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

用户可见的数据会根据其访问权限进行过滤,根据用户角色的不同,访问权限可以是方向、组或建筑工地级别。

我们通过为 BuildingSite 域类创建一个 listWithSecurity 方法轻松地实现了这一点,我们在大多数系统中都使用该方法而不是 list。我们为 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 错误,但它似乎为每个级别调用单独的查询,并将结果作为“in”中的文字传递回 Oracle 以进行查询另一个层次。换句话说,它与我所做的完全一样,所以我得到了同样的错误。

实际上,它可能正在优化一点。它似乎正在解决问题,但只是为了一个级别。在前面的示例中,我不会收到 1001 次检查的错误,但我会收到 1001 次合同或建筑工地的错误。

我还尝试对 findAll 和单个 HQL where 语句执行基本相同的操作,我将单个方向传递给该语句以在一个查询中获取 nonConformities。一样。它解决了第一关,但我在其他关卡中遇到了同样的错误。

我确实设法通过将我的“输入”标准拆分为“或”内的许多“输入”来修补它,因此没有一个单一的文字列表长度超过 1000,但这是非常丑陋的代码。一个 findAllBy[…]In 变成了 10 多行代码。从长远来看,它可能会导致性能问题,因为我们被困在使用大量参数进行查询。

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

【问题讨论】:

  • 可能是编写您自己的查询/sql 并将它们放入存储过程或可能的视图中的一个选项。如果您知道或有一个期望,在您的情况下应该生成什么样的查询 grails,然后试一试。看来,这些查询将成为您的应用程序/系统中非常重要的一部分。

标签: oracle grails grails-orm


【解决方案1】:

这不会赢得任何效率奖,但我认为如果您只是需要查询 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) } }
    }

不幸的是,当您尝试执行包含 > 1000 个项目的 inList 并且您使用的是 Oracle 方言时,Hibernate 或 Grails 没有在幕后为您执行此操作。

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

【讨论】:

    【解决方案2】:

    按照与 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-15
      • 2015-05-29
      • 1970-01-01
      • 2011-07-12
      • 1970-01-01
      • 2018-07-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多