org.nextframework.persistence.QueryBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.nextframework.persistence.QueryBuilder.java

Source

/*
 * Next Framework http://www.nextframework.org
 * Copyright (C) 2009 the original author or authors.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * You may obtain a copy of the license at
 * 
 *     http://www.gnu.org/copyleft/lesser.html
 * 
 */
package org.nextframework.persistence;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.nextframework.persistence.PersistenceUtils.InverseCollectionProperties;
import org.nextframework.persistence.translator.AliasMap;
import org.nextframework.persistence.translator.QueryBuilderResultTranslator;
import org.nextframework.persistence.translator.QueryBuilderResultTranslatorImpl;

/**
 * Helps constructing and executing hibernate queries.
 * 
 * @author rogelgarcia
 *
 * @param <E>
 */
public class QueryBuilder<E> {

    private static final Log log = LogFactory.getLog(QueryBuilder.class);

    protected PersistenceConfiguration config;

    protected Select select;
    protected From from;
    protected List<Join> joins = new ArrayList<Join>();
    protected Map<String, CollectionFetcher> fetchs = new HashMap<String, CollectionFetcher>();
    protected Where where = new Where();
    protected GroupBy groupBy;
    protected String orderby;

    protected HibernateSessionProvider hibernateSessionProvider;

    protected int maxResults = Integer.MIN_VALUE;
    protected int firstResult = Integer.MIN_VALUE;

    protected boolean hasSelect = false;

    /**
     * Utilizar o tradutor quando o resultado for do tipo array
     */
    protected boolean useTranslator = true;

    protected String translatorAlias;

    private String persistenceContext;

    /**
     */
    public QueryBuilder() {
        this(PersistenceUtils.getSessionProvider());
    }

    public QueryBuilder(String persistenceContext) {
        this(PersistenceUtils.getSessionProvider(persistenceContext), persistenceContext);
    }

    public QueryBuilder(HibernateSessionProvider hibernateSessionProvider) {
        this(hibernateSessionProvider, PersistenceConfiguration.DEFAULT_CONFIG);
    }

    public QueryBuilder(HibernateSessionProvider hibernateSessionProvider, String persistenceContext) {
        if (hibernateSessionProvider == null) {
            throw new IllegalArgumentException(
                    "Session provider must not be null. Try using the default constructor. i.e. new QueryBuilder()");
        }
        this.hibernateSessionProvider = hibernateSessionProvider;
        this.config = PersistenceConfiguration.getConfig(persistenceContext);
        this.persistenceContext = persistenceContext;
    }

    public boolean isUseTranslator() {
        return useTranslator;
    }

    public QueryBuilder<E> setUseTranslator(boolean useTranslator) {
        this.useTranslator = useTranslator;
        return this;
    }

    public QueryBuilder<E> setTranslatorAlias(String alias) {
        this.translatorAlias = alias;
        return this;
    }

    //   public ResultList<E> pageAndOrder(PageAndOrder pageAndOrder) {
    //      return new ResultList<E>(this, pageAndOrder);
    //   }

    /**
     * Sets the page number and the size of the pages
     * @param pageNumber number of the page to be queried (zero based index)
     * @param pageSize size of each page (maxResults)
     */
    public QueryBuilder<E> setPageNumberAndSize(int pageNumber, int pageSize) {
        maxResults = pageSize;
        firstResult = pageSize * pageNumber;
        return this;
    }

    /**
     * Seta a clusula from desse queryBuilder
     * Seta o alias para o nome da clase minusculo
     * @param clazz
     * @return
     */
    public QueryBuilder<E> from(Class<?> clazz) {
        return from(clazz, uncapitalize(clazz.getSimpleName()));
    }

    /**
     * Seta a clusula from desse queryBuilder e o alias
     * O alias pode ser utilizado em clausulas where por exemplo
     * 
     * @param _clazz
     * @param _alias
     * @return
     */
    public QueryBuilder<E> from(Class<?> _clazz, String _alias) {
        if (!hasSelect) {
            select(_alias);
        }
        this.from = new From(_clazz, _alias);
        return this;
    }

    /**
     * Cria uma clusula from a partir de outra clusula from
     * @param from
     * @return
     */
    public QueryBuilder<E> from(From from) {
        from(from.getFromClass(), uncapitalize(from.getFromClass().getSimpleName()));
        return this;
    }

    /**
     * Cria uma clausula select (opicional)
     * @param select
     * @return
     */
    public QueryBuilder<E> select(String select) {
        this.select = new Select(select);
        hasSelect = true;
        return this;
    }

    /**
     * Cria uma clausula select (opicional)
     * @param select
     * @return
     */
    public QueryBuilder<E> select(Select select) {
        this.select = select;
        hasSelect = true;
        return this;
    }

