Java Oracleexception – “列表中的最大表达式数为1000”

我将一个字符串列表传递给我的查询(写入SQL查询)以获取所需的数据。 但是我得到了这个例外:

ora-01795列表中的最大表达式数为1000

我检查了传递给查询IN参数的列表中有超过1000个条目。

这是查询中列表传递次数的oracle限制。

  1. 你将不得不砍掉你的查询或
  2. 改为在IN子句中提供子查询/连接。

如果您正在使用Oracle DB,则无法在单个“where”条件中拥有包含1000个以上元素的列表。 因此,您可以在多个“where”条件中删除“where”条件,并使用“or”子句将它们连接起来。

如果您使用的是hibernate Criteria,则可以使用以下Java方法执行此操作。 只需在您使用过的地方替换您的代码

criteria.add(Restrictions.in(propertyName, mainList)); 

 addCriteriaIn(propertyName, mainList, criteria); 

方法是:

  private void addCriteriaIn (String propertyName, List list,Criteria criteria) { Disjunction or = Restrictions.disjunction(); if(list.size()>1000) { while(list.size()>1000) { List subList = list.subList(0, 1000); or.add(Restrictions.in(propertyName, subList)); list.subList(0, 1000).clear(); } } or.add(Restrictions.in(propertyName, list)); criteria.add(or); } 

我通过将列表分成大小为1000的批处理并使用OR连接它来解决这个问题。

例如eid []数组。

如果我想执行此查询,

 String sql = select * from employee where some conditions and empid in(eid) 

我通过编写一小段代码重写了这个查询:

 String sql = select * from employee where some conditions and ( empid in(empid[0...999]) OR empid in(empid[1000...1999]) OR empid in(empid[2000...2999]) OR .... ); 

在使用hibernate时处理此错误,您必须通过将列表分成100个批处理然后加入单个结果来解决此问题(如上面的查询中所示)。

我不认为这是hibernate的限制因为没有处理这个问题,因为可能是这个问题不是像MySQL或DB2这样的另一个数据库的情况。 Hibernate是一个跨DB的ORM框架。

您可以创建一个临时表,并在IN语句中插入要使用的值,并将临时表与真实表一起加入。 有关临时表的更多信息 。

来自dba-oracle.com :

ORA-01795:列表中的最大表达式数为1000个提示

Burleson Consulting的Oracle错误提示(S. Karam)

Oracle文档在ora-01795错误*上注意到这一点:ORA-01795列表中的最大表达式数为1000原因:在列表中指定了超过254列或表达式。 操作:从列表中删除一些表达式。 在Oracle MOSC论坛中,Oracle用户试图找到解决错误代码ORA-01795的方法。 他的问题由Oracle的Reem Munakash回答:

Oracle8中的限制是1000个表达式。 有一个错误495555,针对错误文本提交错误号码(254)。 但是,根据您使用的工具,可能会有进一步的限制。 1000个表达式在sqlplus中。

解决方法是使用子查询。

有关错误消息的错误已在8.1.5中修复。

如果能够将数据库端逻辑从查询转换为存储过程,则可以将更长的数组(集合)传递给它。

在这里,您可以找到一个简单的示例。 文档的链接已过时,所以这里是9i文档的链接http://docs.oracle.com/cd/B10500_01/java.920/a96654/oraarr.htm#1040124

 import java.io.*; import java.sql.*; import oracle.sql.*; import oracle.jdbc.driver.*; public class ArrayDemo { public static void passArray() throws SQLException { Connection conn = new OracleDriver().defaultConnection(); int intArray[] = { 1,2,3,4,5,6 }; ArrayDescriptor descriptor = ArrayDescriptor.createDescriptor( "NUM_ARRAY", conn ); ARRAY array_to_pass = new ARRAY( descriptor, conn, intArray ); OraclePreparedStatement ps = (OraclePreparedStatement)conn.prepareStatement ( "begin give_me_an_array(:x); end;" ); ps.setARRAY( 1, array_to_pass ); ps.execute(); } } 

和SQL部分

 create or replace type NUM_ARRAY as table of number; create or replace procedure give_me_an_array( p_array in num_array ) as begin for i in 1 .. p_array.count loop dbms_output.put_line( p_array(i) ); end loop; end; 

使用Java Hibernate,为了解决这个问题,我决定改变Hibernate核心JAR。 我创建了一个帮助类来分割更多连接中的表达式,如: ... t.column IN (: list_1) OR t.column IN (: list_2) ... ,然后我从hibernate更改了AbstractQueryImpl.expandParameterList方法来调用我的方法,如果集合超出限制。
我的hibernate-core版本是3.6.10.Final,但它工作正常,4.x版本 – 我测试了它。
我的代码将针对下一个案例进行测试

  where t.id in (:idList) where (t.id in (:idList)) where ((t.id) in (:idList)) where 1=1 and t.id in (:idList) where 1=1 and (t.id in (:idList)) where 1=1 and(t.id) in (:idList) where 1=1 and((t.id) in (:idList)) where 1=1 and(t.id in (:idList)) where t.id not in (:idList) where (t.id not in (:idList)) where ((t.id) not in (:idList)) 

