我有一个使用(很老)hibernate映射到oracle 11数据库的java应用程序。我使用org.hibernate.criterion类来生成我的查询(至少对于这个部分)。
我最近遇到了一个错误,我使用org.hibernate.Criteria生成的一个查询产生了一个非常大的' in'条款。以下是Java控制台如何显示生成的查询:
select this_.foo as foo_1_2_3, this_.bar as bar_2_3_4, this_.id as id_5_6_7
from some_table inner join some_other_table inner join
where blah=bloo and this_.id in (?, ?, ?, ?, ?, ......................... ?, ?, ?, ?) order by this_.id asc
其中?
元素的数量> 1000。
此in子句太大,导致我们的数据库中出现ORA-01795: maximum number of expressions in a list is 1000
错误。
这是添加' in'的Java代码。条款:
criteria.add(Restrictions.in("id", getMassiveListOfIDs() ) );
(换句话说,我在子句中添加> 1000元素)
根据this stackoverflow question仅使用多个较小的''条款是解决方案。说得通。
我已对此进行了修改,以便在添加' in'之前拆分列表。查询,如下:
List<Long> listOfIds = getMassiveListOfIDs();
if(listOfIds.size()>=1000){
List<List<Long>> listOfSublists = this.splitIntoSubArrays(listOfIds);
for(List<Long> subArray : listOfSublists){
criteria.add(Restrictions.in("id", subArray));
}
}else{
criteria.add(Restrictions.in("id", listOfIds));
}
现在生成的查询如下所示:
select this_.foo as foo_1_2_3, this_.bar as bar_2_3_4, this_.id as id_5_6_7
from some_table inner join some_other_table inner join
where blah=bloo and
this_.id in (?, ?, .... ?, ?)
and this_.id in (?, ?, .... ?, ?)
and this_.id in (?, ?, .... ?, ?)
and this_.id in (?, ?, ?, ?, ?, ?)
order by this_.id asc
其中每个(?, ?, .... ?, ?, ?)
数组实际上包含1000个元素,最后一个是余数。
问题是这些是AND
而不是OR
我想要OR所有in子句(换句话说,我想获取任何具有在这些子句中列出的id的行),如:
select this_.foo as foo_1_2_3, this_.bar as bar_2_3_4, this_.id as id_5_6_7
from some_table inner join some_other_table inner join
where blah=bloo and (
this_.id in (?, ?, .... ?, ?)
OR this_.id in (?, ?, .... ?, ?)
OR this_.id in (?, ?, .... ?, ?)
OR this_.id in (?, ?, ?, ?, ?, ?)
)
order by this_.id asc
我知道我可以这样做:
Criterion c1 = Restrictions.in("id", sublist1);
Criterion c2 = Restrictions.in("id", sublist2);
Criterion c3 = Restrictions.in("id", sublist3);
Criterion c4 = Restrictions.in("id", sublist4);
Criterion or1 = Restrictions.or(c1, c2);
Criterion or2 = Restrictions.or(c3, c4);
criteria.add(Restrictions.or(or1, or1) );
但我不知道在编译时listOfIds有多大。 500或10000.
有没有办法
动态生成上面显示的ORing
或任意大小的标准列表并将它们添加到查询中?
我意识到这是一个非常小众的问题,所以我将不胜感激任何帮助或建议。为了说明,我还大量简化了这些例子。
真的,我正在寻找一个Java Code解决方案,而不是改变我的数据库或hibernate映射,如果可能的话。
非常感谢。
答案 0 :(得分:0)
你需要这样的东西:
if (arguments.size() > 1000) {
Criterion criterionIn = Restrictions.sqlRestriction("1<>1");
final List<Serializable> inList = new ArrayList<Serializable>();
for (int i = 0; i < arguments.size(); i++) {
inList.add(arguments.get(i));
if (inList.size() == 1000) {
criterionIn = Restrictions.or(criterionIn, Restrictions.in(restriction.getField(), inList));
inList.clear();
}
}
if (!inList.isEmpty()) {
criterionIn = Restrictions.or(criterionIn, Restrictions.in(restriction.getField(), inList));
}
final Criterion criterionNotIn = Restrictions.not(criterionIn);
return criterionNotIn;
}
您可以在github
上找到完整示例答案 1 :(得分:0)
结果是一个非常简单的方法,如:
Criterion restriction = Restrictions.in("id", listOfSublists.get(0));
for(int i=1; i<listOfSublists.size(); i++){
restriction = Restrictions.or(restriction, Restrictions.in("id", listOfSublists.get(i)));
}
工作正常并产生正确的查询。
当我第一次尝试这个问题时,我收到了奇怪的查询,并了解了.or
和.and
方法在示例中如何串联在一起,以及如何将结果查询括起来,但事实证明这很好,这些问题与我在Java中添加限制的方式无关。
下次我在尝试午睡和喝咖啡之前先把它放到堆叠溢出处......