    /**
     * Fetchs the collection of the path property using a default collection fetcher (when useDefaultCollectionFetcher is true).<BR>
     * The fetched collection property must be of the returned item type.<BR> 
     * For example:
     * <pre>
     *       query.from(Foo.class)
     *           .fetchCollection("barList");
     * </pre>
     * The above query will return results of Foo type. The Foo type must have a collection property named barList.<BR>
     * The collection property must not use aliases. Use "barList" intead of "foo.barList".<BR>
     * The actual behavior of the default collection fetcher depends on the implementation configured in the QueryBuilder config object.
     * 
     * @see QueryBuilder.configure(...)
     * @see CollectionFetcher
     * 
     * @param path Collection property of the returned items to fetch.
     * @param useDefaultCollectionFetcher if true uses the default collection fetcher used in the query builder config.
     */
    public QueryBuilder<E> fetchCollection(String path, boolean useDefaultCollectionFetcher) {
        if (useDefaultCollectionFetcher) {
            if (config.getDefaultCollectionFetcher() == null) {
                throw new IllegalArgumentException("No default collection fetcher configured in QueryBuilder");
            }
            return fetch(path, config.getDefaultCollectionFetcher());
        } else {
            return fetch(path, null);
        }
    }

    public QueryBuilder<E> fetch(String path, CollectionFetcher collectionFetcher) {
        fetchs.put(path, collectionFetcher);
        return this;
    }

    /**
     * Inicializa determinada coleo dos beans que essa query retornar
     * IMPORTANTE: No utilizar o alias, apenas o nome da propriedade que deve ser um Collection
     * e deve estar mapeado no hibernate
     * @param path
     * @return
     */
    public QueryBuilder<E> fetchCollection(String path) {
        return fetchCollection(path, false);
    }

    /**
     * Cria uma clausula join
     * @param joinMode Tipo do join: INNER, LEFT, RIGHT
     * @param fetch Indica se a entidade relacionada com o join  para ser inicializada
     * @param path PAth da propriedade, utilizar o alias + ponto + propriedade. ex.: pessoa.municipio
     * @return
     */
    public QueryBuilder<E> join(JoinMode joinMode, boolean fetch, String path) {
        Join join = new Join(joinMode, fetch, path);
        this.joins.add(join);
        return this;
    }

    /**
     * Efetua um inner join sem fetch 
     * Equivalente : join(JoinMode.INNER, false, path);
     * @param path
     * @return
     */
    public QueryBuilder<E> join(String path) {
        return join(JoinMode.INNER, false, path);
    }

    /**
     * Efetua um inner join com fetch
     * Equivalente : join(JoinMode.INNER, true, path);
     * @param path
     * @return
     */
    public QueryBuilder<E> joinFetch(String path) {
        return join(JoinMode.INNER, true, path);
    }

    /**
     * Efetua um left outer join sem fetch
     * Equivalente : join(JoinMode.LEFT_OUTER, false, path);
     * @param path
     * @return
     */
    public QueryBuilder<E> leftOuterJoin(String path) {
        return join(JoinMode.LEFT_OUTER, false, path);
    }

    /**
     * Efetua um left outer join com fetch
     * Equivalente : join(JoinMode.LEFT_OUTER, false, path);
     * @param path
     * @return
     */
    public QueryBuilder<E> leftOuterJoinFetch(String path) {
        return join(JoinMode.LEFT_OUTER, true, path);
    }

    private boolean inOr = false;
    private boolean bypassedlastWhere = false;

    /**
     * Cria uma clusulua where ... like ...
     * S  necessrio informar a expressao que deve ser usado o like, no utilizar '?'
     * Ex.:
     * whereLike("associado.nome", associado.getNome())
     * Isso ser transformado em: associado.nome like '%'||nome||'%'
     * Se o parametro for null ou string vazia essa condio no ser criada
     * @param whereClause
     * @param parameter
     * @return
     */
    public QueryBuilder<E> whereLike(String whereClause, String parameter) {
        if (parameter != null && !parameter.equals("")) {
            if (parameter.indexOf('?') > 0) {
                throw new IllegalArgumentException(
                        "A clusula where do QueryBuilder no pode ter o caracter '?'. Deve ser passada apenas a expresso que se dejesa fazer o like. Veja javadoc!");
            }
            where(whereClause + " like '%'||?||'%'", parameter);
        }
        return this;
    }

    public QueryBuilder<E> whereIntervalMatches(String beginfield, String endfield, Object begin, Object end) {
        this.openParentheses().openParentheses().where(beginfield + " >= ?", begin).where(endfield + " <= ?", end)
                .closeParentheses().or().openParentheses().where(beginfield + " >= ?", begin)
                .where(beginfield + " <= ?", end).closeParentheses().or().openParentheses()
                .where(endfield + " >= ?", begin).where(endfield + " <= ?", end).closeParentheses().or()
                .openParentheses().where(beginfield + " <= ?", begin).where(endfield + " >= ?", end)
                .closeParentheses().closeParentheses();
        return this;
    }

