Criar Criteria via Reflection

13 respostas
marcelo.bellissimo

Galera, mais uma dúvida pra voces…

Seguinte… para fazer buscas no banco usando Hibernate, crio um objeto Criteria e nele vou adicionando restrições baseados nos valores existentes no Bean que eu estou pesquisando… exemplo:

public List<Permissao> summary(Permissao permissao) {
		session = HibernateUtil.getSession();
		List<Permissao> list = null;
		try {
			Criteria criteria = session.createCriteria(Permissao.class);
			addCriteria(criteria, permissao);
			criteria.addOrder(Order.asc("numeroOrdem"));
			list = criteria.list();
			
		} catch (HibernateException e) {
			e.printStackTrace();
		}
		return list;
	}
	
	public void addCriteria(Criteria criteria, Permissao permissao) {
		if(!Util.isEmpty(permissao.getDataCadastro()))
			criteria.add(Restrictions.eq("dataCadastro", permissao.getDataCadastro()));
		
		if(!Util.isEmpty(permissao.getDescricao()))
			criteria.add(Restrictions.eq("descricao", permissao.getDescricao()));
		
		if(!Util.isEmpty(permissao.getId()))
			criteria.add(Restrictions.eq("id", permissao.getId()));
		
		if(!Util.isEmpty(permissao.getNumeroOrdem()))
			criteria.add(Restrictions.eq("numeroOrdem", permissao.getNumeroOrdem()));
		
	}

Como deu pra perceber ai, estou fazendo manualmente a inserção de cada Restriction caso o atributo esteja preenchido (meu método Util.isEmtpy verifica se o atributo está preenchido)… óbviamente isso fica inviável para Bean’s que tenham muitos atributos, seria um trabalho braçal absurdo pra fazer isso…

Minhas alternativas:
[list]1 - Usar Reflection pra pegar atributo por atributo, verificar seu tipo, nome, e adicionar o Restriction na Criteria;[/list]
[list]2 - Rezar pro Hibernate já fazer algo parecido com isso !! :mrgreen: [/list]

Então, alguém sabe se o Hibernate tem essa funcionalidade ? :?

13 Respostas

nextuser
public List buscarPorExemplo(Object persistentObject, String... excludeProperty) throws Exception {
		EntityTransaction tx = manager.getTransaction();
		try {
			List result = new ArrayList();
			tx.begin();

			Session hibernateSession = (Session) manager.getDelegate();
			Criteria criteria = hibernateSession.createCriteria(persistentObject.getClass());

			Example example = Example.create(persistentObject);
			for (String exclude : excludeProperty) {
				example.excludeProperty(exclude);
			}
			example.excludeZeroes();
			example.ignoreCase();
			example.enableLike(MatchMode.ANYWHERE);
			criteria.add(example);
			result.addAll(criteria.list());

			tx.commit();
			return result;

		} catch (Exception e) {
			tx.rollback();
			throw e;
		}
	}
von.juliano

O Hibernate faz sim, chama-se query by example

Blz? Flw! :thumbup:

marcelo.bellissimo

Caramba, por isso que eu adoro esse Fórum… :mrgreen:

O único problema desse Example é que ele ignora os relacionamentos, mas já me poupou um trabalho imenso !

Valeu, von.juliano ! :thumbup:

von.juliano

Como assim, ignora os relacionamentos?

marcelo.bellissimo

"Version properties, identifiers and associations are ignored. By default, null valued properties are excluded."

Realmente ele faz isso, por exemplo, uma classe minha chamada Bairro e que possui os atributos descritos abaixo:

public class Bairro implements Serializable{
	private Long id;
	private Cidade cidade;
	private String nome;
	private Timestamp dataCadastro;
	private UsuarioInterno usuarioCadastro;
}
Note que esses aqui são referências/relacionamentos com outras classes:
private Cidade cidade;
...
	private UsuarioInterno usuarioCadastro;
