All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ctp.cdi.query.handler.EntityDaoHandler Maven / Gradle / Ivy

The newest version!
package com.ctp.cdi.query.handler;

import static com.ctp.cdi.query.util.EntityUtils.entityName;
import static com.ctp.cdi.query.util.QueryUtils.isEmpty;
import static com.ctp.cdi.query.util.QueryUtils.isString;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.metamodel.SingularAttribute;

import org.jboss.solder.logging.Logger;
import org.jboss.solder.properties.Property;
import org.jboss.solder.properties.query.NamedPropertyCriteria;
import org.jboss.solder.properties.query.PropertyQueries;

import com.ctp.cdi.query.AbstractEntityDao;
import com.ctp.cdi.query.EntityDao;
import com.ctp.cdi.query.builder.QueryBuilder;
import com.ctp.cdi.query.spi.DelegateQueryHandler;
import com.ctp.cdi.query.spi.QueryInvocationContext;

/**
 * Implement basic functionality from the {@link EntityDao}.
 *
 * @author thomashug
 *
 * @param    Entity type.
 * @param   Primary key type, must be a serializable.
 */
public class EntityDaoHandler extends AbstractEntityDao
        implements DelegateQueryHandler {

    private final Logger log = Logger.getLogger(EntityDaoHandler.class);
    
    @Inject
    private QueryInvocationContext context;

    @Override
    public E save(E entity) {
        if (context.isNew(entity)) {
            entityManager().persist(entity);
            return entity;
        }
        return entityManager().merge(entity);
    }

    @Override
    public E saveAndFlush(E entity) {
        E result = save(entity);
        entityManager().flush();
        return result;
    }

    @Override
    public void refresh(E entity) {
        entityManager().refresh(entity);
    }

    @Override
    public E findBy(PK primaryKey) {
        return entityManager().find(entityClass(), primaryKey);
    }

    @Override
    public List findBy(E example, SingularAttribute... attributes) {
        return findBy(example, -1, -1, attributes);
    }

    @Override
    public List findBy(E example, int start, int max, SingularAttribute... attributes) {
        return executeExampleQuery(example,start,max,false,attributes);
    }

    @Override
    public List findByLike(E example, SingularAttribute... attributes) {
        return findByLike(example, -1, -1, attributes);
    }

    @Override
    public List findByLike(E example, int start, int max, SingularAttribute... attributes) {
        return executeExampleQuery(example,start,max,true,attributes);
    }

    @Override
    public List findAll() {
        return entityManager().createQuery(allQuery(), entityClass()).getResultList();
    }

    @Override
    public List findAll(int start, int max) {
        TypedQuery query = entityManager().createQuery(allQuery(), entityClass());
        if (start > 0) {
            query.setFirstResult(start);
        }
        if (max > 0) {
            query.setMaxResults(max);
        }
        return query.getResultList();
    }

    @Override
    public Long count() {
        return entityManager().createQuery(countQuery(), Long.class).getSingleResult();
    }

    @Override
    public Long count(E example, SingularAttribute... attributes) {
        return executeCountQuery(example,false,attributes);
    }

    @Override
    public Long countLike(E example, SingularAttribute... attributes) {
        return executeCountQuery(example,true,attributes);
    }

    @Override
    public void remove(E entity) {
        entityManager().remove(entity);
    }

    @Override
    public void flush() {
        entityManager().flush();
    }

    @Override
    public EntityManager entityManager() {
        return context.getEntityManager();
    }

    @Override
    public CriteriaQuery criteriaQuery() {
        return entityManager().getCriteriaBuilder().createQuery(entityClass());
    }

    @Override
    @SuppressWarnings("unchecked")
    public Class entityClass() {
        return (Class) context.getEntityClass();
    }

    // ----------------------------------------------------------------------------
    // PRIVATE
    // ----------------------------------------------------------------------------

    private String allQuery() {
        return QueryBuilder.selectQuery(entityName(entityClass()));
    }

    private String countQuery() {
        return QueryBuilder.countQuery(entityName(entityClass()));
    }

    private String exampleQuery(String queryBase, List> properties, boolean useLikeOperator) {
        StringBuilder jpqlQuery = new StringBuilder(queryBase).append(" where ");
        jpqlQuery.append(prepareWhere(properties, useLikeOperator));
        return jpqlQuery.toString();
    }

    private void addParameters(TypedQuery query, E example, List> properties, boolean useLikeOperator) {
        for (Property property : properties) {
            property.setAccessible();
            query.setParameter(property.getName(), transform(property.getValue(example), useLikeOperator));
        }
    }

    private Object transform(Object value, final boolean useLikeOperator) {
        if (value != null && useLikeOperator && isString(value)) {
            // seems to be an OpenJPA bug:
            // parameters in querys fail validation, e.g. UPPER(e.name) like UPPER(:name) 
            String result = ((String) value).toUpperCase();
            return "%" + result + "%";
        }
        return value;
    }

    private String prepareWhere(List> properties, boolean useLikeOperator) {
        Iterator> iterator = properties.iterator();
        StringBuilder result = new StringBuilder();
        while (iterator.hasNext()) {
            Property property = iterator.next();
            String name = property.getName();
            if (useLikeOperator && property.getJavaClass().getName().equals(String.class.getName())) {
                result.append("UPPER(e.").append(name).append(") like :").append(name)
                        .append(iterator.hasNext() ? " and " : "");
            } else {
                result.append("e.").append(name).append(" = :").append(name).append(iterator.hasNext() ? " and " : "");
            }
        }
        return result.toString();
    }

    private List extractPropertyNames(SingularAttribute... attributes) {
        List result = new ArrayList(attributes.length);
        for (SingularAttribute attribute : attributes) {
            result.add(attribute.getName());
        }
        return result;
    }
    
    private  List> extractProperties(SingularAttribute... attributes) {
        List names = extractPropertyNames(attributes);
        List> properties = PropertyQueries.createQuery(entityClass())
                .addCriteria(new NamedPropertyCriteria(names.toArray(new String[] {}))).getResultList();
        return properties;
    }
    
    private List executeExampleQuery(E example, int start, int max, boolean useLikeOperator, SingularAttribute... attributes) {
        //Not sure if this should be the intended behaviour
        //when we don't get any attributes maybe we should
        //return a empty list instead of all results
        if (isEmpty(attributes)) {
            return findAll(start, max);
        }

        List> properties = extractProperties(attributes);
        String jpqlQuery = exampleQuery(allQuery(), properties, useLikeOperator);
        log.debugv("findBy|findByLike: Created query {0}", jpqlQuery);
        TypedQuery query = entityManager().createQuery(jpqlQuery, entityClass());

        // set starting position
        if (start > 0) {
            query.setFirstResult(start);
        }

        // set maximum results
        if (max > 0) {
            query.setMaxResults(max);
        }

        addParameters(query, example, properties, useLikeOperator);
        return query.getResultList();
    }
    
    private Long executeCountQuery(E example, boolean useLikeOperator, SingularAttribute... attributes) {
        if (isEmpty(attributes)) {
            return count();
        }
        List> properties = extractProperties(attributes);
        String jpqlQuery = exampleQuery(countQuery(), properties, useLikeOperator);
        log.debugv("count: Created query {0}", jpqlQuery);
        TypedQuery query = entityManager().createQuery(jpqlQuery, Long.class);
        addParameters(query, example, properties, useLikeOperator);
        return query.getSingleResult();
    }

}