org.nextframework.persistence.GenericDAO.java Source code

Java tutorial

Introduction

Here is the source code for org.nextframework.persistence.GenericDAO.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.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;

import javax.persistence.Entity;
import javax.persistence.Transient;
import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.nextframework.bean.BeanDescriptor;
import org.nextframework.bean.BeanDescriptorFactory;
import org.nextframework.bean.annotation.DescriptionProperty;
import org.nextframework.controller.crud.ListViewFilter;
import org.nextframework.exception.NextException;
import org.nextframework.persistence.translator.AliasMap;
import org.nextframework.persistence.translator.QueryBuilderResultTranslator;
import org.nextframework.persistence.translator.QueryBuilderResultTranslatorImpl;
import org.nextframework.types.File;
import org.nextframework.util.ReflectionCache;
import org.nextframework.util.ReflectionCacheFactory;
import org.nextframework.util.Util;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.GenericTypeResolver;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.orm.hibernate4.HibernateTemplate;
import org.springframework.orm.hibernate4.support.HibernateDaoSupport;
import org.springframework.transaction.support.TransactionTemplate;

/**
 * Generic implementation of a DataAccessObject. 
 * <p> Implements basic data operations. It is aware of CRUD views and has specialized methods.
 * 
 * @author rogelgarcia
 * @param <BEAN>
 */
public class GenericDAO<BEAN> extends HibernateDaoSupport implements DAO<BEAN>, ApplicationContextAware {

    protected Log log = LogFactory.getLog(this.getClass());

    protected Class<BEAN> beanClass;
    protected String orderBy;
    protected TransactionTemplate transactionTemplate;
    protected FileDAO<File> fileDAO;
    protected JdbcTemplate jdbcTemplate;

    private String persistenceContext;