O Example não leva em conta os valores desses atributos, e se você verificar no objeto Criteria após a adiçlão do Example, realmente não vai constar nada relativo á esses atributos... fiz um teste, e no código HQL gerado ele faz um where bem esquisito:
where
	1=1
Mas isso não é problema, o que eu fiz foi o seguinte, uma rotina que adiciona no Criteria o Example, e os outros objetos que são relacionamento com outra tabela eu adiciono manualmente, assim:
public List<Bairro> summary(Bairro bairro) {
	...
	Criteria criteria = session.createCriteria(Bairro.class);
	addCriteria(criteria, bairro);
	list = criteria.list();
	...
	return list;
	}
	
public void addCriteria(Criteria criteria, Bairro bairro) {
		// criar objeto Example, irá popular automaticamente o Criteria com os dados existentes
		Example example = Example.create(bairro);
		example.enableLike(MatchMode.ANYWHERE);//habilita LIKE
		example.ignoreCase();//case insensitive
		criteria.add(example);//adiciona na Criteria
		
		// Example ignora identificadores e relacionamentos, adicionar manualmente na Criteria		
		if(!Util.isEmpty(bairro.getCidade()))
			criteria.add(Restrictions.eq("usuarioCadastro", bairro.getUsuarioCadastro()));
		
		if(!Util.isEmpty(bairro.getCidade()))
			criteria.add(Restrictions.eq("cidade", bairro.getCidade()));
		
	}

E é isso aí... outra coisa que deu pra perceber no código ai em cima, é que o Example também ignora os identificadores da Classe... e isso é meio lógico, se voce passar o ID do objeto lógicamente só vai existir um único objeto, logo todos esses critérios de busca deixam de fazer sentido...

von.juliano

Ah sim, os relacionamentos são ignorados utilizando Example, mas você pode aninhar mais de um Example, obtendo um resultado que meio que considera os relacionamentos. Vou colocar um exemplo:

Example exampleBairro = Example.create(bairro).enableLike(MatchMode.ANYWHERE).ignoreCase(); Example exampleCidade = Example.create(bairro.getCidade()).enableLike(MatchMode.ANYWHERE).ignoreCase(); ... return criteria.add( exampleBairro).createCriteria("cidade").add(exampleCidade).list();É só um exemplo, talvez não seja tão útil nesse caso, mas ao menos não será necessário fazer as verificações!

Fica a dica. Flw! :thumbup:

marcelo.bellissimo

Ah, tá, eu entendi esse exemplo… vou fazer uns testes aqui pra ver como fica o SQL, pois o meu medo é ele gerar um SQL muito grande, com um Select muito custoso por causa de tantos critérios (por isso eu adiciono apenas o Objeto no Criteria, pra pesquisar pelo ID dele, é mais rápido) … mas já estou vendo que mais pra frente provavelmente encontrarei casos que vou ter que usar algo desse tipo mesmo, valeu a dica ! :thumbup:

GraveDigger

von.juliano:
Ah sim, os relacionamentos são ignorados utilizando Example, mas você pode aninhar mais de um Example, obtendo um resultado que meio que considera os relacionamentos. Vou colocar um exemplo:

Example exampleBairro = Example.create(bairro).enableLike(MatchMode.ANYWHERE).ignoreCase(); Example exampleCidade = Example.create(bairro.getCidade()).enableLike(MatchMode.ANYWHERE).ignoreCase(); ... return criteria.add( exampleBairro).createCriteria("cidade").add(exampleCidade).list();É só um exemplo, talvez não seja tão útil nesse caso, mas ao menos não será necessário fazer as verificações!

Fica a dica. Flw! :thumbup:

Cara, estou tentando fazer isso, sem sucesso.

Tem certeza que é assim mesmo ?

obrigado

EDIT: Esqueçe, deu certo aqui, eu que tava fazendo errado(com a tabela zerada queria trazer registro :roll: )

Abraço e obrigado d novo

marcelo.bellissimo

Hahaha, acontece… :lol:

Eu também, no começo, me matei pra fazer um “select” de uma classe com PK composta, fiquei todo feliz quando acertei o mapeamento, ai fui fazer um teste, um “getAll()” da vida… a lista vinha vazia… :frowning:

