Busca genérica usando hibernate + criteria

8 respostas
russoedu

Estou desenvolvendo um sistema e busquei desenvolver um search que seja bastante genérico e possa ficar num DAO "pai de todos"...

Ainda está dando uns paus e estou tentando resolver....

@SuppressWarnings("unchecked")
	protected List<?> searchAsc(String busca, Class classe, String[] campos,
			String[] ordens) {
		LOGGER.debug("busca = " + busca + " - campo = " + campos
				+ " - classe = " + classe.getName() + " - total de ordens = "
				+ ordens.length);

		crit = getSession().createCriteria(classe);
		Disjunction disjunction = Restrictions.disjunction();
		List<String> alias = new ArrayList<String>();
		String aClass = "";
		String aClassAlias = "";
		String campo = "";
		String ordem = "";
		// Gera uma lista de alias para campo
		for (int i = 0; i < campos.length; i++) {
			LOGGER.info(campos);
			LOGGER.info(alias);
			aClassAlias = "";
			campo = campos[i];
			// Adiciona uma restrição usando alias
			if (campos[i].split("\\.").length > 1) {
				LOGGER.info("precisa de alias - " + campos[i]);
				aClass = (campos[i].split("\\.")[0]);
				aClassAlias = aClass.substring(0, 3);
				campo = campos[i].split("\\.", 2)[1];
				LOGGER.info("aClass = " + aClass + " - " + "aClassAlias = " + aClassAlias);
				// Verifica se  foi encontrada uma mesma classe
				// Se não foi, adiciona o alias
				if (!alias.contains(aClass)) {
					alias.add(aClass);
					LOGGER.info("alias novo");
					crit.createAlias(aClass, aClassAlias);
					LOGGER.info(alias);
				}
				aClassAlias = aClassAlias + ".";
			}
			//Adiciona a restrição a um disjunction (OR)
			disjunction.add(Restrictions.like(aClassAlias + campo, "%" + busca + "%"));
		}
		//adiciona o disjunction à criteria
		crit.add(disjunction);
		
		// Gera uma lista de alias para ordem
		for (int i = 0; i < ordens.length; i++) {
			LOGGER.info(ordens);
			LOGGER.info(alias);
			aClassAlias = "";
			ordem = ordens[i];
			// Adiciona uma restrição usando alias
			if (ordens[i].split("\\.").length > 1) {
				LOGGER.info("precisa de alias - " + ordens[i]);
				aClass = (ordens[i].split("\\.")[0]);
				aClassAlias = aClass.substring(0, 3);
				ordem = ordens[i].split("\\.", 2)[1];
				LOGGER.info("aClass = " + aClass + " - " + "aClassAlias = " + aClassAlias);
				// Verifica se  foi encontrada uma mesma classe
				// Se não foi, adiciona o alias
				if (!alias.contains(aClass)) {
					alias.add(aClass);
					LOGGER.info("alias novo");
					crit.createAlias(aClass, aClassAlias);
					LOGGER.info(alias);
				}
				aClassAlias = aClassAlias + ".";
			}
			//Adiciona a restrição
			crit.addOrder(Order.asc(aClassAlias + ordem));
		}
	return crit.list();
	}
Um exemplo desse método sendo chamado segue abaixo:
searchAsc(nome, Grupo.class, new String[] {"empresa.nome", "nome"}, new String[] {"empresa.nome", "nome"});

O pau que está dando é quando são instanciados mais de dois níveis na chamada.... preciso criar um outro alias.

8 Respostas

Lavieri

não entendi a real intenção… tipo, qual o re-trabalho que vc estaria tentando evitar ?

russoedu

A intenção é criar uma busca extremamente genérica que eu possa usar em qualquer DAO, bastando passar os parâmetros.

Lavieri

foi justamente os parametros q não entendi o.O

tipo, então como fazer, pra criar uma busca onde vc precisa que a idade seja maior que 15 anos ? e a cidade da nacionalidade seja São Paulo ?