    /**
     * Spring application context
     */
    private ApplicationContext applicationContext;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public JdbcTemplate getJdbcTemplate() {
        if (jdbcTemplate == null) {
            String[] beans = applicationContext.getBeanNamesForType(DataSource.class);
            if (beans.length == 1) {
                jdbcTemplate = new JdbcTemplate(applicationContext.getBean(beans[0], DataSource.class));
            } else if (applicationContext instanceof ConfigurableApplicationContext) {
                //TODO THIS LOGIC SHOULD NOT BE HERE
                //check the qualifiers to find the correct datasource
                ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext) applicationContext)
                        .getBeanFactory();
                for (String beanName : beans) {
                    AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) beanFactory
                            .getBeanDefinition(beanName);
                    Set<AutowireCandidateQualifier> qualifiers = beanDefinition.getQualifiers();
                    for (AutowireCandidateQualifier autowireCandidateQualifier : qualifiers) {
                        String[] attributeNames = autowireCandidateQualifier.attributeNames();
                        for (String attribute : attributeNames) {
                            Object value = autowireCandidateQualifier.getMetadataAttribute(attribute).getValue();
                            if (getPersistenceContext().equals(value)) {
                                jdbcTemplate = new JdbcTemplate(
                                        applicationContext.getBean(beanName, DataSource.class));
                                return jdbcTemplate;
                            }
                        }
                    }
                }
            }
        }
        return jdbcTemplate;
    }

    /**
     * Cria um saveOrUpdateStrategy e salva o objeto (No executa)
     * @param entity
     */
    protected SaveOrUpdateStrategy save(Object entity) {
        return new SaveOrUpdateStrategy(getPersistenceContext(), entity).saveEntity();
    }

    /**
     * Cria um saveOrUpdateStrategy e salva o objeto (No executa)
     * @param entity
     */
    protected SaveOrUpdateStrategy save(Object entity, boolean clearSession) {
        return new SaveOrUpdateStrategy(getPersistenceContext(), entity).saveEntity(clearSession);
    }

    protected final List<BEAN> empty() {
        return new ArrayList<BEAN>();
    }

    @Override
    protected HibernateTemplate createHibernateTemplate(SessionFactory sessionFactory) {
        if (getHibernateTemplate() != null) {
            return getHibernateTemplate();
        }
        return super.createHibernateTemplate(sessionFactory);
    }

    public TransactionTemplate getTransactionTemplate() {
        return transactionTemplate;
    }

    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }

    public String getPersistenceContext() {
        return persistenceContext;
    }

    public void setPersistenceContext(String persistenceContext) {
        this.persistenceContext = persistenceContext;
    }

    public GenericDAO(Class<BEAN> beanClass) {
        this.beanClass = beanClass;
        ReflectionCache reflectionCache = ReflectionCacheFactory.getReflectionCache();
        DefaultOrderBy orderBy = reflectionCache.getAnnotation(this.getClass(), DefaultOrderBy.class);
        if (orderBy != null) {
            this.orderBy = orderBy.value();
        }
        checkFileProperties();
    }

    @SuppressWarnings("unchecked")
    public GenericDAO() {
        beanClass = (Class<BEAN>) GenericTypeResolver.resolveTypeArgument(this.getClass(), DAO.class);
        ReflectionCache reflectionCache = ReflectionCacheFactory.getReflectionCache();
        DefaultOrderBy orderBy = reflectionCache.getAnnotation(this.getClass(), DefaultOrderBy.class);
        if (orderBy != null) {
            this.orderBy = orderBy.value();
        }
        checkFileProperties();
    }

    protected void init() {

    }

    protected List<PropertyDescriptor> fileProperties = new ArrayList<PropertyDescriptor>();

    protected void checkFileProperties() {
        if (isDetectFileProperties()) {
            BeanWrapper beanWrapper;
            try {
                beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(beanClass.newInstance());
                PropertyDescriptor[] propertyDescriptors = beanWrapper.getPropertyDescriptors();
                for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                    if (File.class.isAssignableFrom(propertyDescriptor.getPropertyType())) {
                        if (propertyDescriptor.getReadMethod().getAnnotation(Transient.class) == null) {
                            fileProperties.add(propertyDescriptor);
                        }
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException("Cannot check file type for dao", e);
            }
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    protected final void initDao() throws Exception {
        if (fileProperties.size() > 0 && this.fileDAO == null) {
            //achar o DAO para os files...
            Map<String, FileDAO> beans = applicationContext.getBeansOfType(FileDAO.class);
            if (beans.size() == 1) {
                this.fileDAO = beans.values().iterator().next();
            } else if (beans.size() == 0) {
                //nao existe um DAO definido na aplicacao, definir um default
                this.fileDAO = new FileDAO((Class<File>) fileProperties.get(0).getPropertyType(), true);//todas as propriedades do tipo arquivo devem ser da mesma classe
                this.fileDAO.setHibernateTemplate(getHibernateTemplate());
                this.fileDAO.setJdbcTemplate(getJdbcTemplate());
                this.fileDAO.setSessionFactory(getSessionFactory());
                this.fileDAO.setTransactionTemplate(getTransactionTemplate());
            }
        }

        init();
    }

    /**
     * Indica se os atributos do tipo File devem ser detectados automaticamente para salvar e carregar ao carregar o bean
     * @return
     */
    protected boolean isDetectFileProperties() {
        return true;
    }

    /**
     * Cria um QueryBuilder para esse DAO j com o from configurado
     * (O From pode ser alterado)
     * @return
     */
    protected QueryBuilder<BEAN> query() {
        return newQueryBuilder(beanClass).from(beanClass);
    }

    /**
     * Cria um query builder para ser utilizado pelo DAO
     * @param <E>
     * @param clazz
     * @return
     */
    public <E> QueryBuilder<E> newQueryBuilder(Class<E> clazz) {
        return new QueryBuilder<E>(getPersistenceContext());
    }

    protected QueryBuilder<BEAN> queryWithOrderBy() {
        QueryBuilder<BEAN> query = newQueryBuilder(beanClass).from(beanClass);
        if (this.orderBy != null) {
            query.orderBy(orderBy);
        }
        return query;
    }

    /* (non-Javadoc)
     * @see org.nextframework.persistence.DAO#saveOrUpdate(BEAN)
     */
    public void saveOrUpdate(final BEAN bean) {
        SaveOrUpdateStrategy save = getSaveOrUpdateCompleteStrategy(bean);
        save.execute();
        getHibernateTemplate().flush();
    }

    protected SaveOrUpdateStrategy getSaveOrUpdateCompleteStrategy(final BEAN bean) {
        SaveOrUpdateStrategy save = save(bean);
        verifyFileToSave(bean, save);
        updateSaveOrUpdate(save);
        return save;
    }

    /** Verifica necessidade de persistir no banco algum atributo do tipo 'file'.**/
    protected void verifyFileToSave(final BEAN bean, SaveOrUpdateStrategy save) {
        if (autoManageFileProperties() && fileDAO != null) {
            for (final PropertyDescriptor pd : fileProperties) {
                save.attachBefore(new HibernateCommand() {
                    @Override
                    public Object doInHibernate(Session session) throws HibernateException {
                        fileDAO.saveFile(bean, pd.getName());
                        return null;
                    }
                });
            }
        }
    }

    public void bulkSaveOrUpdate(Collection<BEAN> list) {
        if (list.isEmpty()) {
            return;
        }
        SaveOrUpdateStrategy saveFull = new SaveOrUpdateStrategy(getPersistenceContext(), null);
        saveFull.clear();
        for (BEAN bean : list) {
            SaveOrUpdateStrategy save = save(bean, false);
            verifyFileToSave(bean, save);
            updateSaveOrUpdate(save);
            saveFull.attach(save);
        }
        saveFull.execute();
        getHibernateTemplate().flush();
    }

    /* (non-Javadoc)
     * @see org.nextframework.persistence.DAO#load(BEAN)
     */
    public BEAN load(BEAN bean) {
        if (bean == null) {
            return null;
        }
        return query().entity(bean).unique();
    }

    public BEAN loadById(Serializable id) {
        if (id == null) {
            return null;
        }
        return query().idEq(id).unique();
    }

    /* (non-Javadoc)
     * @see org.nextframework.persistence.DAO#loadForEntrada(BEAN)
     */
    public BEAN loadFormModel(BEAN bean) {
        if (bean == null) {
            return null;
        }
        QueryBuilder<BEAN> query = newQueryBuilder(beanClass).from(beanClass).entity(bean);
        // provavel que se existir uma propriedade do tipo File ela deva ser carregada
        if (autoManageFileProperties() && fileDAO != null) {
            for (PropertyDescriptor pd : fileProperties) {
                query.leftOuterJoinFetch(query.getAlias() + "." + pd.getName());
            }
        }
        updateFormQuery(query);
        return query.unique();
    }

    protected boolean autoManageFileProperties() {
        return true;
    }

    // campos uteis para fazer cache dos findAlls dos combos
    protected long cacheTime = 0;
    protected QueryBuilderResultTranslator translatorQueryFindForCombo;
    private String queryFindForCombo;
    private long lastRead;
    private WeakReference<List<?>> findForComboCache = new WeakReference<List<?>>(null);

    /* (non-Javadoc)
     * @see org.nextframework.persistence.DAO#findForCombo(java.lang.String)
     */
    @SuppressWarnings("unchecked")
    public List<BEAN> findForCombo(String... extraFields) {
        if (extraFields != null && extraFields.length > 0) {
            return newQueryBuilder(beanClass).select(getSelectClauseForIdAndDescription(extraFields))
                    .from(beanClass).orderBy(orderBy).list();
        } else {
            if (queryFindForCombo == null) {
                initQueryFindForCombo();
            }
            List<?> listCached = findForComboCache.get();
            if (listCached == null || (System.currentTimeMillis() - lastRead > cacheTime)) {
                listCached = getHibernateTemplate().find(queryFindForCombo);
                listCached = translatorQueryFindForCombo.translate(listCached);
                findForComboCache = new WeakReference<List<?>>(listCached);
                lastRead = System.currentTimeMillis();
            }
            return (List<BEAN>) listCached;
        }
    }

    @Deprecated
    public List<BEAN> findForCombo() {
        return findForCombo((String[]) null);
    }

    /* (non-Javadoc)
     * @see org.nextframework.persistence.DAO#findBy(java.lang.Object)
     */
    public List<BEAN> findBy(Object o) {
        return findBy(o, false, (String[]) null);
    }

    public List<BEAN> findByProperty(String propertyName, Object o) {
        QueryBuilder<BEAN> query = query();
        return query.where(query.getAlias() + "." + propertyName + " = ?", o).list();
    }

    public BEAN findByPropertyUnique(String propertyName, Object o) {
        QueryBuilder<BEAN> query = query();
        return query.where(query.getAlias() + "." + propertyName + " = ?", o).unique();
    }

    //cache
    private Map<Class<?>, String> mapaQueryFindByForCombo = new WeakHashMap<Class<?>, String>();
    private Map<Class<?>, String> mapaQueryFindBy = new WeakHashMap<Class<?>, String>();

    /* (non-Javadoc)
     * @see org.nextframework.persistence.DAO#findBy(java.lang.Object, boolean, java.lang.String)
     */
    @SuppressWarnings("unchecked")
    public List<BEAN> findBy(Object o, boolean forCombo, String... extraFields) {
        if (o == null) {
            return new ArrayList<BEAN>();
        }
        Class<? extends Object> propertyClass = o.getClass();
        while (propertyClass.getName().contains("$$")) {
            propertyClass = propertyClass.getSuperclass();
        }
        String queryString = null;
        if ((extraFields != null && extraFields.length > 0)
                || (forCombo && (queryString = mapaQueryFindByForCombo.get(propertyClass)) == null)
                || (!forCombo && (queryString = mapaQueryFindBy.get(propertyClass)) == null)) {
            //inicializa a query para essa classe
            //System.out.println("\n\n\nLOADING CLASSE "+this.beanClass+"  PROPRIEDADE CLASSE: "+o.getClass());
            String[] propertiesForClass = findPropertiesForClass(propertyClass);
            if (propertiesForClass.length == 1) {// achou uma propriedade
                String alias = Util.strings.uncaptalize(this.beanClass.getSimpleName());
                String property = propertiesForClass[0];
                QueryBuilder qb = queryWithOrderBy();
                qb.where(alias + "." + property + " = ? ", o);
                updateFindByQuery(qb);
                if (forCombo) {
                    if (extraFields != null && extraFields.length > 0) {
                        //verifcar se precisa fazer joins extras
                        int i = 0;
                        for (int j = 0; j < extraFields.length; j++) {
                            String extra = extraFields[j];
                            BeanDescriptor beanDescriptor = BeanDescriptorFactory.forClass(this.beanClass);
                            Type type = beanDescriptor.getPropertyDescriptor(extra).getType();
                            if (type instanceof Class) {
                                if (((Class) type).isAnnotationPresent(Entity.class)) {
                                    extra += "." + BeanDescriptorFactory.forClass((Class) type)
                                            .getDescriptionPropertyName();
                                }
                            }
                            if (extra.contains(".")) {
                                int ultimoponto = extra.lastIndexOf(".");
                                String path = extra.substring(0, ultimoponto);
                                qb.join(alias + "." + path + " autojoin" + i);
                                extraFields[j] = "autojoin" + i + extra.substring(ultimoponto);
                            }
                            i++;
                        }
                        //se for com extraFields no pode usar o cache (no existe cache do select quando tem extra properties)                  
                        qb.select(getSelectClauseForIdAndDescription(extraFields));
                        //String hbquery = qb.getQuery();   
                        //queryString = hbquery;
                        return qb.list();
                    } else {
                        qb.select(getSelectClauseForIdAndDescription());
                        String hbquery = qb.getQuery();
                        queryString = hbquery.replaceAll(":param0", "?");
                        mapaQueryFindByForCombo.put(propertyClass, queryString);
                    }
                } else {
                    String hbquery = qb.getQuery();
                    queryString = hbquery.replaceAll(":param0", "?");
                    ;
                    mapaQueryFindByForCombo.put(propertyClass, queryString);
                }
            } else if (propertiesForClass.length > 1) {// mais de uma propriedade do mesmo tipo
                throw new RuntimeException(
                        "No foi possvel executar findBy(..). Existe mais de uma propriedade da classe "
                                + propertyClass.getName() + " na classe " + this.beanClass.getName() + "  "
                                + Arrays.deepToString(propertiesForClass));
            } else {//nenhuma propriedade do tipo fornecido
                throw new RuntimeException(
                        "No foi possvel executar findBy(..). No existe nenhuma propriedade da classe "
                                + propertyClass.getName() + " na classe " + this.beanClass.getName());
            }
        }

        List list = getHibernateTemplate().find(queryString, o);
        if (forCombo) {
            initQueryFindForCombo();
            list = translatorQueryFindForCombo.translate(list);
        }

        return list;
    }

    /**
     * Atualiza as querys de findBy
     * ex.: findBy(Object o, boolean forCombo, String... extraFields)
     * @param query
     */
    protected void updateFindByQuery(QueryBuilder<?> query) {

    }

    protected String[] findPropertiesForClass(Class<? extends Object> propertyClass) {
        List<String> properties = new ArrayList<String>();
        ReflectionCache reflectionCache = ReflectionCacheFactory.getReflectionCache();
        Method[] methods = reflectionCache.getMethods(this.beanClass);
        for (Method method : methods) {
            if (Util.beans.isGetter(method)) {
                if (method.getReturnType().equals(propertyClass)) {
                    properties.add(Util.beans.getPropertyFromGetter(method.getName()));
                }
            }
        }
        return properties.toArray(new String[properties.size()]);
    }

    //cache
    private String comboSelect;

    /**
     * Retorna o select necessrio para apenas o @Id e o @DescriptionProperty
     * ex.: new QueryBuilder(...).select(getSelectClauseForIdAndDescription()).from(X.class);
     * @param extraFields Outros campos alm do @Id e o @DescriptionProperty que devem ser carregados
     * @return Clausula select montada
     */
    protected String getSelectClauseForIdAndDescription(String... extraFields) {
        String alias = Util.strings.uncaptalize(beanClass.getSimpleName());
        return getSelectClauseForIdAndDescription(alias, extraFields);
    }

    /**
     * Retorna o select necessrio para apenas o @Id e o @DescriptionProperty
     * ex.: new QueryBuilder(...).select(getSelectClauseForIdAndDescription()).from(X.class);
     * @param extraFields Outros campos alm do @Id e o @DescriptionProperty que devem ser carregados
     * @return Clausula select montada
     */
    protected String getSelectClauseForIdAndDescription(String alias, String... extraFields) {
        if (comboSelect == null || extraFields != null && extraFields.length > 0) {
            String[] selectedProperties = getComboSelectedProperties(alias);
            selectedProperties = organizeExtraFields(alias, selectedProperties, extraFields);
            String comboSelect = "";
            for (int i = 0; i < selectedProperties.length; i++) {
                String prop = selectedProperties[i];
                comboSelect = comboSelect + prop;
                if (i + 1 < selectedProperties.length) {
                    comboSelect = comboSelect + ", ";
                }
            }
            if (!(extraFields != null && extraFields.length > 0)) {
                this.comboSelect = comboSelect;
            } else {
                return comboSelect;
            }
        }
        return comboSelect;
    }

    protected String[] getComboSelectedProperties(String alias) {
        BeanDescriptor beanDescriptor = BeanDescriptorFactory.forClass(beanClass);
        String descriptionPropertyName = beanDescriptor.getDescriptionPropertyName();
        String idPropertyName = beanDescriptor.getIdPropertyName();
        String[] selectedProperties;
        if (descriptionPropertyName == null) {
            selectedProperties = new String[] { alias + "." + idPropertyName };
        } else {
            //verificar se o descriptionproperty utiliza outros campos
            String[] usingFields = getUsingFieldsForDescriptionProperty(beanDescriptor, descriptionPropertyName);

            if (usingFields != null && usingFields.length > 0) {
                selectedProperties = new String[usingFields.length + 1];
                selectedProperties[0] = alias + "." + idPropertyName;
                for (int i = 0; i < usingFields.length; i++) {
                    selectedProperties[i + 1] = alias + "." + usingFields[i];
                }
            } else {
                selectedProperties = new String[] { alias + "." + idPropertyName,
                        alias + "." + descriptionPropertyName };
                if (beanDescriptor.getPropertyDescriptor(descriptionPropertyName)
                        .getAnnotation(Transient.class) != null) {
                    //               log.warn("@DescriptionProperty of "+beanDescriptor.getTargetClass()+" must declare usingFields");
                    new RuntimeException("@DescriptionProperty of " + beanDescriptor.getTargetClass()
                            + " must declare usingFields").printStackTrace();
                }
            }

        }
        return selectedProperties;
    }

    private String[] getUsingFieldsForDescriptionProperty(BeanDescriptor beanDescriptor,
            String descriptionPropertyName) {
        Annotation[] annotations = beanDescriptor.getPropertyDescriptor(descriptionPropertyName).getAnnotations();
        DescriptionProperty descriptionProperty = null;
        for (Annotation annotation : annotations) {
            if (DescriptionProperty.class.isAssignableFrom(annotation.annotationType())) {
                descriptionProperty = (DescriptionProperty) annotation;
                break;
            }
        }
        Transient transientAnn = beanDescriptor.getPropertyDescriptor(descriptionPropertyName)
                .getAnnotation(Transient.class);
        String[] usingFields = null;
        if (descriptionProperty != null) {
            //TODO PROCURAR O @DescriptionProperty nas classes superiores
            usingFields = descriptionProperty.usingFields();
        }
        if (usingFields.length == 0 && transientAnn != null) {
            log.warn("Description property of " + beanDescriptor.getTargetClass()
                    + " is transient and does not declare usingFields");
        }
        return usingFields;
    }

    protected void initQueryFindForCombo() {
        if (translatorQueryFindForCombo == null) {
            //BeanDescriptor beanDescriptor = Next.getApplicationContext().getBeanDescriptor(null, beanClass);
            //String descriptionPropertyName = beanDescriptor.getDescriptionPropertyName();
            //String idPropertyName = beanDescriptor.getIdPropertyName();

            String alias = Util.strings.uncaptalize(beanClass.getSimpleName());
            String[] selectedProperties = getComboSelectedProperties(alias);
            String hbQueryFindForCombo = "select " + getSelectClauseForIdAndDescription() + " " + "from "
                    + beanClass.getName() + " " + alias;
            hbQueryFindForCombo = getQueryFindForCombo(hbQueryFindForCombo);
            if (orderBy != null && !hbQueryFindForCombo.contains("order by")) {
                hbQueryFindForCombo += "  order by " + orderBy;
            }
            translatorQueryFindForCombo = new QueryBuilderResultTranslatorImpl();
            translatorQueryFindForCombo.init(getSessionFactory(), selectedProperties,
                    new AliasMap[] { new AliasMap(alias, null, beanClass) });
            queryFindForCombo = hbQueryFindForCombo;
        }
    }

    private String[] organizeExtraFields(String alias, String[] selectedProperties, String... extraFields) {
        if (extraFields != null && extraFields.length > 0) {
            for (int i = 0; i < extraFields.length; i++) {
                if (!extraFields[i].contains(".")) {
                    extraFields[i] = alias + "." + extraFields[i];
                }
            }
            String[] oldselectedProperties = selectedProperties;
            selectedProperties = new String[selectedProperties.length + extraFields.length];
            System.arraycopy(oldselectedProperties, 0, selectedProperties, 0, oldselectedProperties.length);
            System.arraycopy(extraFields, 0, selectedProperties, oldselectedProperties.length, extraFields.length);
        }
        return selectedProperties;
    }

    //TODO RENOMEAR PARA UPDATEFINDFORCOMBOQUERY
    /**
     * Permite alterar a query que ser utilizada no findForCombo. O parametro  a query montada automaticamente.
     * Ser feito cache dessa query entao esse mtodo s ser chamado uma vez.
     * @param hbQueryFindForCombo
     * @return
     */
    protected String getQueryFindForCombo(String hbQueryFindForCombo) {
        return hbQueryFindForCombo;
    }

    /* (non-Javadoc)
     * @see org.nextframework.persistence.DAO#findAll()
     */
    public List<BEAN> findAll() {
        return findAll(this.orderBy);
    }

    public boolean isEmpty() {
        return query().setUseTranslator(false).select("1").setMaxResults(1).unique() == null;
    }

    /* (non-Javadoc)
     * @see org.nextframework.persistence.DAO#findAll(java.lang.String)
     */
    public List<BEAN> findAll(String orderBy) {
        return query().orderBy(orderBy).list();
    }

    /* (non-Javadoc)
     * @see org.nextframework.persistence.DAO#findForListagem(org.nextframework.controller.crud.ListingFilter)
     */
    public ResultList<BEAN> loadListModel(ListViewFilter filter) {
        QueryBuilder<BEAN> query = query();
        if (orderBy == null) {//ordenao default para telas de listagem de dados
            query.orderBy(Util.strings.uncaptalize(beanClass.getSimpleName()) + ".id");
        } else {
            query.orderBy(orderBy);
        }
        updateListQuery(query, filter);
        QueryBuilder<BEAN> queryBuilder = query;
        return new ResultListImpl<BEAN>(queryBuilder, filter);
    }

    /**
     * Override this method to update the list view query 
     * @param query
     * @param _filter cast to the class configured in the crudcontroller
     */
    public void updateListQuery(QueryBuilder<BEAN> query, ListViewFilter _filter) {
    }

    /**
     * Override this method to update the form view query
     * @param query
     */
    public void updateFormQuery(QueryBuilder<BEAN> query) {
    }

    /**
     * Override this method to update the strategy to save the bean
     * @param save
     */
    public void updateSaveOrUpdate(SaveOrUpdateStrategy save) {
    }

    /* (non-Javadoc)
     * @see org.nextframework.persistence.DAO#delete(BEAN)
     */
    @SuppressWarnings("all")
    public void delete(final BEAN bean) {
        HibernateTransactionSessionProvider sessionProvider = (HibernateTransactionSessionProvider) PersistenceConfiguration
                .getConfig().getSessionProvider();
        sessionProvider.executeInTransaction(new HibernateTransactionCommand() {
            @Override
            public Object doInHibernate(Session session, Object transactionStatus) {

                BEAN bean2 = bean;

                if (autoManageFileProperties() && fileDAO != null) {
                    BEAN load = load(bean2);
                    if (load != null) {
                        bean2 = load;
                    }
                }

                session.delete(bean2);
                if (autoManageFileProperties() && fileDAO != null) {
                    for (PropertyDescriptor pd : fileProperties) {
                        File fileToDelete = null;
                        try {
                            fileToDelete = (File) pd.getReadMethod().invoke(bean2);
                        } catch (Exception ex) {
                            throw new NextException("Erro ao ler atributo", ex);
                        }
                        if (fileToDelete != null) {
                            fileDAO.delete(fileToDelete);
                        }
                    }
                }

                session.flush();
                return null;
            }
        });
    }

    @SuppressWarnings("all")
    public Collection loadCollection(Object owner, String role) {
        owner = query().entity(owner).fetchCollection(role).unique();
        return Util.beans.getPropertyValue(owner, role);
    }

    public void loadDescriptionProperty(BEAN object) {
        BeanDescriptor beanDescriptor = BeanDescriptorFactory.forBean(object);
        String descriptionPropertyName = beanDescriptor.getDescriptionPropertyName();
        BEAN newValue = query().select(getSelectClauseForIdAndDescription()).from(beanClass).entity(object)
                .unique();

        try {
            Object value = PropertyAccessorFactory.forBeanPropertyAccess(newValue)
                    .getPropertyValue(descriptionPropertyName);
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(object);
            bw.setPropertyValue(descriptionPropertyName, value);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static CollectionFetcher getDAODelegateCollectionFetcher() {
        return new CollectionFetcher() {
            @SuppressWarnings("unchecked")
            public List<?> load(QueryBuilder<?> qb, Object owner, String collectionProperty, Class<?> itemType) {
                @SuppressWarnings("rawtypes")
                GenericDAO daoForClass = DAOUtils.getDAOForClass(itemType);
                daoForClass.updateFormQuery(qb);
                return qb.list();
            }
        };
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}