【问题标题】:Hibernate Criterion IN Clause 1000 break up第 1000 条中的休眠标准分手
【发布时间】:2013-01-29 21:56:23
【问题描述】:

嗨,我有这个大型的 oracle hibernate web 应用程序,它似乎给出了这个错误

ORA-01795: maximum number of expressions in a list is 1000

我需要一个由某人测试的 java 代码作为休眠用户定义的组件,以便尽可能容易地添加到我的屏幕中的搜索 java 类中,有人可以拥有这样的测试组件吗?

【问题讨论】:

    标签: java hibernate database-performance


    【解决方案1】:

    我从link 尝试了下面的代码,它似乎工作得很好,我会粘贴代码以防将来链接被破坏。

    保持简单保持微笑:)

        /**
        * An utility method to build the Criterion Query IN clause if the number of parameter
        * values passed has a size more than 1000. Oracle does not allow more than
        * 1000 parameter values in a IN clause. Doing so a {@link SQLException} is
        * thrown with error code, 'ORA-01795: maximum number of expressions in a list is 1000'.
        * @param propertyName
        * @param values
        * @return
        */
    import java.util.List;
    import org.hibernate.criterion.Restrictions;
    
    /**
     *
     * @author 2796
     */
    public class SplitHibernateIn {
    
        private static int PARAMETER_LIMIT = 999;
    
        public static org.hibernate.criterion.Criterion buildInCriterion(String propertyName, List values) {
            org.hibernate.criterion.Criterion criterion = null;
    
            int listSize = values.size();
            for (int i = 0; i < listSize; i += PARAMETER_LIMIT) {
                List subList;
                if (listSize > i + PARAMETER_LIMIT) {
                    subList = values.subList(i, (i + PARAMETER_LIMIT));
                } else {
                    subList = values.subList(i, listSize);
                }
                if (criterion != null) {
                    criterion = Restrictions.or(criterion, Restrictions.in(propertyName, subList));
                } else {
                    criterion = Restrictions.in(propertyName, subList);
                }
            }
            return criterion;
        }
    }
    

    【讨论】:

    • 仅供参考;这段代码在hibernate 3和oracle 10g上测试过
    • 如果有人(像我一样)认为 IN 子句对于没有参数(对于作为参数传递的空列表)不应该返回任何内容,则可以修改此方法返回一个始终为假的 Criterion。我通过在方法顶部添加以下代码来做到这一点: if ((values == null) || values.isEmpty()) { return Restrictions.sqlRestriction("(1=0)"); }
    【解决方案2】:

    同样的想法,但使用 javax Predicate。

    private static int PARAMETER_LIMIT = 999;
    
    private static Predicate createInStatement(CriteriaBuilder cb, Path fieldName, List values) {
        int listSize = values.size();
        Predicate predicate = null;
        for (int i = 0; i < listSize; i += PARAMETER_LIMIT) {
            List subList;
            if (listSize > i + PARAMETER_LIMIT) {
                subList = values.subList(i, (i + PARAMETER_LIMIT));
            } else {
                subList = values.subList(i, listSize);
            }
            if (predicate == null) {
                predicate = fieldName.in(subList);
            } else {
                predicate = cb.or(predicate, fieldName.in(subList));
            }
        }
        return predicate;
    }
    

    及用法

    public List<Bean> getBeanList(List<Long> pkList) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Bean> query = cb.createQuery(Bean.class);
        Root<Bean> root = query.from(Bean.class);
    
        // "Bean_" is a @StaticMetamodel(Bean.class)
        Predicate inStatement = createInStatement(cb, root.get(Bean_.pk), pkList);
    
        query.select(root).where(inStatement);
        return entityManager.createQuery(query).getResultList();
    }
    

    【讨论】:

    • 这不适用于限制为 2100 个参数的 MS SQL!似乎 JDBC 驱动程序连接了 OR-ed IN 子句?!
    【解决方案3】:

    虽然目前的答案很好,但我认为这个更容易实现和理解:

    private <T> Disjunction restrictionPropertyIn(String property, ArrayList<T> list) {
        Disjunction criterion = Restrictions.disjunction();
        for (List<T> idSubset : Lists.partition(list, 1000)) {
            criterion.add(Restrictions.in(property, idSubset));
        }
        return criterion;
    }
    
    • Restrictions.disjunction() 相当于使用Restrictions.or() 连接多个Criteria
    • Lists 是来自 Guava 的实用程序类; partition(list, limit)list 拆分为大小为 limit 的子列表。

    返回的Criterion 可以在任何需要Criterion 的地方按原样使用,例如:

    List<Long> fiveThousandIds = Arrays.asList(1, 2, 3, ..., 999, 1000, 1001, ..., 5000);
    Criteria crit = session.createCriteria(Employee.class);
    crit.add(restrictionPropertyIn("employeeId", fiveThousandIds));
    crit.list();
    

    如果您需要支持具有不同IN 子句限制的不同DB,您可以将硬编码的1000 改为参数。

    【讨论】:

    • 不是说ORing这些部分的性能很差吗?
    • @geneb。他们做了吗?请提供一个来源,因为我希望 Oracle SQL“编译器”至少从版本 8 开始就可以考虑并优化X IN (...) OR X IN (...) 这种非常常见的用法。此外,这个问题的每个答案都使用OR;我的只是 4 行而不是 20 行。这是我们多年来一直这样做的方式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-23
    • 2011-10-09
    • 2012-12-24
    相关资源
    最近更新 更多