Hibernate Criterion IN语句1000个分割

20

你好,我有一个大型的Oracle Hibernate Web应用程序,似乎出现了以下错误:

ORA-01795: 列表中的表达式数量已达到最大值1000

我需要一个Java代码,并且由一个Hibernate的用户定义组件进行测试,以便将其尽可能简单地添加到我的屏幕搜索Java类中。请问是否有人有这样经过测试的组件?

3个回答

29

我尝试了下面这段代码,来自链接,它似乎非常有效,以防将来链接失效,我将复制代码。

保持简单,保持微笑:)

    /**
    * 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;
    }
}

FYI;此代码已在Hibernate 3和Oracle 10g上进行了测试。 - shareef
如果有人(比如我)认为在IN语句中当传入空列表作为参数时应该返回空,那么可以通过修改此方法返回始终为false的Criterion来实现。 我是通过在方法顶部添加以下代码来完成的: if ((values == null) || values.isEmpty()) { return Restrictions.sqlRestriction("(1=0)"); } - Borneo777

4
相同的思路,但使用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();
}

这对于 MS SQL 不起作用,因为参数限制为 2100!看起来 JDBC 驱动程序将 OR-ed IN 子句连接起来了?! - raho

1

虽然目前的答案都不错,但我认为这个答案更简单易懂,实现起来也更容易:

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子句限制,您可以将硬编码的1000更改为参数。

他们没有说过这些部分进行“或”运算的性能很差吗? - gene b.
@geneb。他们做到了吗?请提供来源,因为我希望 Oracle SQL “编译器” 至少从8版本开始就考虑和优化 X IN(...)OR X IN(...) 的常见用法。此外,对于这个问题的每个答案都使用了 OR;我的解决方法只需要4行而不是20行。这是我们几年来一直在使用的方式。 - walen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接