Problema HibernateCriteria-Restrictions.in() nao aceita mais de 1000 valores. RESOLVIDO

8 respostas
K

Olá pessoal!
Estou com o seguinte problema: Em consultas, no oracle, usando a instrução sql IN(), existe a restrição de um máximo de 1000 valores passados dentro do IN().
Preciso passar uma quantidade maior que 1000. Utilizo hibernate “Criteria” para construir a consulta. Pensei em dividir os valores em vários vetores com, no
máximo, 1000 posições. Atualmente, a função está da seguinte forma:

public Collection<Contrato> recuperaInfoPorListaCPF(Long[] listaCPF) throws HibernateException, TransactionException {
	Session sess = holder.getSession();
	Criteria criteria = sess.createCriteria(Contrato.class);
	criteria.createAlias(Contrato.pessoa, Contrato.pessoa);
	criteria.createAlias(Contrato.desligamento, Contrato.desligamento);
		
	criteria.add(Restrictions.in(concat(Contrato.pessoa, Pessoa.cpf), listaCPF));
			
	return criteria.list();
}

A questão é, ao passar como parâmetro uma Collection<Long[]> (uma lista de vetores contendo todos os CPF), como eu poderia adicionar instruções do tipo Restrictions.or()
para retornar todos os valores que preciso? Ou, então, há algum parâmetro que posso setar no oracle para que aceite mais de 1000 valores dentro de uma instrução IN()?

Pessoal, desde já obrigado pela atenção!
Kallás

8 Respostas

vanessasouza

Aqui na empresa tivemos esse problema, o IN do oracle só retorna 1000 vc tera q dividir a consulta execultando várias vezes.

A

Não sei qual é seu problema, mas eu tentaria mudar a forma de fazer as coisas.

Eu acho um certo exagero utilizar 1000 itens em um IN…

Não dá para trocar por um inner join?

K

Ei pessoal! Primeiramente, obrigado pela prontidão na ajuda…

Vanessa, dá pra chamar a função várias vezes, mas, o que não quero é fazer isso.
Abel, também acho e, se entendi bem, sua dica é fazer vários “join” com a mesma tabela, porém, cada “join” referenciando uma parte dos valores? Ou seja, (desculpe, não dê atenção à sintaxe) faria algo como:

select tab a "join" tab a (com sinônimo diferente...) 
on ...
where a.cpf in(1,2,3...1000) and
b.cpf in(1001,1002...)
and ...

É isso mesmo? Eu faria o join usando qual campo?
Valeu. Kallás.

K

E aí pessoal, alguém pode me ajudar a traduzir essa questão para o hibernate criteria? Como eu poderia montar as instruções Restrictions.or(…) para todos os vetores da minha lista (Collection<Long[]> lst). Digamos que tenha 4 vetores nessa lst, como eu monto isso usando criteria?

Obrigado.

A

kallas:
Ei pessoal! Primeiramente, obrigado pela prontidão na ajuda…

Vanessa, dá pra chamar a função várias vezes, mas, o que não quero é fazer isso.
Abel, também acho e, se entendi bem, sua dica é fazer vários “join” com a mesma tabela, porém, cada “join” referenciando uma parte dos valores? Ou seja, (desculpe, não dê atenção à sintaxe) faria algo como:

select tab a "join" tab a (com sinônimo diferente...) 
on ...
where a.cpf in(1,2,3...1000) and
b.cpf in(1001,1002...)
and ...

É isso mesmo? Eu faria o join usando qual campo?
Valeu. Kallás.

Não, não era isso que eu tinha mente não.

O que eu quis dizer foi o seguinte: de onde vem essa lista de 1000 ids?

Acredito que não seja uma seleção pelo usuário.
Provavelmente essa lista de ids vem do resultado de uma outra query, ou de uma fonte externa, algo assim.

Se for resultado de outra query, por exemplo, você poderia embutir nessa query (com um inner join).

K

Abel, essa lista é de uma consulta realizada em outro momento e é recuperada da session. No método que mostrei acima eu já passo uma “Collection<Long[]> lst” e minha dificuldade está em montar o Criteria com o Restriction.or(…).
Obrigado.

K

