Note que os métodos são genéricos, mas que isso só funcionária se a query nunca tivesse nenhum tipo de restrição, ou seja, se você quisesse realizar o count apenas de determinadas informações, dessa maneira seria impossível e você teria que reescrever o método count sempre que precisasse restringir alguma informação.
Pensando nisso vamos abstrair mais ainda o Método count. Nele agora será necessário informar às restrições que deverão compor a query.
Vejamos como ficará o método count.
}
public Integer count(List<Criterion> restricoes){
Integer size = 0;
try {
getHibernate().beginTransaction();
Criteria criteria = getHibernate().getSession().createCriteria(getEntidade().getClass());
if(restricoes != null){
for (Criterion criterion : restricoes) {
criteria.add(criterion);
}
}
criteria.setProjection(Projections.rowCount());
size = (Integer) criteria.uniqueResult();
getHibernate().commit();
} catch (Exception e) {
getHibernate().rollback();
e.printStackTrace();
} finally {
getHibernate().close();
}
return size;
Note que agora é solicitado uma Lista de restrições (Criterions) que serão utilizados pela criteria. Isso torna o método genérico a qualquer consulta, sendo necessário passar a restrição que desejar dentro de uma lista, da seguinte maneira.
List lista = new ArrayList();
lista.add(Restrictions.eq("usuario", usuario.GetUsuario()));
Dessa maneira é informado qual restrição quero para o método count.
Pensando dessa forma o método listByCriteriaDemanda deverá permitir restrições, ordenações e até mesmo projeções. Aqui entramos em um problema:
Teremos que prevenir o LIE (Lazy Initialization Exception). Se a sua entidade possuir uma listagem e está será utilizada na tela, a mesma deverá ser Lazy false ou você terá que realizar a prevenção do LIE. No meu caso teremos que inicializar essa dependência.
Mas como? Meu DAO é genérico, não tenho acesso aos atributos da classe e não sei se esta classe possui ou não listagens que poderão causar o LIE.
Vamos utilizar um recurso do Java. Vamos utilizar reflexão, ou seja, vamos descobrir se a classe possui ou não atributos que são listas e iremos inicializar esses atributos.
Vamos ver como ficaria o nosso método após a adição das restrições e inicialização de objetos de um determinado tipo por reflexão.
public List<T> listByCriteriaDemanda
(Integer startPage, Integer maxPage, List<Criterion> restricoes, List<Projection> projecoes, List<Order> ordenacao){
List<T> list = new ArrayList<T>();
try {
getHibernate().beginTransaction();
Criteria criteria = getHibernate().getSession().createCriteria(getEntidade().
getClass());
if(restricoes !=null){
for(Criterion r: restricoes){
criteria.add(r);
}
}
if(projecoes != null){
for (Projection p : projecoes) {
criteria.setProjection(p);
}
}
if(ordenacao != null){
for(Order order: ordenacao){
criteria.addOrder(order);
}
}
criteria.setFirstResult(startPage);
criteria.setMaxResults(maxPage);
list = criteria.list();
for (T t : list) {
for (Method method : t.getClass().getMethods()) {
if(method.getName().indexOf(“get”) > -1){
Object obj = method.invoke(t);
if(obj instanceof java.util.Collection){
for (T lista: (List<T>)obj) {
Hibernate.initialize(lista);
}
}
}
}
}
getHibernate().commit();
} catch (Exception e) {
getHibernate().rollback();
e.printStackTrace();
} finally {
getHibernate.close();
}
return list;
}
Nosso método agora ficou bastante genérico, trouxemos as restrições, as projeções e as ordenações para dentro dele. Na nossa classe agora poderemos criar nossas restrições, projeções e ordenação e manter nosso DAO genérico, sem precisar implementar novamente as funcionalidades da classe.
O trecho abaixo é referente a parte de reflexão que iremos utilizar para descobrir se um atributo é uma coleção ou não, para podermos inicializar este atributo.
for (T t : list) {
for (Method method : t.getClass().getMethods()) {
if(method.getName().indexOf(“get”) > -1){
Object obj = method.invoke(t);
if(obj instanceof java.util.Collection){
for (T lista: (List<T>)obj) {
Hibernate.initialize(lista);
}
}
}
}
}
Aqui nos fazemos uma iteração na lista criada pela criteria. Criamos um objeto do tipo Method, esse objeto é um Array e é carregado a partir do método
t.getClass.getMethods();
Esse método retorna um Methods[].
Feito isso nós testamos se no nome do método existe a palavra “get”, pois será necessário invocar esse get para sabermos qual o tipo de objeto será retornado.
O método indexOf retorna >-1 caso seja verdadeiro, ou seja, se a palavra get existir no nome do método então o retorno será verdadeiro.
Com isso nós invocamos o método através do method.invoke(t);
Como não sabemos qual o tipo de retorno então criamos um objeto do tipo Object que irá receber o retorno. Se esse objeto for filho de collection (List, Set, ArrayList, HashTree, etc) então iremos iterar sobre esse objeto e iremos inicializar item após item da listagem resultante. Por padrão, os relacionamentos OneToMany retornam um objeto do tipo PersistenceBag que é filho de Collection.
Feito isso sua classe está completamente inicializada e não haverá problemas com LazyInitialization e você terá uma paginação em demanda funcional.
Em breve estarei disponibilizando um projeto war contendo esse DAO, SuperDAO e as classes necessárias para realizar a paginação. Um exemplo funcional com acesso a uma base de dados.