    /**
     * Cria uma clusulua where ... like ... ignorando caixa e acentos.
     * S  necessrio informar a expressao que deve ser usado o like, no utilizar '?'
     * Ex.:
     * whereLikeIgnoreAll("associado.nome", associado.getNome())
     * Isso ser transformado em: UPPER(TIRAACENTO(associado.nome)) LIKE '%'||UPPER(TIRAACENTO(nome))||'%'
     * Se o parametro for null ou string vazia essa condio no ser criada
     * @param whereClause
     * @param parameter
     * @return
     */
    public QueryBuilder<E> whereLikeIgnoreAll(String whereClause, String parameter) {
        if (parameter != null && !parameter.equals("")) {
            if (parameter.indexOf('?') > 0) {
                throw new IllegalArgumentException(
                        "A clusula where do QueryBuilder no pode ter o caracter '?'. Deve ser passada apenas a expresso que se deseja fazer o like. Veja javadoc!");
            }
            //         String funcaoTiraacento = Next.getApplicationContext().getConfig().getProperties().getProperty(Config.FUNCAO_TIRA_ACENTO);
            String funcaoTiraacento = config.getRemoveAccentFunction();
            if (funcaoTiraacento != null) {
                where("UPPER(" + funcaoTiraacento + "(" + whereClause + ")) LIKE '%'||?||'%'",
                        PersistenceUtils.removeAccents(parameter).toUpperCase());
            } else {
                where("UPPER(" + whereClause + ") LIKE '%'||?||'%'",
                        PersistenceUtils.removeAccents(parameter).toUpperCase());
            }
        }
        return this;
    }

    /**
     * Cria uma clausula where ... in ... S  necessrio informar a expressao que deve ser usado o in, no utilizar '?' Ex.: whereIn("associado.inscricoes", inscricoes) Isso ser transformado em: associado.inscricoes in ? [onde no lugar de '?' ser colocado a colecao] Se o parametro for null ou
     * string vazia e emptyCollectionReturnFalse for false essa condio no ser criada
     * 
     * @param whereClause
     * @param collection
     * @param emptyCollectionReturnTrue
     *            Se a colecao for vazia  para retornar verdadeiro?
     * @return
     */
    public QueryBuilder<E> whereIn(String whereClause, Collection<?> collection,
            boolean emptyCollectionReturnTrue) {
        if (collection != null) {
            if (collection.isEmpty()) {
                if (!emptyCollectionReturnTrue) {
                    where("1 = 0");
                }
            } else {
                where(whereClause + " in (?)", collection);
                //            String cod = "p" + where.getNamedParameters().size();
                //            where(whereClause + " in (:" + cod + ")");
                //            where.putNamedParameters(cod, collection);
            }
        } else if (!emptyCollectionReturnTrue) {
            where("1 = 0");
        }
        return this;
    }

    /**
     * Cria uma clausula where ... in ...
     * S  necessrio informar a expressao que deve ser usado o in, no utilizar '?'
     * Ex.:
     * whereIn("associado.inscricoes", "1,2,4,5")
     * Isso ser transformado em: associado.inscricoes in ? [onde no lugar de '?' ser colocado o values]
     * Se o parametro for null ou string vazia e emptyCollectionReturnTrue for true essa condio no ser criada
     * @param whereClause 
     * @param values valores separados por virgula
     * @param emptyCollectionReturnTrue Se a colecao for vazia  para retornar verdadeiro?
     * @return
     */
    public QueryBuilder<E> whereIn(String whereClause, String values, boolean emptyCollectionReturnTrue) {
        if (values != null && values.trim().length() > 0) {
            where(whereClause + " in (" + values + ")");
        } else if (!emptyCollectionReturnTrue) {
            where("1 = 0");
        }
        return this;
    }

    /**
     * Cria uma clausula where ... in ...
     * S  necessrio informar a expressao que deve ser usado o in, no utilizar '?'
     * Ex.:
     * whereIn("associado.inscricoes", "1,2,4,5")
     * Isso ser transformado em: associado.inscricoes in ? [onde no lugar de '?' ser colocado o values]
     * Se o parametro for null ou string vazia essa condio no ser criada
     * @param whereClause 
     * @param values valores separados por virgula
     * @param emptyCollectionReturnTrue Se a colecao for vazia  para retornar verdadeiro?
     * @return
     */
    public QueryBuilder<E> whereIn(String whereClause, String values) {
        return whereIn(whereClause, values, true);
    }