Moçada, estamos quase lá… Descobri a instrução “criteria.add(Restrictions.sqlRestriction(String))”. O método ficou assim:

public Collection<Contrato> buscaContratos(Collection<Object[]> listaCPF) throws BancoException {
		Session sess = holder.getSession();
		Criteria criteria = sess.createCriteria(Contrato.class);
		criteria.createAlias(Contrato.pessoa, "p");
		
		StringBuffer sql = new StringBuffer("");
		
		if (listaCPF != null && !listaCPF.isEmpty()) {
			sql.append(" ( ");
			
			Object[] arrayObject = null;
			StringBuffer strAux = new StringBuffer("");
			int i = 0;			
			for (Object lst : listaCPF) {				
				if (lst instanceof Collection) {
					arrayObject = ((Collection)lst).toArray();
				} else {
					arrayObject =  ((Object[])lst);
				}
				
				for (int j = 0; j < arrayObject.length; j++) {
					if (j < (arrayObject.length - 1)) {
						strAux.append(arrayObject[j] + ",");
					} else {
						strAux.append(arrayObject[j]);
					}
				}
				
				switch (i) {
				case 0:
					sql.append("p.numcpf in( " + strAux + ") "); // << PROBLEMA AQUI: NÃO ACEITA O MESMO ALIAS INDICADO NO criteria.createAlias(Contrato.pessoa, "p")
					break;                                            // NA HORA Q VAI EXECUTAR A CONSULTA.
				default:
					sql.append(" OR " + "p.numcpf in( " + strAux + ") ");					
					break;
				}
				
				strAux.setLength(0);
				i++;
			}
			sql.append(" ) ");
			criteria.add(Restrictions.sqlRestriction(sql.toString()));			
			
		}
		
		return criteria.list();
	}

O Hibernate cria a consulta, mas, com um alias diferente para o objeto Pessoa. Ou seja, Para o código “criteria.createAlias(Contrato.pessoa, “p”);” ele cria o alias como “p1_” e não como “p”, como indiquei.

Falta pouco… alguém tem idéia sobre como acertar o alias?
Obrigado!

K

Ei pessoal. Problema resolvido.

No método que mostrei acima, em vez de utilizar “criteria.createAlias(…)”, chamei o método “criteria.createCriteria(…)”. Dessa forma, é possível chamar esse último método “em cascata” e referenciar o {alias} do criteria desejado. Enfim, o método final ficou assim, para quem precisar:

public Collection<Contrato> recuperaContratos(Collection<Object[]> lstCPF) throws BancoException {
	Session sess = holder.getSession();
	Criteria criteria = sess.createCriteria(Contrato.class);				
	//criteria.createAlias(Contrato.pessoa, "p");
		
	Criteria pessoaCriteria = criteria.createCriteria(Contrato.pessoa);
		
	this.addRestrictionContrato(criteria.getAlias(), criteria, retornaData());
		
	StringBuffer sql = new StringBuffer("");		
	if (lstCPF != null && !lstCPF.isEmpty()) {
		sql.append(" ( ");			
		Object[] arrayObject = null;
		StringBuffer strAux = new StringBuffer("");
		int i = 0;			
		for (Object lst : lstCPF) {				
			if (lst instanceof Collection) {
				arrayObject = ((Collection)lst).toArray();
			} else {
				arrayObject =  ((Object[])lst);
			}				
			for (int j = 0; j < arrayObject.length; j++) {
				if (j < (arrayObject.length - 1)) {
					strAux.append(arrayObject[j] + ",");
				} else {
					strAux.append(arrayObject[j]);
				}
			}				
			switch (i) {
			case 0:
				sql.append("{alias}.numcpf in( " + strAux + ") ");
				break;
			default:
				sql.append(" OR " + "{alias}.numcpf in( " + strAux + ") ");					
				break;
			}				
			strAux.setLength(0);
			i++;
		}			
		sql.append(" ) ");
		pessoaCriteria.add(Restrictions.sqlRestriction(sql.toString()));			
	}		
	return criteria.list();
}

Obrigado a todos pelas dicas, pois, foram grande ajuda para a solução!
Kallás.

Criado 21 de outubro de 2011
Ultima resposta 25 de out. de 2011
Respostas 8
Participantes 3