Lavieri

vc ja pensou no padrão builder ?? eu fiz 1 pra mim, porem ainda não conseguir bolar uma forma de incluir os criterios OR (ou) e NOT (negar outro criterio)...

ai ta assim... a busca
public List&lt;Cidade&gt; findAllByEstado(Estado estado,int firstResult,int maxResult) {
        return super.find()
                .createAlias("estado", "Estado")
                .createAlias("Estado.pais", "Pais")
                .insensitiveLike("estado", estado)
                .asc("nome")
                .setFirstResult(firstResult)
                .setMaxResults(maxResult)
                .list();
    }

    public List&lt;Cidade&gt; findAllContainsNome(String cidade) {
         return super.find()
                .createAlias("estado", "Estado")
                .createAlias("Estado.pais", "Pais")
                .insensitiveLike("nome", cidade, MatchMode.ANYWHERE)
                .asc("nome")
                .asc("Estado.nome")
                .asc("Pais.nome")
                .list();
    }

eu evito akele monte de .add(Restriction.ilike()) addOrder(Order.asc()) etc etc... acho que assim fica + fluente...

o meu builder esta aqui: CriteriaBuilder.java
ele retorna uma interface: Builder<E>.java

a interface é que faz o código ficar fluente

Collection&lt;Cidade&gt; cidades = CriteriaBuilder.search(Cidade.class)
                .insensitiveLike("nome", "joão",MatchMode.START)
                .createAlias("estado", "Estado")
                .createAlias("Estado.pais", "Pais")
                .insensitiveLike("Pais.nome","brasil")
                .list();

        for(Cidade c : cidades)
            System.out.println(c.getNome() + "/" + c.getEstado());

estou pensando em criar automaticamente os Aliases das classes, mais n sei c vale a pena

.........

dessa forma com qualquer navegador ele sai fazendo código fluente... so digitar o "." que ele mostra as opções...

Kleber-rr
Lavieri:
vc ja pensou no padrão builder ?? eu fiz 1 pra mim, porem ainda não conseguir bolar uma forma de incluir os criterios OR (ou) e NOT (negar outro criterio)... ai ta assim... a busca
public List&lt;Cidade&gt; findAllByEstado(Estado estado,int firstResult,int maxResult) {
        return super.find()
                .createAlias("estado", "Estado")
                .createAlias("Estado.pais", "Pais")
                .insensitiveLike("estado", estado)
                .asc("nome")
                .setFirstResult(firstResult)
                .setMaxResults(maxResult)
                .list();
    }

    public List&lt;Cidade&gt; findAllContainsNome(String cidade) {
         return super.find()
                .createAlias("estado", "Estado")
                .createAlias("Estado.pais", "Pais")
                .insensitiveLike("nome", cidade, MatchMode.ANYWHERE)
                .asc("nome")
                .asc("Estado.nome")
                .asc("Pais.nome")
                .list();
    }

eu evito akele monte de .add(Restriction.ilike()) addOrder(Order.asc()) etc etc... acho que assim fica + fluente...

o meu builder esta aqui: CriteriaBuilder.java
ele retorna uma interface: Builder<E>.java

a interface é que faz o código ficar fluente

Collection&lt;Cidade&gt; cidades = CriteriaBuilder.search(Cidade.class)
                .insensitiveLike("nome", "joão",MatchMode.START)
                .createAlias("estado", "Estado")
                .createAlias("Estado.pais", "Pais")
                .insensitiveLike("Pais.nome","brasil")
                .list();

        for(Cidade c : cidades)
            System.out.println(c.getNome() + "/" + c.getEstado());

estou pensando em criar automaticamente os Aliases das classes, mais n sei c vale a pena

.........

dessa forma com qualquer navegador ele sai fazendo código fluente... so digitar o "." que ele mostra as opções...

