Package org.jsf2jpa.ejbs

Source Code of org.jsf2jpa.ejbs.AbstractFacade

/*
* The MIT License
*
* Copyright 2011 ASementsov.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jsf2jpa.ejbs;

import java.io.Serializable;
import java.math.BigInteger;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.ejb.EJBException;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;
import javax.persistence.metamodel.EntityType;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import org.jsf2jpa.entities.AbstractAttribute;
import org.jsf2jpa.entities.BaseEntity;
import org.jsf2jps.utils.EJBUtils;
import org.jsf2jps.utils.NamingConstants;

/**
* Class defines base EJB functions. This fucntions include several methods to find entity,
* to persist, remove and merge. This class uses JPA 2.0 Criteria API to make queries.
*
* T - type of the entity
* A - type of the entity attributes
*
* <br/>$LastChangedRevision: 59 $
* <br/>$LastChangedDate: 2011-05-04 19:15:03 +0400 (Wed, 04 May 2011) $
*
* @author ASementsov
*/
public abstract class AbstractFacade<T extends BaseEntity, A extends AbstractAttribute> implements Serializable
{
    private static final long   serialVersionUID = 1L;
    private static final String REV_NUMBER = "$Revision: 59 $";

    /**
     * Function retrieves entity manager
     * @return entity manager
     */
    protected abstract EntityManager getEntityManager();
    protected abstract UserTransaction getUserTransaction();

    /**
     * Entity class
     */
    private Class<T> entityClass;
    /**
     * Entity attributes class
     */
    private Class<A> attributesClass;
   
    /**
     * Constructor
     * @param entityClass - entity class
     * @param attributesClass - entity attributes class
     */
    public AbstractFacade(Class<T> entityClass, Class<A> attributesClass)
    {
        this.entityClass = entityClass;
        this.attributesClass = attributesClass;
    }

    /**
     * Function used to add order by to criteria query
     * @param cq - criteria query object
     * @param builder - criteria query builder
     * @param from - root object
     * @param sortField - field used to sort
     * @param descend - flag indicates sort direction
     */
    protected void setOrderBy (CriteriaQuery<T> cq, CriteriaBuilder builder, Root<T> from, String sortField, boolean descend)
    {
        if (sortField != null) {
            sortField = sortField.replace("wrappedObject.", "");
            if (sortField.indexOf('.') != -1 || sortField.indexOf('[') != -1)
                return;

            cq.orderBy(descend ? builder.desc(from.get(sortField)) : builder.asc(from.get(sortField)));
        }
    }