Ai depois de muito tempo, verificando mapeamento, se estava tudo certinho, eu pensei… “po… tem dados nessa tabela ?”

select * from xyz;
09:00:00 [SELECT - 0 row(s), 0.019 secs] Empty result set fetched
"… hmmmmmm … falha nossa… " :roll:

Mas qualquer duvida, tamos ai… :smiley:

feltraco

Ressuscitando o TOPICO.

Alguem saberia me dizer se tem algum forma de eu fazer o Example adicionar os relacionamentos na clausula WHERE ?
Tenho um DAO Generico, e nao achei uma forma elegante de faze-lo.

Grato desde jah.

GraveDigger

Opa,

Então cara, eu consegui fazer aqui, funciona da seguinte maneira, eu informo o nome da entidade correspondente como parâmetro(ele não usa todos os possíveis por default, tenho que especificar cada um) e com base nisso eu faço:

Object entity = method.invoke(mainEntityExample, new Object[0]);
criteria.add(Restrictions.eq(entityFieldName, entity));
criteria.setFetchMode(entityFieldName, org.hibernate.FetchMode.JOIN);

De forma que method simboliza o getter adequado pra entidade que foi passada como parâmetro.

Tenho outras coisas aqui, mas o segredo é isso daí, qualquer dúvida avise,

Att

feltraco

GraveDigger:
Opa,

Então cara, eu consegui fazer aqui, funciona da seguinte maneira, eu informo o nome da entidade correspondente como parâmetro(ele não usa todos os possíveis por default, tenho que especificar cada um) e com base nisso eu faço:

Object entity = method.invoke(mainEntityExample, new Object[0]);
criteria.add(Restrictions.eq(entityFieldName, entity));
criteria.setFetchMode(entityFieldName, org.hibernate.FetchMode.JOIN);

De forma que method simboliza o getter adequado pra entidade que foi passada como parâmetro.

Tenho outras coisas aqui, mas o segredo é isso daí, qualquer dúvida avise,

Att

Cara valeu pela ajuda, ajudou mt mesmo resolvi o problema e dei uma melhorada na sua solucao =)
Vou deixar aqui oque fiz, pode ser que ajude alguem tambem.

Class<?> clazz = dto.getClass();	
				while (clazz != AbstractPersistentObject.class)
				{
					for (Field campo : clazz.getDeclaredFields()) 
					{
						if( campo.getType().getName().contains("domain.entity") )
						{
							campo.setAccessible(true);
							c.add(Restrictions.eq(campo.getName(), campo.get(dto)));
							c.setFetchMode(campo.getName(), org.hibernate.FetchMode.JOIN);
						}
					}
					clazz = clazz.getSuperclass();
				}

Onde: AbstractPersistentObject eh o meu objeto PAI para todo objeto que vai ser persistido e DTO eh o objeto que esta sendo persistido.
Fiz dentro de um while para pegar relacionamentos que estejam nas classes PAI.

Eh isso ai…
Valeu

Priuli

Aê pessoal, fiz um projeto de filtro justamente para não usar o Example a um tempo atraz, pois o mesmo ñ fazia tudo o que eu queria, ñ pega os ids dos relacionametos, então resolvi criar um projeto só para isso eu uso a 1 ano e alguns meses em uma aplicação jsf que tem bastantes acessos diarios e alguns amigos tb usam, segue ai caso interesse.
Ele tem mais coisas interessantes… segue link
Forum
http://www.guj.com.br/java/221618-filter-dinamico-para-hibernate
Download
È Free LGPL, tem testes de unidade e svn, e maven -> http://sourceforge.net/projects/priuli-filter/
Reposi… MAVEN
https://oss.sonatype.org/content/groups/staging/net/priuli/
+ Informações
http://felipepriuli.wordpress.com/2010/10/12/priuli-filter/

Criado 16 de dezembro de 2008
Ultima resposta 2 de fev. de 2011
Respostas 13
Participantes 6