    /**
     * Cria uma clausula where ... in ...
     * S  necessrio informar a expressao que deve ser usado o in, no utilizar '?'
     * Ex.:
     * whereIn("associado.inscricoes", inscricoes)
     * Isso ser transformado em: associado.inscricoes in ? [onde no lugar de '?' ser colocado a colecao]
     * Se a colecao for vazia a query retornar verdadeiro
     * @param whereClause 
     * @param collection
     * @return
     */
    public QueryBuilder<E> whereIn(String whereClause, Collection<?> collection) {
        return whereIn(whereClause, collection, true);
    }

    public QueryBuilder<E> where(String whereClause, Object parameter, boolean addClause) {
        if (addClause) {
            where(whereClause, parameter);
        } else {
            bypassedlastWhere = true;
        }
        if (inOr) {
            inOr = false;
        }
        return this;
    }

    /**
     * Cria uma clausula where.
     * Escrever a clausula completa . Ex:
     * where("pessoa.municipio.nome like '?'", nome)
     * Onde a ? ser substituida por parameter
     * Se parameter for null essa clausula nao ser criada
     * @param whereClause
     * @param parameter
     * @return
     */
    public QueryBuilder<E> where(String whereClause, Object parameter) {
        if (parameter != null && (!(parameter instanceof String) || !parameter.equals(""))) {
            if (inOr) {
                where.or();
                inOr = false;
            } else {
                where.and();
            }
            where.append(whereClause, parameter);
            bypassedlastWhere = false;
        } else {
            bypassedlastWhere = true;
        }
        if (inOr) {
            inOr = false;
        }
        return this;
    }

    /**
     * Cria uma clausula where com vrios parmetros.
     * Se parameter for null essa clausula nao ser criada
     * @param whereClause
     * @param parameter
     * @return
     */
    public QueryBuilder<E> where(String whereClause, Object[] parameters) {
        if (parameters == null) {
            return this;
        }
        boolean allBlank = true;
        for (int i = 0; i < parameters.length; i++) {
            if (parameters[i] != null && (!(parameters[i] instanceof String) || !parameters[i].equals(""))) {
                allBlank = false;
                break;
            }
        }

        if (!allBlank) {
            if (inOr) {
                where.or();
                inOr = false;
            } else {
                where.and();
            }
            where.append(whereClause, parameters);
            bypassedlastWhere = false;
        } else {
            bypassedlastWhere = true;
        }
        return this;
    }

    /**
     * Cria uma clausula where.
     * Escrever a clausula completa . Ex:
     * where("pessoa.municipio.nome like 'BH'")
     * @param whereClause
     * @param parameter
     * @return
     */
    public QueryBuilder<E> where(String whereClause) {
        if (inOr) {
            where.or();
            inOr = false;
        } else {
            where.and();
        }
        where.append(whereClause);
        bypassedlastWhere = false;
        return this;
    }

    public QueryBuilder<E> where(String whereClause, boolean addClause) {
        if (addClause) {
            where(whereClause);
        }
        return this;
    }

    public QueryBuilder<E> whereWhen(String whereClause, Boolean include) {
        if (include != null && include == true) {
            if (inOr) {
                where.or();
                inOr = false;
            } else {
                where.and();
            }
            where.append(whereClause);
            bypassedlastWhere = false;
        } else {
            bypassedlastWhere = true;
        }
        return this;
    }

    public QueryBuilder<E> where(Where where) {
        this.where = where;
        return this;
    }

    public QueryBuilder<E> orderBy(String order) {
        if (order != null && !order.trim().equals("")) {
            this.orderby = order;
        }
        return this;
    }

    private int subConditionStack = 0;

    /**
     * Cria um "abre parenteses" na query
     * @return
     */
    public QueryBuilder<E> openParentheses() {
        if (inOr) {
            where.or();
            inOr = false;
        } else {
            where.and();
        }
        where.append("(");
        subConditionStack++;
        return this;
    }

    /**
     * "Fecha parenteses" na query
     * @return
     */
    public QueryBuilder<E> closeParentheses() {
        if (inOr) {
            inOr = false;
        }
        bypassedlastWhere = false;
        where.append(")");
        subConditionStack--;
        if (subConditionStack < 0) {
            throw new RuntimeException("No existem subcondicoes a serem fechadas");
        }
        return this;
    }

    /**
     * Cria uma condio or. A prxima instruo ser concatenada a query com um or e nao com um and
     * @see openParentheses
     * @return
     */
    public QueryBuilder<E> or() {
        if (!bypassedlastWhere) {
            inOr = true;
        }
        return this;
    }

    /**
     * Adiciona ao where uma clusula onde o id deve ser igual ao 
     * Serializable fornecido
     * @param serializable
     * @return
     */
    public QueryBuilder<E> idEq(Serializable serializable) {
        String idPropertyName = PersistenceUtils.getIdPropertyName(from.getFromClass(), getSessionFactory());
        where(from.getAlias() + "." + idPropertyName + " = ?", serializable);
        return this;
    }