    /**
     * Function used to add predicates to criteria query
     * @param cq - criteria query object
     * @param builder - criteria query builder
     * @param from - root object
     * @param model - entity model
     * @param filters - filters map
     * @param preList - predicate list (in, out)
     */
    @SuppressWarnings("rawtypes")
    protected void addSimpleFilter (CriteriaQuery cq, CriteriaBuilder builder, Path<T> from, EntityType<T> model, Map<String, ?> filters, List<Predicate> preList)
    {
        Pattern numPat = Pattern.compile("<|>|=");
       
        if (filters != null && filters.size() > 0) {
            for (String column : filters.keySet()) {
                Object var = filters.get(column);
                /*
                 * Removes wrapped object definitions
                 */
                column = column.replace("wrappedObject.", "");

                /*
                 * If this is an attribute
                 */
                if (column.indexOf('[') != -1) {
                    try {
                        String attrName = column.substring(column.indexOf('[')+2, column.indexOf(']')-1);
                        AbstractAttribute attr = attributesClass.newInstance();
                        attr.setName(attrName);
                        attr.setStringValue((String)var);
                        addAttributeFilter(cq, builder, from, Arrays.asList(attr), NamingConstants.PARENT, preList);
                    }
                    catch (Exception ex) {
                        throw (new EJBException(ex));
                    }
                   
                    continue;
                }

                if (column.indexOf('.') != -1) {
                    /*
                     * This is not simple query because this field could become a reason for table join
                     */
                    String[] fields = column.split("\\.");
                    /*
                     * This is simple join. This filter allowed only simple joins
                     */
                    if (fields.length == 2) {
                        /*
                         * Get main attribute
                         */
                        Path attr = from.get(fields[0]);
                        /*
                         * If filterable column is id then just get desired object by it's identifier
                         */
                        if (fields[1].equals(NamingConstants.ID)) {
                            try {
                                BigInteger id = BigInteger.valueOf(NumberFormat.getInstance().parse(var.toString()).longValue());
                                Object value = getEntityManager().find(attr.getJavaType(), id);
                                preList.add(builder.equal(from.get(fields[0]), value));
                            }
                            catch (ParseException ex) {
                                Logger.getLogger(AbstractFacade.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        }
                        else {
                            /*
                             * Create subquery to find by attribute
                             */
                            Subquery subquery = cq.subquery(attr.getJavaType());
                            Root fromAttr = subquery.from(attr.getJavaType());
                            subquery.select(fromAttr.get(NamingConstants.ID));

                            if (var instanceof String && ((String)var).contains("%")) {
                                /*
                                 * This is a like query
                                 */
                                subquery.where(builder.like(builder.lower(fromAttr.get(fields[1])), var.toString().toLowerCase()));  
                            }
                            else {
                                /*
                                 * This is strong equals query
                                 */
                                subquery.where(builder.equal(fromAttr.get(fields[1]), var));
                            }

                            /*
                             * Add where to main query
                             */
                            preList.add(builder.in(from.get(fields[0]).get(NamingConstants.ID)).value(subquery));
                        }
                    }
                    else
                        /*
                         * Just skip this filter becauseit's unusable
                         */
                        continue;
                }
                else {
                    Path ptAttr = from.get(column);
                    Class<?> cl = ptAttr.getJavaType();
                    if (cl.equals(String.class)) {
                        if (var instanceof String && ((String)var).contains("%")) {
                            Expression<String> literal = from.get(column);
                            preList.add(builder.like(builder.lower(literal), var.toString().toLowerCase()));
                        }
                    }
                    else if (Number.class.isAssignableFrom(cl)) {
                        Number numVar = 0;

                        if (var instanceof String) {
                            try {
                                String vs = ((String)var).replaceAll(numPat.pattern(), "");
                                if (!vs.isEmpty())
                                    numVar = NumberFormat.getNumberInstance().parse(vs);
                                else
                                    continue;
                            }
                            catch (ParseException ex) {
                                Logger.getLogger(AbstractFacade.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        }
                        else if (var instanceof Number) {
                            numVar = (Number)var;
                        }

                        Matcher matcher = numPat.matcher((String)var);
                        if (matcher.find()) {
                            switch (matcher.group().charAt(0)) {
                                case '<':
                                    preList.add(builder.le(ptAttr, numVar));
                                    break;

                                case '>':
                                    preList.add(builder.ge(ptAttr, numVar));
                                    break;

                                case '=':
                                    preList.add(builder.equal(ptAttr, numVar));
                                    break;
                            }
                        }
                        else {
                            preList.add(builder.equal(from.get(column), numVar));
                        }
                    }
                    else {
                        preList.add(builder.equal(from.get(column), var));
                    }
                }
            }
        }
    }

    /**
     * Function used to add predicates to criteria query
     * @param cq - criteria query
     * @param builder - criteria query builder
     * @param from - root object
     * @param filters - filter map
     * @param preList - predicate list (in, out)
     */
    @SuppressWarnings("rawtypes")
    protected void addSimpleFilter (CriteriaQuery cq, CriteriaBuilder builder, Root<T> from, Map<String, ?> filters, List<Predicate> preList)
    {
        addSimpleFilter(cq, builder, from, from.getModel(), filters, preList);
    }

    /**
     * Function used to add subquery filter by entity attributes
     * @param cq - criteria query
     * @param builder - criteria query builder
     * @param from - root object
     * @param attributesFilter - attributes for filter
     * @param joinColumn - column of base object in attribute object
     * @param preList  - predicates list
     */
    protected void addAttributeFilter (CriteriaQuery cq,
                                       CriteriaBuilder builder,
                                       Path<T> from,
                                       List attributesFilter,
                                       String joinColumn,
                                       List<Predicate> preList)
    {
        Subquery<A> subquery = cq.subquery(attributesClass);
        Root fromAttr = subquery.from(attributesClass);

        subquery.select(fromAttr.get(joinColumn).get(NamingConstants.ID));
        subquery.groupBy(fromAttr.get(joinColumn).get(NamingConstants.ID));
        subquery.having(builder.equal(builder.count(fromAttr.get(joinColumn).get(NamingConstants.ID)), attributesFilter.size()));

        Predicate wherePredicates = null;

        for (Object a : attributesFilter) {
            if (!(a instanceof AbstractAttribute))
                continue;

            AbstractAttribute attr = (AbstractAttribute)a;
            Predicate pp = null;
            switch (attr.getDataType()) {
                case STRING:
                    pp = builder.and (
                            builder.equal(fromAttr.get(NamingConstants.NAME), attr.getName()),
                            builder.equal(
                                    fromAttr.get(NamingConstants.STRING_VALUE),
                                    attr.getStringValue())
                        );
                    break;
                   
                case NUMBER:
                    pp = builder.and (
                            builder.equal(fromAttr.get(NamingConstants.NAME), attr.getName()),
                            builder.equal(
                                    fromAttr.get(NamingConstants.NUMBER_VALUE),
                                    attr.getNumberValue())
                        );
                    break;
                   
                case DATE:
                    pp = builder.and (
                            builder.equal(fromAttr.get(NamingConstants.NAME), attr.getName()),
                            builder.equal(
                                    fromAttr.get(NamingConstants.DATE_VALUE),
                                    attr.getDateValue())
                        );
                    break;
            }
           
            if (pp == null)
                continue;

            if (wherePredicates == null)
                wherePredicates = pp;
            else
                wherePredicates = builder.or(wherePredicates, pp);
        }
       
        subquery.where(wherePredicates);
        preList.add(builder.in(from.get(NamingConstants.ID)).value(subquery));
    }

    /**
     * Function used to create new entity on <T> type
     * @param entity - entity to create
     */
    public void create(T entity)
    {
        try {
            boolean isOwnTran = EJBUtils.beginTransaction(getUserTransaction());
            getEntityManager().persist(entity);
            EJBUtils.commit(getUserTransaction(), isOwnTran);
        }
        catch (Exception ex) {
            throw (new EJBException(ex));
        }
    }

    /**
     * Function used to merge entity
     * @param entity - entity to merge
     */
    public void merge(T entity)
    {
        try {
            boolean isOwnTran = EJBUtils.beginTransaction(getUserTransaction());
            getEntityManager().merge(entity);
            EJBUtils.commit(getUserTransaction(), isOwnTran);
        }
        catch (Exception ex) {
            throw (new EJBException(ex));
        }
    }

    /**
     * Function removes entity
     * @param entity - entity to remove
     */
    public void remove(T entity)
    {
        try {
            boolean isOwnTran = EJBUtils.beginTransaction(getUserTransaction());
            getEntityManager().remove(getEntityManager().merge(entity));
            EJBUtils.commit(getUserTransaction(), isOwnTran);
        }
        catch (Exception ex) {
            throw (new EJBException(ex));
        }
    }

    /**
     * Function find entity by primary key
     * @param id - primary key object
     * @return found entity
     */
    public T find(Object id)
    {
        return getEntityManager().find(entityClass, id);
    }

    /**
     * Function find all entities
     * @return list of entities
     */
    public List<T> findAll()
    {
        CriteriaQuery<T> cq = getEntityManager().getCriteriaBuilder().createQuery(entityClass);
        cq.select(cq.from(entityClass));
        return getEntityManager().createQuery(cq).getResultList();
    }

    /**
     * Function retrieves list of entities from database. It uses sorting, filtering and paging to do this.
     * @param first - first row number
     * @param pageSize - page size
     * @param sortField - field to sort by
     * @param descend - flag indicates sort direction
     * @param filters - map of filters
     * @return list of entities
     */
    @SuppressWarnings("unchecked")
    public List<T> findFilteredRange(int first, int pageSize, String sortField, boolean descend, Map<String, Object> filters)
    {
        CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<T> cq = builder.createQuery(entityClass);
        Root<T> from = cq.from(entityClass);
        cq.select(from);

        if (filters != null && !filters.isEmpty()) {
            List<Predicate> predicates = new ArrayList<Predicate>();
            addSimpleFilter (cq, builder, from, filters, predicates);
            cq.where(predicates.toArray(new Predicate[predicates.size()]));
        }

        if (sortField != null && !sortField.isEmpty())
            setOrderBy(cq, builder, from, sortField, descend);

        Query q = getEntityManager().createQuery(cq);
        q.setMaxResults(pageSize);
        q.setFirstResult(first);
        return q.getResultList();
    }

    /**
     * Function retrieves count of rows for entities. Result set was pre-filtered using map of filters
     * @param filters - map of filters
     * @return count rows
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public int countFiltered(Map<String, ?> filters)
    {
        CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
        CriteriaQuery cq = builder.createQuery();
        Root<T> from = cq.from(entityClass);
        cq.select(builder.count(from));

        if (filters != null && !filters.isEmpty()) {
            List<Predicate> predicates = new ArrayList<Predicate>();
            addSimpleFilter (cq, builder, from, filters, predicates);
            cq.where(predicates.toArray(new Predicate[predicates.size()]));
        }

        Query q = getEntityManager().createQuery(cq);
        return ((Long) q.getSingleResult()).intValue();
    }

    /**
     * Function retrieves list of entities without any filtering and storing
     * @param first - first row number
     * @param pageSize - page size
     * @return list of entities
     */
    public List<T> findRange(int first, int pageSize)
    {
        return findFilteredRange(first, pageSize, null, false, null);
    }

    /**
     * Function retrieves count of entities without any filtering
     * @return
     */
    public int count()
    {
        return countFiltered(null);
    }

    /**
     * Refresh entity. Reload it from database and overwrite all changes made on it if exists
     * @param entity - entity to refresh
     * @return refresed entity
     */
    public T refresh (T entity)
    {
        getEntityManager().refresh(entity);
        return entity;
    }
   
    /**
     * Function find range of entities filtered with two dimensions:
     * <ul>
     * <li>Simple filtration use embedded attributes
     * <li>Extended filtration use values of external attributes
     * </ul>
     * For extended filtration it uses subquery, e.g. if it need to find all equipment
     * with external attribute STATE=AVAILABLE and attribute PERFORMANCE_RATE=100 select
     * statement for Oracle will seems as follow
     * <code>
     * select *
     * from eq
     * where eq_id in (
     *              select e.id
     *              from eq_attr a, eq e
     *              where (
     *                      (a.name = 'STATE' and value = 'AVAILABLE')
     *                      or
     *                      (a.name = 'PERFORMANCE_RATE' and value = 100)
     *                     )
     *                     and a.eq_id = e.id
     *              group by e.id
     *              having count(e.id) = 2)
     * </code>
     * @param first - first row number
     * @param pageSize - page size
     * @param sortField - field to sort with
     * @param descend - filter direction
     * @param filters - simple filters list
     * @param attributesFilter - attributes metadata with names and values which will be used to make additional filter
     * @param joinColumn - column used to join between attribute and container class
     * @return list of selected entities
     */
    public List<T> findExtendedFilteredRange (int first, int pageSize,
                                          String sortField, boolean descend,
                                          Map<String, Object> filters,
                                          List attributesFilter,
                                          String joinColumn)
    {
        CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();

        CriteriaQuery<T> cq = builder.createQuery(entityClass);
        Root<T> fromEntity = cq.from(entityClass);
        cq.select(fromEntity);
       
        List<Predicate> predicates = new ArrayList<Predicate>();
        /*
        * Add Subquery predicate
        */
        addAttributeFilter(cq, builder, fromEntity, attributesFilter, joinColumn, predicates);

        if (filters != null && !filters.isEmpty()) {
            /*
            * Add other filters
            */
            addSimpleFilter (cq, builder, fromEntity, filters, predicates);
        }

        /*
         * Set where clause for query
         */
        cq.where(predicates.toArray(new Predicate[predicates.size()]));
        
        /*
         * Set sorting clause
         */
        if (sortField != null && !sortField.isEmpty())
            setOrderBy(cq, builder, fromEntity, sortField, descend);

        Query q = getEntityManager().createQuery(cq);
        q.setMaxResults(pageSize);
        q.setFirstResult(first);
        return q.getResultList();
    }

    /**
     * Calculates count of the entities. Functionality like as findExtendedFilteredRange fucntion
     * @param filters - simple filter
     * @param attributesFilter - extendsed filter by external attributes
     * @param joinColumn - column used to join between attribute and container class
     * @return count of filtered entities
     */
    public int countExtendedFiltered(Map<String, Object> filters, List attributesFilter, String joinColumn)
    {
        CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
        CriteriaQuery cq = builder.createQuery();
        Root<T> from = cq.from(entityClass);
        cq.select(builder.count(from));

        /*
        * Add Subquery predicate
        */
        List<Predicate> predicates = new ArrayList<Predicate>();
        addAttributeFilter(cq, builder, from, attributesFilter, joinColumn, predicates);
       
        /*
         * Set where clause for query
         */
        if (filters != null && !filters.isEmpty()) {
            addSimpleFilter (cq, builder, from, filters, predicates);
        }
       
        cq.where(predicates.toArray(new Predicate[predicates.size()]));

        Query q = getEntityManager().createQuery(cq);
        return ((Long) q.getSingleResult()).intValue();
    }

    /**
     * get children entities by parent entity
     * @param parent - parent entity
     * @return children
     */
    public List<T> findChildren(T parent)
    {
        CriteriaQuery<T> cq = getEntityManager().getCriteriaBuilder().createQuery(entityClass);
        Root<T> from = cq.from(entityClass);
        cq.select(from);
        cq.where(getEntityManager().getCriteriaBuilder().equal(from.get(NamingConstants.PARENT), parent));
        return getEntityManager().createQuery(cq).getResultList();
    }
   
    /**
     * Retrieve count children
     * @param parent- parent entity
     * @return children count
     */
    public int countChildren(T parent)
    {
        CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery(entityClass);
        Root<T> from = cq.from(entityClass);
        cq.select(getEntityManager().getCriteriaBuilder().count(from));
        cq.where(getEntityManager().getCriteriaBuilder().equal(from.get(NamingConstants.PARENT), parent));
        Query q = getEntityManager().createQuery(cq);
        return ((Long) q.getSingleResult()).intValue();
    }
   
    public boolean beginTransaction () throws SystemException, NotSupportedException
    {
        return EJBUtils.beginTransaction(getUserTransaction());
    }

    public void commitTransaction (boolean flag) throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException
    {
        EJBUtils.commit(getUserTransaction(), flag);
    }
}
TOP

Related Classes of org.jsf2jpa.ejbs.AbstractFacade

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.