AbstractQueryImpl.expandParameterList:

 private String expandParameterList(String query, String name, TypedValue typedList, Map namedParamsCopy) { Collection vals = (Collection) typedList.getValue(); Type type = typedList.getType(); boolean isJpaPositionalParam = parameterMetadata.getNamedParameterDescriptor( name ).isJpaStyle(); String paramPrefix = isJpaPositionalParam ? "?" : ParserHelper.HQL_VARIABLE_PREFIX; String placeholder = new StringBuffer( paramPrefix.length() + name.length() ) .append( paramPrefix ).append( name ) .toString(); if ( query == null ) { return query; } int loc = query.indexOf( placeholder ); if ( loc < 0 ) { return query; } String beforePlaceholder = query.substring( 0, loc ); String afterPlaceholder = query.substring( loc + placeholder.length() ); // check if placeholder is already immediately enclosed in parentheses // (ignoring whitespace) boolean isEnclosedInParens = StringHelper.getLastNonWhitespaceCharacter( beforePlaceholder ) == '(' && StringHelper.getFirstNonWhitespaceCharacter( afterPlaceholder ) == ')'; if ( vals.size() == 1 && isEnclosedInParens ) { // short-circuit for performance when only 1 value and the // placeholder is already enclosed in parentheses... namedParamsCopy.put( name, new TypedValue( type, vals.iterator().next(), session.getEntityMode() ) ); return query; } // *** changes by Vasile Bors for HHH-1123 *** // case vals.size() > 1000 if ((vals.size() >= InExpressionExpander.MAX_ALLOWED_PER_INEXPR) && isEnclosedInParens) { InExpressionExpander inExpressionExpander = new InExpressionExpander(beforePlaceholder, afterPlaceholder); if(inExpressionExpander.isValidInOrNotInExpression()){ List list = new ArrayList( vals.size() ); Iterator iter = vals.iterator(); int i = 0; String alias; while ( iter.hasNext() ) { alias = ( isJpaPositionalParam ? 'x' + name : name ) + i++ + '_'; namedParamsCopy.put( alias, new TypedValue( type, iter.next(), session.getEntityMode() ) ); list.add(ParserHelper.HQL_VARIABLE_PREFIX + alias ); } String expandedExpression = inExpressionExpander.expandExpression(list); if(expandedExpression != null){ return expandedExpression; } } } // *** end changes by Vasile Bors for HHH-1123 *** StringBuffer list = new StringBuffer(16); Iterator iter = vals.iterator(); int i = 0; while (iter.hasNext()) { String alias = (isJpaPositionalParam ? 'x' + name : name) + i++ + '_'; namedParamsCopy.put(alias, new TypedValue(type, iter.next(), session.getEntityMode())); list.append(ParserHelper.HQL_VARIABLE_PREFIX).append(alias); if (iter.hasNext()) { list.append(", "); } } return StringHelper.replace( beforePlaceholder, afterPlaceholder, placeholder.toString(), list.toString(), true, true ); } 

我的助手类InExpressionExpander:

包org.hibernate.util;

 import org.hibernate.QueryException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Iterator; import java.util.List; import java.util.Stack; /** * Utility class for expand Hql and Sql IN expressions with a parameter with more than IN expression limit size (HHH-1123). * 
* It work for expression with formats: *
 * * where t.id in (:idList) * where (t.id in (:idList)) * where ((t.id) in (:idList)) * where 1=1 and t.id in (:idList) * where 1=1 and (t.id in (:idList)) * where 1=1 and(t.id) in (:idList) * where 1=1 and((t.id) in (:idList)) * where 1=1 and(t.id in (:idList)) * * where t.id not in (:idList) * where (t.id not in (:idList)) * where ((t.id) not in (:idList)) * 

*

* Example: *

 * select t.id from tableOrEntity t where t.id IN (:idList) * 

stackExpr = new Stack(); private StringBuilder toWalkQuery; private final String beforePlaceholder; private final String afterPlaceholder; private boolean wasChecked = false; private boolean isEnclosedInParens = false; private boolean isInExpr = false; private boolean isNotInExpr = false; public InExpressionExpander(String beforePlaceholder, String afterPlaceholder) { this.toWalkQuery = new StringBuilder(beforePlaceholder); this.beforePlaceholder = beforePlaceholder; this.afterPlaceholder = afterPlaceholder; } public boolean isValidInOrNotInExpression() { if (!wasChecked) { String lastExpr = extractLastExpression(); if ("(".equals(lastExpr)) { isEnclosedInParens = true; lastExpr = extractLastExpression(); } isInExpr = "in".equalsIgnoreCase(lastExpr); } wasChecked = true; return isInExpr; } public String expandExpression(List paramList) { if (isValidInOrNotInExpression()) { final String lastExpr = extractLastExpression(false); if ("not".equalsIgnoreCase(lastExpr)) { isNotInExpr = true; extractLastExpression(); //extract "not" and consume it } extractColumnForInExpression(); StringBuilder exprPrefixBuilder = new StringBuilder(); for (int i = stackExpr.size() - 1; i > -1; i--) { exprPrefixBuilder.append(stackExpr.get(i)).append(' '); } if (!isEnclosedInParens) { exprPrefixBuilder.append('('); } String expandedExpression = expandInExpression(exprPrefixBuilder, paramList); String beforeExpression = getBeforeExpression(); String afterExpression = getAfterExpression(); String expandedQuery = new StringBuilder(beforeExpression).append(expandedExpression) .append(afterExpression) .toString(); if (log.isDebugEnabled()) { log.debug( "Query was changed to prevent exception for maximum number of expression in a list. Expanded IN expression query:\n {}", expandedExpression); log.debug("Expanded query:\n {}", expandedQuery); } return expandedQuery; } log.error("Illegal call of InExpressionExpander.expandExpression() without IN expression."); return null; } private String expandInExpression(StringBuilder exprPrefixBuilder, List values) { String joinExpr = isNotInExpr ? ") and " : ") or "; StringBuilder expr = new StringBuilder(16); Iterator iter = values.iterator(); int i = 0; boolean firstExpr = true; while (iter.hasNext()) { if (firstExpr || i % MAX_PARAMS_PER_INEXPR == 0) { //close previous expression and start new expression if (!firstExpr) { expr.append(joinExpr); } else { firstExpr = false; } expr.append(exprPrefixBuilder); } else { expr.append(", "); } expr.append(iter.next()); i++; } expr.append(')');// close for last in expression return expr.toString(); } /** * Method extract last expression parsed by space from toWalkQuery and remove it from toWalkQuery;
* If expression has brackets it will return al content between brackets and it will add additional space to adjust splitting by space. * * @return last expression from toWalkQuery */ private String extractLastExpression() { return extractLastExpression(true); } /** * Method extract last expression parsed by space from toWalkQuery, remove it from toWalkQuery if is consume = true;
* If expression has brackets it will return al content between brackets and it will add additional space to adjust splitting by space. * * @param consum if true the method will extract and remove last expression from toWalkQuery * @return last expression from toWalkQuery */ private String extractLastExpression(final boolean consum) { int lastIndex = this.toWalkQuery.length() - 1; String lastExpr; int exprSeparatorIndex = this.toWalkQuery.lastIndexOf(" "); if (lastIndex == exprSeparatorIndex) { //remove last space from the end this.toWalkQuery.delete(exprSeparatorIndex, this.toWalkQuery.length()); return extractLastExpression(consum); } else { lastExpr = this.toWalkQuery.substring(exprSeparatorIndex + 1, this.toWalkQuery.length()); if (lastExpr.length() > 1) { if (lastExpr.endsWith(")")) { //if parens are closed at the end we need to find where it is open int opensParens = 0; int closedParens = 0; int startExprIndex = -1; char c; for (int i = lastExpr.length() - 1; i > -1; i--) { c = lastExpr.charAt(i); if (c == ')') { closedParens++; } else if (c == '(') { opensParens++; } if (closedParens == opensParens) { startExprIndex = i; break; } } if (startExprIndex > -1) { lastExpr = lastExpr.substring(startExprIndex, lastExpr.length()); exprSeparatorIndex = exprSeparatorIndex + startExprIndex + 1; // +1 because separator is not space and don't must be deleted } } else if (lastExpr.contains("(")) { int parentsIndex = exprSeparatorIndex + lastExpr.indexOf('(') + 1; this.toWalkQuery.replace(parentsIndex, parentsIndex + 1, " ( "); return extractLastExpression(consum); } } if (consum) { this.toWalkQuery.delete(exprSeparatorIndex, this.toWalkQuery.length()); } } if (consum) { stackExpr.push(lastExpr); } return lastExpr; } private String extractColumnForInExpression() { String column = extractLastExpression(); String beforeColumn = extractLastExpression(false); long pointIndx = beforeColumn.lastIndexOf('.'); if (pointIndx > -1) { if (pointIndx == (beforeColumn.length() - 1)) { throw new QueryException( "Invalid column format: " + beforeColumn + ' ' + column + " . Remove space from column!"); } } return column; } private String getBeforeExpression() { return this.toWalkQuery + " ("; } private String getAfterExpression() { if (StringHelper.getFirstNonWhitespaceCharacter(afterPlaceholder) == ')') { return afterPlaceholder; } return afterPlaceholder + ") "; } }

我很高兴收到任何改进此解决方案的建议。