    /**
     * Descobre o id do objeto e adiciona uma clusula where
     * que retornar o objeto com o mesmo id do objeto fornecido
     * @param object
     * @return
     */
    public QueryBuilder<E> entity(Object object) {
        if (object == null)
            throw new NullPointerException("entity null");
        idEq(PersistenceUtils.getId(object, getSessionFactory()));
        return this;
    }

    public QueryBuilder<E> groupBy(String groupBy, String having) {
        this.groupBy = new GroupBy(groupBy, having);
        return this;
    }

    public QueryBuilder<E> groupBy(String groupBy) {
        this.groupBy = new GroupBy(groupBy, null);
        return this;
    }

    /**
     * Executa a query e retorna a lista
     */
    @SuppressWarnings("unchecked")
    public List<E> list() {
        if (from == null) {
            throw new NullPointerException("Query criada sem clusula from");
        }
        QueryBuilderResultTranslator qbt = getQueryBuilderResultTranslator();
        List list = (List) hibernateSessionProvider.execute(new HibernateCommand() {
            public Object doInHibernate(Session session) {
                Query query = createQuery(session);
                if (maxResults != Integer.MIN_VALUE) {
                    query.setMaxResults(maxResults);
                }
                if (firstResult != Integer.MIN_VALUE) {
                    query.setFirstResult(firstResult);
                }
                return query.list();
            }
        });
        if (qbt != null) {
            list = organizeListWithResultTranslator(qbt, list);
        }
        try {
            for (Object object : list) {
                initializeProxys(object);
            }
        } catch (RuntimeException e) {
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            throw new RuntimeException("Erro ao inicializar Proxys (Colees). " + stackTrace[7], e);
        }
        return (List<E>) list;
    }

    //   /**
    //    * Excecuta a query e retorna um iterator
    //    */
    //    @SuppressWarnings("unchecked")
    //   public Iterator<E> iterate(){
    //      if(from == null){
    //         throw new NullPointerException("Query criada sem clusula from");
    //      }
    //      final Session session = sessionProvider.newSession();
    //      
    //      Query query = createQuery(session);
    //      if(maxResults != Integer.MIN_VALUE){
    //         query.setMaxResults(maxResults);
    //      }
    //      if(firstResult != Integer.MIN_VALUE){
    //         query.setFirstResult(firstResult);
    //      }
    //      final Iterator<E> iterator = query.iterate();
    //      Iterator<E> closeSessionIterator = new Iterator<E>(){
    //
    //         public boolean hasNext() {
    //            boolean hasNext = iterator.hasNext();
    //            if(!hasNext){
    //               if (session.isConnected()) {
    //                  session.close();
    //                  //System.out.println("closing connection");
    //               }
    //            }
    //            return hasNext;
    //         }
    //
    //         public E next() {
    //            E next = iterator.next();
    //            initializeProxys(next);
    //            return next;
    //         }
    //
    //         public void remove() {
    //            iterator.remove();
    //         }
    //
    //         @Override
    //         protected void finalize() throws Throwable {
    //            if (session.isConnected()) {
    //               session.close();
    //               //System.out.println("closing finalize");
    //            }
    //         }
    //         
    //      };
    //      return closeSessionIterator;
    //   }   

    @SuppressWarnings("unchecked")
    /**
     * Exceuta a query e retorna um nico resultado
     */
    public E unique() {
        if (from == null) {
            throw new NullPointerException("query with null from clause");
        }
        QueryBuilderResultTranslator qbt = getQueryBuilderResultTranslator();
        final boolean useUnique = qbt == null;
        Object execute = hibernateSessionProvider.execute(new HibernateCommand() {
            public Object doInHibernate(Session session) {
                Query query = createQuery(session);
                if (maxResults != Integer.MIN_VALUE) {
                    if (maxResults != 1) {
                        throw new IllegalArgumentException("Method unique of " + QueryBuilder.class.getSimpleName()
                                + " must be used with maxResults set to 1");
                    }
                    query.setMaxResults(maxResults);
                }
                if (firstResult != Integer.MIN_VALUE) {
                    query.setFirstResult(firstResult);
                }
                Object uniqueResult;
                if (useUnique) {
                    uniqueResult = query.uniqueResult();
                } else {
                    uniqueResult = query.list();
                }

                try {
                    initializeProxys(uniqueResult);
                } catch (RuntimeException e) {
                    StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
                    throw new RuntimeException("Erro ao inicializar Proxys (Colees). " + stackTrace[7], e);
                }

                if (uniqueResult != null && !uniqueResult.getClass().getName().startsWith("java")
                        && !uniqueResult.getClass().isArray()) {
                    //only evict entities
                    //TODO check with hibernate if the result is an entity
                    session.evict(uniqueResult);
                }
                return uniqueResult;
            }
        });
        if (qbt != null) {//ORGANIZAR.. TEM 2 LUGARES COM CDIGO IGUAL
            if (execute instanceof List) {
                execute = organizeListWithResultTranslator(qbt, (List) execute);
                if (((List) execute).size() == 0) {
                    execute = null;
                } else {
                    execute = ((List) execute).get(0);
                }
            } else {
                execute = organizeUniqueResultWithTranslator(qbt, (Object[]) execute);
            }
        }
        return (E) execute;
    }