Eae man, blz? Achei interessante tua idéia de builder. Tem como vc postar o teu método find() do teu Dao??
Agradeço.

Jair_Rillo_Junior

A idéia é fazer um DAO Genérico?
Se sim, no site do Hibernate tem um exemplo: https://www.hibernate.org/328.html

Creio que com poucas modificações você pode usar esse DAO genérico no seu projeto. (No meu último projeto tinhamos um DAO Genérico parecido com esse)

Kleber-rr
Estou tentando fazer da seguinte forma: - criei as criterias no FuncionarioDao, para pesquisa por nome, cpf, rg e orgaogestor do funcionario, todos em métodos independentes; - criei os métodos para usarem as criterias acima, um para cada (e todos funcionam perfeitamente); O problema é que no método que eu criei para testar qual dos campos está preenchido e fornecer o resultado baseando-se nesse campo. o do órgao gestor está errado, como se ele não estivesse funcionando. segue o método do orgao gestor e o outro geral:
public List&lt;Funcionario&gt; getFuncionariosOrgaoGestor() {
		Session session = HibernateUtil.currentSession();
		FuncionarioDao funcionarioDao = new FuncionarioDao(session,
				Funcionario.class);
		List&lt;Funcionario&gt; lista = funcionarioDao
				.pesquisaFuncionariosOrgaoGestor(this.orgaogestor
						.getCod_orgaogestor());
		return lista;
	}

	// método que testa todos os campos
	public List&lt;Funcionario&gt; getFuncionariosByCampos() {
		List&lt;Funcionario&gt; nome = getFuncionariosNome();
		List&lt;Funcionario&gt; cpf = getFuncionariosCPF();
		List&lt;Funcionario&gt; rg = getFuncionariosRG();
		List&lt;Funcionario&gt; og = getFuncionariosOrgaoGestor();

		if (nome != null) {
			return nome;
		}
		if (cpf != null) {
			return cpf;
		}
		if (rg != null) {
			return rg;
		}
		if (og != null) {
			return og;
		}
		return null;
	}
ou o problema pode ser no objeto na pagina xhtml, que está assim:
&lt;h:outputText value="#{selectOG}" id="msgs-og"/&gt;
		&lt;h:selectOneMenu value="#{funcionarioHandler.orgaogestor.cod_orgaogestor}" id="og"&gt;
				&lt;f:selectItem itemValue="4" itemLabel="Selecione" id="selog"/&gt;
				&lt;f:selectItems value="#{funcionarioHandler.orgaoGestorParaComboBox}" id="sog"/&gt;
		&lt;/h:selectOneMenu&gt;

Se alguem puder ajudar, agradeço.

Kleber-rr
resolvido pessoal. O que faltou tratar os atributos como String e não como list, o método getFuncionariosByCampos tratar como List<> e colocar o retorno, e trabalhar corretamente as condiçoes:
public List&lt;Funcionario&gt; getFuncionariosByCampos() {
		List&lt;Funcionario&gt; list = new ArrayList&lt;Funcionario&gt;();

		String nome = this.funcionario.getNome();
		String cpf = this.funcionario.getCpf();
		String rg = this.funcionario.getRg();
		Long og = this.orgaogestor.getCod_orgaogestor();

		if (og != null && (nome).isEmpty()) {
			System.out.println("leu primeira condição");
			return getFuncionariosOrgaoGestor();
		} else if ((nome) != null && !(nome).isEmpty()) {
			System.out.println("leu segunda condição");
			return getFuncionariosNome();
		} else if ((rg) != null && !(rg).isEmpty()) {
			System.out.println("leu terceira condição");
			return getFuncionariosRG();
		} else if ((cpf) != null && !(cpf).isEmpty()) {
			System.out.println("leu quarta condição");
			return getFuncionariosCPF();
		}

		return list;
	}

Agradeço a ajuda de todos.

Criado 26 de março de 2009
Ultima resposta 1 de mar. de 2010
Respostas 8
Participantes 4