org.vulpe.model.dao.impl.jpa.AbstractVulpeBaseDAOJPA.java Source code

Java tutorial

Introduction

Here is the source code for org.vulpe.model.dao.impl.jpa.AbstractVulpeBaseDAOJPA.java

Source

/**
 * Vulpe Framework - Quick and Smart ;)
 * Copyright (C) 2011 Active Thread
 *
 * Este programa  software livre; voc pode redistribu-lo e/ou
 * modific-lo sob os termos da Licena Pblica Geral GNU, conforme
 * publicada pela Free Software Foundation; tanto a verso 2 da
 * Licena como (a seu critrio) qualquer verso mais nova.
 *
 * Este programa  distribudo na expectativa de ser til, mas SEM
 * QUALQUER GARANTIA; sem mesmo a garantia implcita de
 * COMERCIALIZAO ou de ADEQUAO A QUALQUER PROPSITO EM
 * PARTICULAR. Consulte a Licena Pblica Geral GNU para obter mais
 * detalhes.
 *
 * Voc deve ter recebido uma cpia da Licena Pblica Geral GNU
 * junto com este programa; se no, escreva para a Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 */
/**
 * Vulpe Framework - Quick and Smart ;)
 * Copyright (C) 2011 Active Thread
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.vulpe.model.dao.impl.jpa;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.Column;
import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.Table;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
import org.vulpe.audit.model.entity.AuditOccurrence;
import org.vulpe.commons.VulpeConstants.Model.Entity;
import org.vulpe.commons.util.VulpeHashMap;
import org.vulpe.commons.util.VulpeReflectUtil;
import org.vulpe.commons.util.VulpeStringUtil;
import org.vulpe.commons.util.VulpeValidationUtil;
import org.vulpe.exception.VulpeApplicationException;
import org.vulpe.model.annotations.Like;
import org.vulpe.model.annotations.QueryConfiguration;
import org.vulpe.model.annotations.QueryConfigurations;
import org.vulpe.model.annotations.QueryParameter;
import org.vulpe.model.annotations.Relationship;
import org.vulpe.model.annotations.Relationship.RelationshipScope;
import org.vulpe.model.dao.impl.AbstractVulpeBaseDAO;
import org.vulpe.model.entity.Parameter;
import org.vulpe.model.entity.VulpeEntity;

/**
 * Default implementation of DAO with JPA
 *
 * @author <a href="mailto:fabio.viana@vulpe.org">Fbio Viana</a>
 * @author <a href="mailto:felipe@vulpe.org">Geraldo Felipe</a>
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public abstract class AbstractVulpeBaseDAOJPA<ENTITY extends VulpeEntity<ID>, ID extends Serializable & Comparable<?>>
        extends AbstractVulpeBaseDAO<ENTITY, ID> {

    protected static final String CGLIB_ENHANCER = "EnhancerByCGLIB";
    protected static final String CGLIB_CALLBACK_0 = "CGLIB$CALLBACK_0";
    protected static final String CGLIB_CALLBACK_0_TARGET = "target";

    @PersistenceContext
    private EntityManager entityManager;

    /**
     *
     * @param <T>
     * @param entity
     */
    protected <T> void repairRelationship(final T entity) {
        final List<Field> fields = VulpeReflectUtil.getFields(entity.getClass());
        for (final Field field : fields) {
            final OneToMany oneToMany = field.getAnnotation(OneToMany.class);
            if (oneToMany != null) {
                try {
                    final List<ENTITY> entities = (List<ENTITY>) PropertyUtils.getProperty(entity, field.getName());
                    if (VulpeValidationUtil.isNotEmpty(entities)) {
                        for (final ENTITY entityChild : entities) {
                            repairRelationship(entityChild);
                            PropertyUtils.setProperty(entityChild, oneToMany.mappedBy(), entity);
                        }
                    }
                } catch (Exception e) {
                    LOG.error(e.getMessage());
                }
            }
        }
    }

    /**
     *
     * @param entities
     */
    private void repairInstance(final List<ENTITY> entities) {
        for (final ENTITY entity : entities) {
            if (VulpeValidationUtil.isNotEmpty(entity)) {
                final List<Field> fields = VulpeReflectUtil.getFields(entity.getClass());
                for (final Field field : fields) {
                    final ManyToOne manyToOne = field.getAnnotation(ManyToOne.class);
                    // final OneToMany oneToMany =
                    // field.getAnnotation(OneToMany.class);
                    if (manyToOne != null) {
                        try {
                            ENTITY value = (ENTITY) PropertyUtils.getProperty(entity, field.getName());
                            if (value != null && value.getClass().getSimpleName().contains("_javassist_")) {
                                final ENTITY newValue = (ENTITY) field.getType().newInstance();
                                final Object handler = VulpeReflectUtil.getFieldValue(value, "handler");
                                final ID id = VulpeReflectUtil.getFieldValue(handler, "id");
                                newValue.setId(id);
                                VulpeReflectUtil.setFieldValue(entity, field.getName(), newValue);
                            }
                        } catch (Exception e) {
                            LOG.error(e.getMessage());
                        }
                        // } else if (oneToMany != null) {
                        // try {
                        // List<ENTITY> childs =
                        // VulpeReflectUtil.getFieldValue(entity, field
                        // .getName());
                        // if (VulpeValidationUtil.isNotEmpty(childs)) {
                        // repairInstance(childs);
                        // }
                        // } catch (Exception e) {
                        // LOG.error(e.getMessage());
                        // }
                    }
                }
            }
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see org.vulpe.model.dao.VulpeDAO#merge(java.lang.Object)
     */
    public <T> T merge(final T entity) {
        if (entity instanceof AuditOccurrence) {
            entityManager.clear();
        }
        repairRelationship(entity);
        T merged = entity;
        if (((ENTITY) entity).map().containsKey(Entity.ONLY_UPDATE_DETAILS)) {
            final List<String> detailNames = (List<String>) ((ENTITY) entity).map().get(Entity.ONLY_UPDATE_DETAILS);
            if (VulpeValidationUtil.isNotEmpty(detailNames)) {
                for (final String detail : detailNames) {
                    List<ENTITY> details = null;
                    try {
                        details = (List<ENTITY>) PropertyUtils.getProperty(entity, detail);
                    } catch (Exception e) {
                        LOG.error(e.getMessage());
                    }
                    if (VulpeValidationUtil.isNotEmpty(details)) {
                        for (final ENTITY entity2 : details) {
                            loadEntityRelationships(entity2);
                            merge(entity2);
                        }
                    }
                }
            }
        } else {
            merged = entityManager.merge(entity);
            ((ENTITY) entity).setId(((ENTITY) merged).getId());
        }
        entityManager.flush();
        entityManager.clear();
        loadEntityRelationships((ENTITY) entity);
        return entity;
    }

    /**
     * Execute HQL query.
     *
     * @param <T>
     * @param hql
     * @param params
     * @return
     * @throws VulpeApplicationException
     */
    protected <T> List<T> execute(final String hql, final Map<String, Object> params)
            throws VulpeApplicationException {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Reading object: ".concat(hql));
        }
        final Query query = entityManager.createQuery(hql);
        setParams(query, params);
        return query.getResultList();
    }

    /*
     * (non-Javadoc)
     *
     * @see org.vulpe.model.dao.VulpeDAO#executeProcedure(java.lang.String,
     * java.util.List)
     */
    public CallableStatement executeProcedure(final String name, final List<Parameter> parameters)
            throws VulpeApplicationException {
        return executeCallableStatement(name, null, parameters);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.vulpe.model.dao.VulpeDAO#executeFunction(java.lang.String, int,
     * java.util.List)
     */
    @Override
    public CallableStatement executeFunction(final String name, final int returnType,
            final List<Parameter> parameters) throws VulpeApplicationException {
        return executeCallableStatement(name, returnType, parameters);
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.vulpe.model.dao.VulpeDAO#executeCallableStatement(java.lang.String,
     * java.lang.Integer, java.util.List)
     */
    public CallableStatement executeCallableStatement(final String name, final Integer returnType,
            final List<Parameter> parameters) throws VulpeApplicationException {
        CallableStatement cstmt = null;
        try {
            final Session session = (Session) entityManager.getDelegate();
            final SessionFactoryImplementor sessionFactoryImplementor = (SessionFactoryImplementor) session
                    .getSessionFactory();
            final ConnectionProvider connectionProvider = sessionFactoryImplementor.getConnectionProvider();
            final Connection connection = connectionProvider.getConnection();

            final StringBuilder call = new StringBuilder();
            call.append(returnType == null ? "{call " : "{? = call ");
            call.append(name);
            int count = 0;
            if (parameters != null) {
                call.append("(");
                do {
                    if (count > 0) {
                        call.append(",");
                    }
                    call.append("?");
                    ++count;
                } while (count < parameters.size());
                call.append(")");
            }
            call.append("}");
            if (parameters != null) {
                cstmt = connection.prepareCall(call.toString());
                if (returnType != null) {
                    if (returnType != 0) {
                        cstmt.registerOutParameter(1, returnType);
                        count = 1;
                    } else {
                        count = 0;
                    }
                }
                for (Parameter parameter : parameters) {
                    ++count;
                    if (parameter.getType() == Types.ARRAY) {
                        // Connection nativeConnection =
                        // cstmt.getConnection().getMetaData().getConnection();
                        // ArrayDescriptor objectArrayDescriptor =
                        // ArrayDescriptor.createDescriptor(
                        // parameter.getArrayType(), nativeConnection);
                        // Array array = new ARRAY(objectArrayDescriptor,
                        // nativeConnection, parameter
                        // .getArrayValues());
                        // cstmt.setArray(count, array);
                        LOG.info("");
                    } else {
                        cstmt.setObject(count, parameter.getValue(), parameter.getType());
                    }
                    if (parameter.isOut()) {
                        if (parameter.getType() == Types.ARRAY) {
                            cstmt.registerOutParameter(count, Types.ARRAY, parameter.getArrayType());
                        } else {
                            cstmt.registerOutParameter(count, parameter.getType());
                        }
                    }
                }
            }
            cstmt.execute();
        } catch (SQLException e) {
            throw new VulpeApplicationException(e.getMessage());
        }
        return cstmt;
    }

    /**
     * Prepares object Query, setting parameters
     */
    protected void setParams(final Query query, final Map<String, Object> params) {
        if (params != null) {
            for (final String name : params.keySet()) {
                Object value = params.get(name);
                final String paramName = name.replace("!", "").replace("_", "").replaceAll("\\.", "_");
                if (value instanceof String) {
                    value = ((String) value).replace("[like]", "");
                }
                query.setParameter(paramName, value);
            }
        }
    }

    /**
     * Returns NamedQuery defined in entity
     */
    protected NamedQuery getNamedQuery(final Class<?> entityClass, final String nameQuery) {
        if (entityClass.isAnnotationPresent(NamedQueries.class)) {
            final NamedQueries namedQueries = entityClass.getAnnotation(NamedQueries.class);
            for (NamedQuery namedQuery : namedQueries.value()) {
                if (namedQuery.name().equals(nameQuery)) {
                    return namedQuery;
                }
            }
        } else if (entityClass.isAnnotationPresent(NamedQuery.class)) {
            final NamedQuery namedQuery = entityClass.getAnnotation(NamedQuery.class);
            if (namedQuery.name().equals(nameQuery)) {
                return namedQuery;
            }
        }
        return null;
    }

    /**
     *
     * @param entity
     */
    protected void loadEntityRelationships(final ENTITY entity) {
        final List<ENTITY> entities = new ArrayList<ENTITY>();
        entities.add(entity);
        loadRelationships(entities, null, true);
    }

    /**
     *
     * @param attribute
     * @param parent
     * @return
     */
    private String loadRelationshipsMountReference(final String attribute, final String parent) {
        final StringBuilder hqlAttribute = new StringBuilder();
        final List<String> attributeList = new ArrayList<String>();
        String first = attribute.substring(0, attribute.indexOf('['));
        loadRelationshipsSeparateAttributes(attributeList, attribute.replace(first, ""), null);
        if (StringUtils.isNotBlank(first)) {
            first += "_";
        }
        for (final String attributeName : attributeList) {
            hqlAttribute.append(", obj.").append(attributeName).append(" as ").append(first)
                    .append(attributeName.replaceAll("\\.", "_"));
        }
        return hqlAttribute.toString();
    }

    /**
     *
     * @param attributeList
     * @param value
     */
    private void loadRelationshipsSeparateAttributes(final List<String> attributeList, String value,
            String parent) {
        if (StringUtils.isNotBlank(value)) {
            value = value.replaceAll(" ", "");
        }
        boolean initial = value.startsWith("[") && value.endsWith("]");
        if (!initial && value.contains("],")) {
            final String[] valueParts = value.split("\\],");
            int count = 1;
            for (String valuePart : valueParts) {
                int countBracketOpen = VulpeStringUtil.count(valuePart, "[");
                int countBracketClose = VulpeStringUtil.count(valuePart, "]");
                for (int i = countBracketOpen; i > countBracketClose; i--) {
                    valuePart += "]";
                }
                if (count == valueParts.length) {
                    value = value.replace(valuePart, "");
                } else {
                    value = value.replace(valuePart + "],", "");
                }
                loadRelationshipsSeparateAttributes(attributeList, valuePart, parent);
                ++count;
            }
        } else {
            String subParent = "";
            if (value.indexOf('[') > 0) {
                if (StringUtils.isEmpty(parent)) {
                    parent = value.substring(0, value.indexOf('['));
                } else {
                    final String parentTest = value.substring(0, value.indexOf('['));
                    if (!parent.endsWith(parentTest)) {
                        parent += parentTest;
                    }
                }
                if (parent.contains(",")) {
                    final String[] parentParts = parent.split(",");
                    int count = 1;
                    for (final String parentPart : parentParts) {
                        if (count == parentParts.length) {
                            parent = parentPart;
                        } else {
                            attributeList.add(parentPart);
                        }
                        ++count;
                    }
                }
            }
            if (value.contains("[")) {
                value = value.substring(value.indexOf('[') + 1, value.lastIndexOf("]"));
            }
            final String text = value.contains("[") ? value.substring(0, value.indexOf('[')) : value;
            final String[] textParts = text.split(",");
            parent = StringUtils.isNotBlank(parent) ? parent + "." : "";
            int count = 1;
            for (final String textPart : textParts) {
                if (count == textParts.length && !textPart.equals(value)) {
                    subParent = textPart;
                } else {
                    attributeList.add(parent + textPart);
                    if (textPart.equals(value)) {
                        value = "";
                    } else {
                        value = value.substring(textPart.length() + 1);
                    }
                }
                ++count;
            }
            if (StringUtils.isEmpty(subParent) && StringUtils.isNotBlank(value)) {
                attributeList.add(value);
            }
            if (value.contains("[")) {
                if (value.contains("],")) {
                    subParent = "";
                }
                loadRelationshipsSeparateAttributes(attributeList, value, parent + subParent);
            }
        }
    }

    /**
     * "[teste1[teste1_1,teste1_2[teste1_2_1,teste1_2_2,teste1_2_3[teste1_2_3_1,teste1_2_3_2],teste1_2_4]],teste2,teste3[teste3_1]]"
     * @param expression
     */
    //TODO
    public static void build(final String expression) {
        char currentCharacter, previousCharacter;
        int open = 0;
        int initialPosition = 0;
        int finalPosition = 0;
        String value = "";
        List<String> previous = new ArrayList<String>();
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < expression.length(); i++) {
            currentCharacter = expression.charAt(i);
            if (i > 0) {
                previousCharacter = expression.charAt(i - 1);
                if (previousCharacter == '[' || previousCharacter == ',' || previousCharacter == ']') {
                    initialPosition = i;
                }
                if (currentCharacter == '[' || currentCharacter == ',' || currentCharacter == ']') {
                    finalPosition = i;
                }
                if (previousCharacter == '[') {
                    ++open;
                    if (!value.isEmpty()) {
                        previous.add(value);
                    }
                }
                if (currentCharacter == ']') {
                    --open;
                    if (open > -1) {
                        previous.remove(open);
                    }
                }
            }
            if (finalPosition > initialPosition) {
                value = expression.substring(initialPosition, finalPosition);
                final StringBuilder hierarchy = new StringBuilder();
                for (final String parent : previous) {
                    hierarchy.append(parent).append('.');
                }
                hierarchy.append(value);
                list.add(hierarchy.toString());
                System.out.println(hierarchy.toString());
            }
        }
    }

    /**
     * Load relationships and optimize lazy load.
     *
     * @param entities
     * @param params
     */
    protected void loadRelationships(final List<ENTITY> entities, final Map<String, Object> params,
            final boolean onlyInMain) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Method loadRelationships - Start");
        }
        if (VulpeValidationUtil.isNotEmpty(entities)) {
            repairInstance(entities);
            final ENTITY entity = entities.get(0);
            final String queryConfigurationName = entity.getQueryConfigurationName();
            final Class<?> entityClass = initializeAndUnproxy(entity).getClass();
            final QueryConfigurations queryConfigurations = entityClass.getAnnotation(QueryConfigurations.class);
            if (queryConfigurations != null && queryConfigurations.value().length > 0) {
                for (final QueryConfiguration queryConfiguration : queryConfigurations.value()) {
                    if (queryConfiguration != null && queryConfiguration.name().equals(queryConfigurationName)
                            && queryConfiguration.relationships().length > 0) {
                        final List<ID> parentIds = new ArrayList<ID>();
                        for (final ENTITY parent : entities) {
                            parentIds.add(parent.getId());
                        }
                        for (final Relationship relationship : queryConfiguration.relationships()) {
                            final boolean loadAll = relationship.attributes().length == 1
                                    && "*".equals(relationship.attributes()[0]);
                            if ((!relationship.scope().equals(RelationshipScope.SELECT) && loadAll && !onlyInMain)
                                    || (relationship.scope().equals(RelationshipScope.SELECT) && onlyInMain)) {
                                continue;
                            }
                            try {
                                final StringBuilder hql = new StringBuilder();
                                final String parentName = VulpeStringUtil
                                        .getAttributeName(entityClass.getSimpleName());
                                final Class propertyType = PropertyUtils.getPropertyType(entityClass.newInstance(),
                                        relationship.property());
                                final OneToMany oneToMany = VulpeReflectUtil.getAnnotationInField(OneToMany.class,
                                        entityClass, relationship.property());
                                final Map<String, String> hqlAttributes = new HashMap<String, String>();
                                // select and from
                                hql.append(loadRelationshipsMountSelectAndFrom(relationship, parentName,
                                        propertyType, entity, hqlAttributes, oneToMany, loadAll));
                                // where
                                hql.append(
                                        loadRelationshipsMountWhere(relationship, parentName, params, oneToMany));
                                final Query query = entityManager.createQuery(hql.toString());
                                if (oneToMany != null) {
                                    query.setParameter("parentIds", parentIds);
                                } else {
                                    final List<ID> ids = new ArrayList<ID>();
                                    for (final ENTITY entity2 : entities) {
                                        final VulpeEntity<ID> propertyEntity = (VulpeEntity<ID>) PropertyUtils
                                                .getProperty(entity2, relationship.property());
                                        if (VulpeValidationUtil.isNotEmpty(propertyEntity)) {
                                            ids.add(propertyEntity.getId());
                                        }
                                    }
                                    if (VulpeValidationUtil.isEmpty(ids)) {
                                        continue;
                                    }
                                    query.setParameter("ids", ids);
                                }
                                if (relationship.parameters() != null) {
                                    for (final QueryParameter queryParameter : relationship.parameters()) {
                                        if (params.containsKey(queryParameter.equals().name())) {
                                            query.setParameter(queryParameter.equals().name(),
                                                    params.get(queryParameter.equals().name()));
                                        }
                                    }
                                }
                                final List<ENTITY> childs = new ArrayList<ENTITY>();
                                final Map<ID, ID> relationshipIds = new HashMap<ID, ID>();
                                if (oneToMany != null && loadAll) {
                                    final List<ENTITY> result = query.getResultList();
                                    childs.addAll(loadRelationshipsMountChild(relationship, result, relationshipIds,
                                            parentName));
                                } else {
                                    final List<Map> result = query.getResultList();
                                    childs.addAll(loadRelationshipsMountChild(relationship, result, relationshipIds,
                                            parentName, propertyType, hqlAttributes, oneToMany));
                                }
                                if (!onlyInMain) {
                                    final Session session = (Session) entityManager.getDelegate();
                                    session.clear();
                                }
                                loadRelationshipsMountEntities(relationship, entities, childs, relationshipIds,
                                        parentName, oneToMany);
                            } catch (Exception e) {
                                LOG.error(e.getMessage());
                            }
                        }
                    }
                }
            }
        }
        if (LOG.isDebugEnabled()) {
            LOG.debug("Method loadRelationships - End");
        }
    }

    private String loadRelationshipsMountSelectAndFrom(final Relationship relationship, final String parentName,
            final Class propertyType, final ENTITY firstEntity, final Map<String, String> hqlAttributes,
            final OneToMany oneToMany, final boolean loadAll) {
        final StringBuilder hql = new StringBuilder();
        try {
            final List<String> hqlJoin = new ArrayList<String>();
            if (oneToMany != null && loadAll) {
                hql.append("select obj ");
            } else {
                hql.append("select new map(obj.id as id");
                for (final String attribute : relationship.attributes()) {
                    if ("id".equals(attribute)) {
                        continue;
                    }
                    if (oneToMany != null) {
                        final Class attributeType = PropertyUtils
                                .getPropertyType(oneToMany.targetEntity().newInstance(), attribute);
                        boolean manyToOne = attributeType.isAnnotationPresent(ManyToOne.class)
                                || VulpeEntity.class.isAssignableFrom(attributeType);
                        hql.append(", ").append("obj.").append(attribute + (manyToOne ? ".id" : "")).append(" as ")
                                .append(attribute);
                    } else {
                        if (attribute.contains("[")) {
                            final StringBuilder hqlAttribute = new StringBuilder("select new map(obj.id as id");
                            String attributeParent = attribute.substring(0, attribute.indexOf('['));
                            hqlAttribute.append(loadRelationshipsMountReference(attribute, null));
                            if (StringUtils.isEmpty(attributeParent)) {
                                attributeParent = relationship.property();
                                final Class attributeParentType = PropertyUtils.getPropertyType(firstEntity,
                                        attributeParent);
                                hqlAttribute.append(") from ").append(attributeParentType.getSimpleName())
                                        .append(" obj");
                            } else {
                                final Class attributeParentType = PropertyUtils
                                        .getPropertyType(propertyType.newInstance(), attributeParent);
                                hqlAttribute.append(") from ").append(attributeParentType.getSimpleName())
                                        .append(" obj");
                                int joinCount = hqlJoin.size() + 1;
                                hqlJoin.add((joinCount > 0 ? "" : ",") + "left outer join obj." + attributeParent
                                        + " obj" + joinCount);
                                hql.append(", obj").append(joinCount).append(".id").append(" as ")
                                        .append(attributeParent);
                            }
                            hqlAttribute.append(" where obj.id in (:ids)");
                            hqlAttributes.put(attribute, hqlAttribute.toString());
                        } else {
                            final Class attributeType = PropertyUtils.getPropertyType(propertyType.newInstance(),
                                    attribute);
                            boolean manyToOne = attributeType.isAnnotationPresent(ManyToOne.class)
                                    || VulpeEntity.class.isAssignableFrom(attributeType);
                            hql.append(", ").append("obj.").append(attribute + (manyToOne ? ".id" : ""))
                                    .append(" as ").append(attribute);
                        }
                    }
                }
                if (oneToMany != null) {
                    hql.append(", obj.").append(parentName).append(".id as ").append(parentName);
                }
                hql.append(")");
            }
            final String className = oneToMany == null ? propertyType.getSimpleName()
                    : oneToMany.targetEntity().getSimpleName();
            hql.append(" from ").append(className).append(" obj ");
            for (final String join : hqlJoin) {
                hql.append(join);
            }
        } catch (Exception e) {
            LOG.error(e.getMessage());
        }
        return hql.toString();
    }

    private String loadRelationshipsMountWhere(final Relationship relationship, final String parentName,
            final Map<String, Object> params, final OneToMany oneToMany) {
        final StringBuilder hql = new StringBuilder();
        if (oneToMany != null) {
            hql.append(" where obj.").append(parentName).append(".id in (:parentIds)");
        } else {
            hql.append(" where obj.id in (:ids)");
        }
        if (StringUtils.isNotBlank(relationship.condition())) {
            hql.append(" and ").append(relationship.condition());
        }

        if (relationship.parameters() != null) {
            for (final QueryParameter queryParameter : relationship.parameters()) {
                if (params.containsKey(queryParameter.equals().name())) {
                    hql.append(" and ");
                    hql.append(StringUtils.isNotBlank(queryParameter.equals().alias())
                            ? queryParameter.equals().alias()
                            : "obj").append(".");
                    hql.append(queryParameter.equals().name());
                    final Like like = VulpeReflectUtil.getAnnotationInField(Like.class, oneToMany.targetEntity(),
                            queryParameter.equals().name());
                    hql.append(" ").append(like != null ? "like" : queryParameter.equals().operator().getValue())
                            .append(" ");
                    hql.append(":").append(queryParameter.equals().name());
                }
            }
        }
        return hql.toString();
    }

    private List<ENTITY> loadRelationshipsMountChild(final Relationship relationship, final List<ENTITY> result,
            final Map<ID, ID> relationshipIds, final String parentName) {
        final List<ENTITY> childs = new ArrayList<ENTITY>();
        try {
            for (final ENTITY entity : result) {
                entity.setQueryConfigurationName(relationship.forceLoadQueryConfiguration());
                loadEntityRelationships(entity);
                final ENTITY parent = (ENTITY) PropertyUtils.getProperty(entity, parentName);
                relationshipIds.put(entity.getId(), parent.getId());
                childs.add(entity);
            }
        } catch (Exception e) {
            LOG.error(e.getMessage());
        }
        return childs;
    }

    private List<ENTITY> loadRelationshipsMountChild(final Relationship relationship, final List<Map> result,
            final Map<ID, ID> relationshipIds, final String parentName, final Class propertyType,
            final Map<String, String> hqlAttributes, final OneToMany oneToMany) {
        final List<ENTITY> childs = new ArrayList<ENTITY>();
        try {
            for (final Map map : result) {
                final ENTITY child = (ENTITY) (oneToMany != null ? oneToMany.targetEntity().newInstance()
                        : propertyType.newInstance());
                VulpeReflectUtil.setFieldValue(child, "id", map.get("id"));
                relationshipIds.put(child.getId(), (ID) map.get(parentName));
                childs.add(child);
            }
            for (final String attribute : relationship.attributes()) {
                if (hqlAttributes.containsKey(attribute)) {
                    final String attributeParent = attribute.substring(0, attribute.indexOf('['));
                    final String hqlAttribute = hqlAttributes.get(attribute);
                    final List<ID> ids = new ArrayList<ID>();
                    for (final ENTITY child : childs) {
                        Map map = null;
                        for (final Map resultMap : result) {
                            if (child.getId().equals((Long) resultMap.get("id"))) {
                                map = resultMap;
                                break;
                            }
                        }
                        if (StringUtils.isNotBlank(attributeParent)) {
                            final Class attributeType = PropertyUtils.getPropertyType(propertyType.newInstance(),
                                    attributeParent);
                            final VulpeEntity<ID> newAttribute = (VulpeEntity<ID>) attributeType.newInstance();
                            newAttribute.setId((ID) map.get(attributeParent));
                            ids.add(newAttribute.getId());
                            VulpeReflectUtil.setFieldValue(child, attributeParent, newAttribute);
                        } else {
                            ids.add(child.getId());
                        }
                    }
                    if (StringUtils.isNotBlank(attributeParent)) {
                        final Query queryAttribute = getEntityManager().createQuery(hqlAttribute)
                                .setParameter("ids", ids);
                        final List<Map> listAttributeMap = (List<Map>) queryAttribute.getResultList();
                        for (ENTITY child : childs) {
                            final Class attributeType = PropertyUtils.getPropertyType(propertyType.newInstance(),
                                    attributeParent);
                            final VulpeEntity<ID> newAttribute = (VulpeEntity<ID>) attributeType.newInstance();
                            for (Map map : listAttributeMap) {
                                if (((Long) map.get("id")).equals(child.getId())) {
                                    loadRelationshipsMountProperties(map, newAttribute, attribute, null);
                                }
                            }
                            VulpeReflectUtil.setFieldValue(child, attributeParent, newAttribute);
                        }
                    } else {
                        final Query queryAttribute = getEntityManager().createQuery(hqlAttribute)
                                .setParameter("ids", ids);
                        final List<Map> listAttributeMap = (List<Map>) queryAttribute.getResultList();
                        for (final ENTITY child : childs) {
                            for (final Map map : listAttributeMap) {
                                if (((Long) map.get("id")).equals(child.getId())) {
                                    loadRelationshipsMountProperties(map, child, attribute, null);
                                }
                            }
                        }
                    }
                } else {
                    for (final ENTITY child : childs) {
                        Map map = null;
                        for (final Map resultMap : result) {
                            if (child.getId().equals((Long) resultMap.get("id"))) {
                                map = resultMap;
                                break;
                            }
                        }
                        final Class attributeType = PropertyUtils.getPropertyType(child, attribute);
                        boolean manyToOne = attributeType.isAnnotationPresent(ManyToOne.class)
                                || VulpeEntity.class.isAssignableFrom(attributeType);
                        VulpeReflectUtil.instanciate(child, attribute + (manyToOne ? ".id" : ""),
                                map.get(attribute));
                    }
                }
            }
            if (relationship.forceLoad()) {
                if (!"default".equals(relationship.forceLoadQueryConfiguration())) {
                    for (final ENTITY entity : childs) {
                        entity.setQueryConfigurationName(relationship.forceLoadQueryConfiguration());
                    }
                }
                loadRelationships(childs, null, false);
            }
        } catch (Exception e) {
            LOG.error(e.getMessage());
        }
        return childs;
    }

    private void loadRelationshipsMountEntities(final Relationship relationship, final List<ENTITY> entities,
            final List<ENTITY> childs, final Map<ID, ID> relationshipIds, final String parentName,
            final OneToMany oneToMany) {
        try {
            for (final ENTITY parent : entities) {
                if (VulpeValidationUtil.isEmpty(childs)) {
                    PropertyUtils.setProperty(parent, relationship.property(), null);
                } else {
                    final List<ENTITY> loadedChilds = new ArrayList<ENTITY>();
                    for (final ENTITY child : childs) {
                        if (oneToMany != null) {
                            final ID parentId = (ID) relationshipIds.get(child.getId());
                            if (parent.getId().equals(parentId)) {
                                PropertyUtils.setProperty(child, parentName, parent);
                                loadedChilds.add(child);
                            }
                            PropertyUtils.setProperty(parent, relationship.property(), loadedChilds);
                        } else {
                            final VulpeEntity<ID> parentChild = (VulpeEntity<ID>) PropertyUtils.getProperty(parent,
                                    relationship.property());
                            if (VulpeValidationUtil.isNotEmpty(parentChild)
                                    && child.getId().equals(parentChild.getId())) {
                                PropertyUtils.setProperty(parent, relationship.property(), child);
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            LOG.error(e.getMessage());
        }
    }

    /**
     *
     * @param map
     * @param entity
     * @param attribute
     * @param parent
     */
    private void loadRelationshipsMountProperties(final Map<String, Object> map, final VulpeEntity<ID> entity,
            final String attribute, final String parent) {
        try {
            final List<String> attributeList = new ArrayList<String>();
            String first = attribute.substring(0, attribute.indexOf('['));
            loadRelationshipsSeparateAttributes(attributeList, attribute.replace(first, ""), null);
            if (StringUtils.isNotBlank(first)) {
                first += "_";
            }
            for (final String attributeName : attributeList) {
                VulpeReflectUtil.instanciate(entity, attributeName,
                        map.get(first + attributeName.replaceAll("\\.", "_")));
            }
        } catch (Exception e) {
            LOG.error(e.getMessage());
        }
    }

    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    public EntityManager getEntityManager() {
        return entityManager;
    }

    public Object findByNamedQuery(final String name) {
        try {
            return getEntityManager().createNamedQuery(name).getSingleResult();
        } catch (EntityNotFoundException e) {
            return null;
        }
    }

    public Object findByNamedQueryAndNamedParams(final String name, final Map<String, Object> map) {
        try {
            final Query query = getEntityManager().createNamedQuery(name);
            for (final String parameter : map.keySet()) {
                query.setParameter(parameter, map.get(parameter));
            }
            return query.getSingleResult();
        } catch (EntityNotFoundException e) {
            return null;
        }
    }

    public List<?> listByNamedQuery(final String name) {
        return getEntityManager().createNamedQuery(name).getResultList();
    }

    public List<?> listByNamedQueryAndNamedParams(final String name, final Map<String, Object> map) {
        final Query query = getEntityManager().createNamedQuery(name);
        for (final String parameter : map.keySet()) {
            if (parameter.equals("limit")) {
                continue;
            }
            query.setParameter(parameter, map.get(parameter));
        }
        final Object limit = map.get("limit");
        if (limit != null) {
            query.setMaxResults((Integer) limit);
        }
        return query.getResultList();
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.vulpe.model.dao.VulpeDAO#updateSomeAttributes(org.vulpe.model.entity
     * .VulpeEntity)
     */
    @Override
    public void updateSomeAttributes(final ENTITY entity) {
        if (entity.getId() == null) {
            LOG.debug(entity.getClass().getSimpleName() + " id not found");
            return;
        }
        final StringBuilder sql = new StringBuilder(getUpdateSomeAttributtesSQL(entity));
        try {
            final VulpeHashMap<String, Object> map = getUpdateSomeAttributesMap(entity);
            final Field idField = VulpeReflectUtil.getField(entity.getClass(), "id");
            final Column column = idField.getAnnotation(Column.class);
            sql.append(column != null ? column.name() : "id").append(" = ").append(entity.getId());
            final Query query = entityManager.createNativeQuery(sql.toString());
            for (final String key : map.keySet()) {
                final Object value = map.get(key);
                if (value instanceof Enum) {
                    query.setParameter(key, value.toString());
                } else if (value instanceof VulpeEntity) {
                    query.setParameter(key, ((VulpeEntity<ID>) value).getId());
                } else {
                    query.setParameter(key, value);
                }
            }
            query.executeUpdate();
            entityManager.flush();
        } catch (Exception e) {
            LOG.error(e.getMessage());
        }
    }

    @Override
    public void updateSomeAttributes(final List<ENTITY> list) {
        for (final ENTITY entity : list) {
            updateSomeAttributes(entity);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.vulpe.model.dao.VulpeDAO#updateSomeAttributes(org.vulpe.model.entity
     * .VulpeEntity, java.util.List)
     */
    @Override
    public void updateSomeAttributes(final ENTITY entity, final List<ID> ids) {
        if (VulpeValidationUtil.isEmpty(ids)) {
            LOG.debug(entity.getClass().getSimpleName() + " id list not found");
            return;
        }
        final StringBuilder sql = new StringBuilder(getUpdateSomeAttributtesSQL(entity));
        try {
            final VulpeHashMap<String, Object> map = getUpdateSomeAttributesMap(entity);
            StringBuilder sqlIds = new StringBuilder();
            for (final ID id : ids) {
                if (sqlIds.length() > 0) {
                    sqlIds.append(",");
                }
                sqlIds.append(((Long) id).intValue());
            }
            final Field idField = VulpeReflectUtil.getField(entity.getClass(), "id");
            final Column column = idField.getAnnotation(Column.class);
            sql.append(column != null ? column.name() : "id").append(" in (").append(sqlIds).append(")");
            final Query query = entityManager.createNativeQuery(sql.toString());
            for (final String key : map.keySet()) {
                final Object value = map.get(key);
                if (value instanceof Enum) {
                    query.setParameter(key, value.toString());
                } else if (value instanceof VulpeEntity) {
                    query.setParameter(key, ((VulpeEntity<ID>) value).getId());
                } else {
                    query.setParameter(key, value);
                }
            }
            query.executeUpdate();
            entityManager.flush();
        } catch (Exception e) {
            LOG.error(e.getMessage());
        }
    }

    private String getUpdateSomeAttributtesSQL(final ENTITY entity) {
        final StringBuilder sql = new StringBuilder();
        sql.append("update ");
        final Table table = entity.getClass().getAnnotation(Table.class);
        if (table != null) {
            sql.append(table.name());
        } else {
            sql.append(entity.getClass().getSimpleName());
        }
        sql.append(" set ");
        int count = 0;
        for (final String key : getUpdateSomeAttributesMap(entity).keySet()) {
            if (count > 0) {
                sql.append(", ");
            }
            sql.append(key).append(" = ").append(":").append(key);
            ++count;
        }
        sql.append(" where ");
        return sql.toString();
    }

    private VulpeHashMap<String, Object> getUpdateSomeAttributesMap(final ENTITY entity) {
        final List<Field> fields = VulpeReflectUtil.getFields(entity.getClass());
        final VulpeHashMap<String, Object> map = new VulpeHashMap<String, Object>();
        for (final Field field : fields) {
            if (field.getName().equals("id") || Modifier.isTransient(field.getModifiers())) {
                continue;
            }
            final Object value = VulpeReflectUtil.getFieldValue(entity, field.getName());
            if (VulpeValidationUtil.isNotEmpty(value)) {
                String columnName = field.getName();
                if (entity.fieldColumnMap().containsKey(field.getName())) {
                    columnName = entity.fieldColumnMap().get(field.getName());
                } else {
                    final Column column = field.getAnnotation(Column.class);
                    final JoinColumn joinColumn = field.getAnnotation(JoinColumn.class);
                    if (column != null) {
                        columnName = column.name();
                    } else if (joinColumn != null) {
                        columnName = (StringUtils.isNotBlank(joinColumn.referencedColumnName())
                                ? joinColumn.referencedColumnName()
                                : joinColumn.name());
                    }
                }
                map.put(columnName, value);
            }
        }
        return map;
    }

    protected <T> T initializeAndUnproxy(T entity) {
        if (entity == null) {
            throw new NullPointerException("Entity passed for initialization is null");
        }

        Hibernate.initialize(entity);
        return unproxy(entity);
    }

    protected <T> T unproxy(T entity) {
        if (entity instanceof HibernateProxy) {
            entity = (T) ((HibernateProxy) entity).getHibernateLazyInitializer().getImplementation();
        }
        return entity;
    }

    public class QueryFunction {
        public String execute(final ENTITY entity, final String query, final String token) {
            String queryModified = query;
            final String initialToken = token + "(";
            while (queryModified.contains(initialToken)) {
                final int initialPos = queryModified.indexOf(token);
                final int initialAttributePos = queryModified.indexOf(initialToken) + initialToken.length();
                final int finalPos = queryModified.indexOf(")", initialPos);
                final String queryAttributte = queryModified.substring(initialAttributePos,
                        queryModified.indexOf(")", initialAttributePos));
                queryModified = queryModified.substring(0, initialPos) + queryModified.substring(finalPos + 1);
                final int openPos = queryModified.indexOf("{");
                final int closePos = queryModified.indexOf("}", openPos);
                if (checkCondition(entity, queryAttributte)) {
                    queryModified = queryModified.substring(0, openPos)
                            + queryModified.substring(openPos + 1, closePos)
                            + queryModified.substring(closePos + 1);
                } else {
                    queryModified = queryModified.substring(0, openPos) + queryModified.substring(closePos + 1);
                }
            }
            return queryModified;
        }

        public boolean checkCondition(final ENTITY entity, final String queryAttributte) {
            return false;
        }
    }

    protected String validateQueryFunctions(final ENTITY entity, final String query) {
        String whereModified = emptyQueryFunction(entity, query);
        whereModified = notEmptyQueryFunction(entity, whereModified);
        return whereModified;
    }

    protected String notEmptyQueryFunction(final ENTITY entity, final String query) {
        return new QueryFunction() {
            @Override
            public boolean checkCondition(ENTITY entity, String queryAttributte) {
                return VulpeValidationUtil.isNotEmpty(VulpeReflectUtil.getFieldValue(entity, queryAttributte));
            };
        }.execute(entity, query, "notEmpty");
    }

    protected String emptyQueryFunction(final ENTITY entity, final String query) {
        return new QueryFunction() {
            @Override
            public boolean checkCondition(ENTITY entity, String queryAttributte) {
                return VulpeValidationUtil.isEmpty(VulpeReflectUtil.getFieldValue(entity, queryAttributte));
            };
        }.execute(entity, query, "empty");
    }

}