    protected Object organizeUniqueResultWithTranslator(QueryBuilderResultTranslator qbt, Object[] execute) {
        Object[] resultado = execute;
        Object novoResultado = qbt.translate((Object[]) resultado);
        return novoResultado;
    }

    protected List<?> organizeListWithResultTranslator(QueryBuilderResultTranslator qbt, List<?> execute) {
        List<?> resultado = execute;
        List<Object> novoResultado = new ArrayList<Object>(resultado.size());
        for (int j = 0; j < resultado.size(); j++) {
            Object translate = qbt.translate((Object[]) resultado.get(j));
            if (translate != null) {
                novoResultado.add(translate);
            }
        }
        execute = novoResultado;
        return execute;
    }

    /**
     * Joins que sero ignorados pelo translator
     */
    private Set<String> ignoreJoinPaths = new HashSet<String>();

    /**
     * Faz com que o resultTranslator ignore joins com determinados alias
     * @param alias
     */
    public void ignoreJoin(String alias) {
        ignoreJoinPaths.add(alias);
    }

    protected QueryBuilderResultTranslator getQueryBuilderResultTranslator() {
        QueryBuilderResultTranslator qbt = null;
        if (useTranslator) {//ORGANIZAR TEM 2 LUGARES COM CDIGO IGUAL
            String selectString = select.select;
            if (selectString.contains(".")) {
                //utilizar o query builder translator
                qbt = new QueryBuilderResultTranslatorImpl();
                if (translatorAlias != null) {
                    qbt.setResultAlias(translatorAlias);
                }
                List<AliasMap> aliasMaps = new ArrayList<AliasMap>();
                aliasMaps.add(new AliasMap(from.getAlias(), null, from.getFromClass()));
                for (Join join : joins) {
                    String[] joinpath = join.path.split(" +");
                    if (join.fetch) {
                        throw new QueryBuilderException(
                                " necessrio utilizar joins sem Fetch quando especificar os campos a serem selecionados. Erro no join: "
                                        + join);
                    }
                    if (joinpath.length < 2) {
                        throw new QueryBuilderException(
                                " necessrio informar um alias para todos os joins quando especificar os campos a serem selecionados. Erro no join: "
                                        + join);
                    }
                    if (ignoreJoinPaths.contains(joinpath[1])) {
                        //se o alias deve ser ignorado.. nao adicionar o aliasmap
                        continue;
                    }
                    aliasMaps.add(new AliasMap(joinpath[1], joinpath[0], null));
                }
                int indexOfDistinct = selectString.indexOf("distinct ");
                if (indexOfDistinct >= 0) {
                    if (selectString.substring(0, indexOfDistinct).trim().equals("")) {//verificando se distinct  a primeira palavra
                        selectString = selectString.substring(indexOfDistinct + 9);
                    }
                }
                String[] properties = selectString.split("( )*?,( )*?");
                for (int j = 0; j < properties.length; j++) {
                    String property = properties[j];
                    if (!property.trim().matches("[^ ]+\\.[^ ]+( +as +[^ ]+)?")) {
                        throw new RuntimeException("O campo \"" + property + "\" do select no  vlido.");
                    }
                    int indexOfAs = property.indexOf(" as");
                    if (indexOfAs > 0) {
                        properties[j] = property.substring(0, indexOfAs);
                    }
                    properties[j] = properties[j].trim();
                }
                qbt.init(getSessionFactory(), properties, aliasMaps.toArray(new AliasMap[aliasMaps.size()]));
                //os extra fields so campos que o QueryBuilderResultTranslator necessita
                String[] extraFields = qbt.getExtraFields();
                String extraFieldsSelect = "";
                for (int j = 0; j < extraFields.length; j++) {
                    String extra = extraFields[j];
                    extraFieldsSelect += ", ";
                    extraFieldsSelect += extra;
                }
                select.select += extraFieldsSelect;
            }
        }
        return qbt;
    }

    protected void initializeProxys(Object object) {
        if (object == null) {
            return;//se o objeto for nulo, nao devemos inicializar os proxys pois no houve resultados
        }
        Set<Entry<String, CollectionFetcher>> entrySet = fetchs.entrySet();
        for (Entry<String, CollectionFetcher> entry : entrySet) {
            String collectionProperty = entry.getKey();
            CollectionFetcher fetcher = entry.getValue();
            if (fetcher == null) {
                fetcher = new CollectionFetcher() {
                    public List<?> load(QueryBuilder<?> qb, Object owner, String collectionProperty,
                            Class<?> itemType) {
                        return qb.list();
                    }
                };
            }

            InverseCollectionProperties inverseCollectionProperty = PersistenceUtils
                    .getInverseCollectionProperty(getSessionFactory(), object.getClass(), collectionProperty);
            QueryBuilder<?> qb = getQueryBuilderForCollection(object, collectionProperty,
                    inverseCollectionProperty);
            List<?> list = fetcher.load(qb, object, collectionProperty, inverseCollectionProperty.type);
            PersistenceUtils.setProperty(object, collectionProperty, list);
        }
        //      for (String fetch : fetchs.keySet()) {
        //         try {
        //            Object proxyObj = QueryBuilderUtils.getProperty(object, fetch);
        //            
        //            Hibernate.initialize(proxyObj);
        //            if(fetchsDAO.contains(fetch) && proxyObj instanceof Collection){
        //               Collection newCollection = null;
        //               //TODO A QUERY SER FEITA DUAS VEZES.. OTIMIZAR
        //               Collection<?> proxy = (Collection) proxyObj;
        //               if(proxy.iterator().hasNext()){
        //                  newCollection = new ArrayList(); //changed in 2012-08-15, used ListSet
        //                  String idProperty = null;
        //                  for (Object object2 : proxy) {
        //                     if(idProperty == null){
        //                        idProperty = QueryBuilderUtils.getIdPropertyName(object2.getClass(), sessionProvider.getSessionFactory());
        //                     }
        //                     newCollection.add(load(idProperty, object2));
        //                  }
        //               }
        //               QueryBuilderUtils.setProperty(object, fetch, newCollection);
        //            }
        //         } catch (Exception e) {
        //            throw new QueryBuilderException("Erro ao tentar fazer fetch de "+fetch+" em "+from.getFromClass().getName(), e);
        //         }
        //      }
    }

    protected QueryBuilder<?> getQueryBuilderForCollection(Object parent, String collectionProperty,
            InverseCollectionProperties inverseCollectionProperty) {
        QueryBuilder<?> queryBuilder = new QueryBuilder<Object>(hibernateSessionProvider);
        queryBuilder.from(inverseCollectionProperty.type)
                .where(queryBuilder.getAlias() + "." + inverseCollectionProperty.property + " = ?", parent);
        return queryBuilder;
    }

    protected Query createQuery(Session session) {
        Query query;
        String queryString = null;
        try {
            queryString = getQuery();
            query = session.createQuery(queryString);
        } catch (NullPointerException e) {
            throw new RuntimeException("NullPointerException when creating query \n\t" + queryString, e);
        }
        for (int i = 0; i < where.getParameters().size(); i++) {
            Object value = where.getParameters().get(i);
            if (value instanceof Collection<?>) {
                query.setParameterList("param" + i, (Collection<?>) value);
            } else {
                query.setParameter("param" + i, value);
            }
        }
        //      for (String cod : where.getNamedParameters().keySet()) {
        //         Collection<?> collection = where.getNamedParameters().get(cod);
        //         query.setParameterList(cod, collection);
        //      }
        return query;
    }

    /**
     * Cria uma query do hibernate com os parametros passados
     * @return
     */
    public String getQuery() {
        //TODO VERIFICAR SE A QUERY FOI CONSTRUIDA DE FORMA CORRETA
        if (from == null) {
            throw new RuntimeException("The FROM clause is missing on query builder");
        }
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(select);
        stringBuilder.append(" ");
        stringBuilder.append(from);
        stringBuilder.append(" ");
        for (Join join : joins) {
            stringBuilder.append(join);
            stringBuilder.append(" ");
        }

        stringBuilder.append(where);

        if (groupBy != null) {
            stringBuilder.append(" ");
            stringBuilder.append(groupBy);
        }

        if (orderby != null) {
            stringBuilder.append(" ORDER BY ");
            stringBuilder.append(orderby);
        }
        String hibernateQuery = stringBuilder.toString();

        log.info(hibernateQuery);
        return hibernateQuery;
    }

    public static class Select {
        private String select;

        public Select(String select) {
            this.select = select;
        }

        public String toString() {
            return "SELECT " + select;
        }

        public String getValue() {
            return select;
        }
    }

    public static class From {
        private String alias;
        private Class<?> from;

        public Class<?> getFromClass() {
            return from;
        }

        public String getAlias() {
            return alias;
        }

        public From(Class<?> from, String alias) {
            this.from = from;
            this.alias = alias;
        }

        public String toString() {
            return "FROM " + from.getName() + " " + alias;
        }
    }

    public static class Join {
        private JoinMode joinMode;
        private boolean fetch;
        private String path;

        public Join(JoinMode mode, boolean fetch, String path) {
            joinMode = mode;
            this.fetch = fetch;
            this.path = path;
        }

        public boolean isFetch() {
            return fetch;
        }

        public JoinMode getJoinMode() {
            return joinMode;
        }

        public String getPath() {
            return path;
        }

        public String toString() {
            return " " + joinMode + " JOIN " + (fetch ? "FETCH " : "") + path;
        }

        public String dontFetchToString() {
            return " " + joinMode + " JOIN " + path;
        }
    }

    public static enum JoinMode {
        LEFT_OUTER, INNER, RIGHT_OUTER;

        public String toString() {
            return name().replace('_', ' ');
        };
    }

    public static class GroupBy {
        private String groupBy;
        private String having;

        public GroupBy(String groupBy, String having) {
            this.groupBy = groupBy;
            this.having = having;
        }

        public String getValue() {
            return groupBy;
        }

        public String toString() {
            String string = "GROUP BY " + groupBy;
            if (having != null) {
                string += " HAVING " + having;
            }
            return string;
        }
    }

    public static class Where {
        private StringBuilder stringBuilder = new StringBuilder();
        private List<Object> parameters = new ArrayList<Object>();
        boolean inParentesis;

        public List<Object> getParameters() {
            return parameters;
        }

        private String convertToNamedParameter(String clause) {
            int count = parameters.size();
            while (clause.contains("?")) {
                clause = clause.replace("?", ":param" + (count++));
            }
            return clause;
        }

        public void or() {
            if (stringBuilder.length() > 0 && !inParentesis)
                append(" OR ");
        }

        public void and() {
            if (stringBuilder.length() > 0 && !inParentesis)
                append(" AND ");
        }

        public void append(String clause, Object parameter) {
            inParentesis = false;
            stringBuilder.append(convertToNamedParameter(clause));
            parameters.add(parameter);
        }

        public void append(String clause, Object[] parameters) {
            inParentesis = false;
            stringBuilder.append(convertToNamedParameter(clause));
            for (Object parameter : parameters) {
                this.parameters.add(parameter);
            }
        }

        public void append(String clause) {
            if (clause.contains("?")) {
                throw new QueryBuilderException("Invalid query, no parameters set for clause where " + clause);
            }
            if (")".equals(clause) && inParentesis) {
                inParentesis = stringBuilder.substring(stringBuilder.length() - 1, stringBuilder.length())
                        .equals("( ");

                //se abriu e fechou parenteses .. cancelar os parenteses
                stringBuilder.delete(stringBuilder.length() - 2, stringBuilder.length());
                stringBuilder.append(" 1=1 ");

                return;
            }
            if ("(".equals(clause)) {
                inParentesis = true;
            } else {
                inParentesis = false;
            }
            stringBuilder.append(clause);
            stringBuilder.append(" ");
        }

        public String toString() {
            if (stringBuilder.length() == 0) {
                return "";
            } else {
                StringBuilder builder = new StringBuilder();
                builder.append("WHERE ");
                builder.append(stringBuilder);
                return builder.toString();
            }
        }

        @Override
        public QueryBuilder.Where clone() {
            Where clone = new Where();
            clone.stringBuilder.append(this.stringBuilder.toString());
            clone.parameters.addAll(this.parameters);
            return clone;
        }
    }

    public From getFrom() {
        return from;
    }

    public void setFrom(From from) {
        this.from = from;
    }

    public List<Join> getJoins() {
        return joins;
    }

    public void setJoins(List<Join> joins) {
        this.joins = joins;
    }

    public String getOrderby() {
        return orderby;
    }

    public Select getSelect() {
        return select;
    }

    public void setSelect(Select select) {
        this.select = select;
    }

    public Where getWhere() {
        return where;
    }

    public void setWhere(Where where) {
        this.where = where;
    }

    public GroupBy getGroupBy() {
        return groupBy;
    }

    public void setGroupBy(GroupBy groupBy) {
        this.groupBy = groupBy;
    }

    public String getAlias() {
        return from.getAlias();
    }

    public QueryBuilder<E> setFirstResult(int firstResult) {
        this.firstResult = firstResult;
        return this;
    }

    public QueryBuilder<E> setMaxResults(int maxResults) {
        this.maxResults = maxResults;
        return this;
    }

    public <X> QueryBuilder<X> createNew(Class<X> class1) {
        return new QueryBuilder<X>(this.hibernateSessionProvider, this.persistenceContext);
    }

    private SessionFactory getSessionFactory() {
        return hibernateSessionProvider.getSessionFactory();
    }

    private static String uncapitalize(String name) {
        return Character.toLowerCase(name.charAt(0)) + name.substring(1);
    }

}