Package com.adaptrex.core.persistence.jpa

Source Code of com.adaptrex.core.persistence.jpa.JPAStoreData

/*
* Copyright 2012 Adaptrex, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.adaptrex.core.persistence.jpa;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.adaptrex.core.ext.ExtConfig;
import com.adaptrex.core.ext.ExtTypeFormatter;
import com.adaptrex.core.ext.Filter;
import com.adaptrex.core.ext.ModelInstance;
import com.adaptrex.core.ext.Sorter;
import com.adaptrex.core.persistence.api.AdaptrexEntityType;
import com.adaptrex.core.persistence.api.BaseStoreData;
import com.adaptrex.core.persistence.api.AdaptrexStoreData;

public class JPAStoreData extends BaseStoreData implements AdaptrexStoreData {

  private ExtConfig extConfig;
 
  private static Logger log = Logger.getLogger(JPAStoreData.class);
 
  public JPAStoreData(ExtConfig extConfig) {
    this.extConfig = extConfig;
  }
 
  @SuppressWarnings("unchecked")
  @Override
  public List<Map<String, Object>> getData() throws Exception {
    List<Map<String, Object>> data = new ArrayList<Map<String, Object>>();
   
    Class<?> clazz = extConfig.getEntityClass();
    JPAPersistenceManager jpa = (JPAPersistenceManager) extConfig.getORMPersistenceManager();
    EntityManager em = jpa.getEntityManager();
    AdaptrexEntityType adaptrexEntity = jpa.getAdaptrexEntity(clazz.getSimpleName());
   
    /*
     * If we have a where clause, fall back to JPQL
     */
    if (this.getWhere() != null && !this.getWhere().isEmpty()) {
      return getWhereFallback(jpa, em, clazz, data);
    }
   
   
    try {
      /*
       * Get the criteria builder
       */
      CriteriaBuilder builder = em.getCriteriaBuilder();

      /*
       * Create the CriteriaQuery and it's Root
       */
      CriteriaQuery<?> query = builder.createQuery(clazz);
      Root<?> root = query.from(clazz);
     
      /*
       * Create the list of predicates to hold our individual filters
       */
      List<Predicate> predicates = new ArrayList<Predicate>();
      for (Filter filter : this.getFilters()) {
       
        /*
         * Get the value of the filter
         */
        Object val = filter.getValue();
        if (val == null || String.valueOf(val).isEmpty()) continue;

        /*
         * Get the property to filter
         */
        String key = filter.getProperty();
        String propertyName = key;
        String extFieldType = null;
        Path<String> currentNode = (Path<String>) root;
        if (key.contains(".")) {
          propertyName = StringUtils.substringAfterLast(key, ".");
          key = StringUtils.substringBeforeLast(key, ".");
         
          String[] propParts = StringUtils.split(key, ".");
          for (String propPart : propParts) {
            currentNode = currentNode.get(propPart);
          }
         
          extFieldType = jpa.getAdaptrexEntity(currentNode.getJavaType().getSimpleName())
              .getField(propertyName).getFieldDefinition().getType();
        } else {
          extFieldType = adaptrexEntity.getField(propertyName).getFieldDefinition().getType();
        }
       
       
        /*
         * Get an expression representing the entity property we're filtering
         */
        Expression<?> path = null;
        try {
          path = currentNode.get(propertyName);
        } catch (Exception e) {
          log.warn("Adaptrex Store Error: Filter: Couldn't find property for " + filter.getProperty());
          continue;
        }
       
       
        /*
         * String filter
         *
         * Should be consistent with ExtJS filtering
         * By default, the settings are:  exactMatch:false, caseSensitive:false, anyMatch:false
         */
        if (extFieldType.equals(ExtTypeFormatter.STRING)) {
          String stringVal = String.valueOf(val);
         
          /*
           * If we're not doing a case sensitive filter, normalize both
           * the path and the value we're comparing
           */
          if (!filter.getCaseSensitive()) {
            path = builder.lower((Expression<String>) path);
            val = stringVal.toLowerCase()
          }
         
          /*
           * If we've got an "or" get the parts
           */
          String[] stringOrParts = stringVal.contains("||")
              ? StringUtils.split(stringVal, "||")
              : null;
         
          /*
           * If exactMatch = true, we need to check for absolute equality
           */
          if (filter.getExactMatch()) {
            if (stringOrParts != null) {
              List<Predicate> orParts = new ArrayList<Predicate>();
              for (String orPart : stringOrParts) {
                orParts.add(builder.equal(path, orPart));
              }
              predicates.add(builder.or(orParts.toArray(new Predicate[orParts.size()])));
            } else {
              predicates.add(builder.equal(path, stringVal));
            }
           
           
          /*
           * If anyMatch = true, check for a match anywhere in the string
           */
          } else if (filter.getAnyMatch()) {
            if (stringOrParts != null) {
              List<Predicate> orParts = new ArrayList<Predicate>();
              for (String orPart : stringOrParts) {
                orParts.add(builder.like((Expression<String>) path, "%" + orPart + "%"));
              }
              predicates.add(builder.or(orParts.toArray(new Predicate[orParts.size()])));
            } else {
              predicates.add(builder.like((Expression<String>) path, "%" + stringVal + "%"));
            }
           
          /*
           * Otherwise match just the beginning of the string
           */
          } else {
            if (stringOrParts != null) {
              List<Predicate> orParts = new ArrayList<Predicate>();
              for (String orPart : stringOrParts) {
                orParts.add(builder.like((Expression<String>) path, orPart + "%"));
              }
              predicates.add(builder.or(orParts.toArray(new Predicate[orParts.size()])));
            } else {
              predicates.add(builder.like((Expression<String>) path, stringVal + "%"));
            }
           
           
          }
         
        /*
         * Boolean filter
         */
        } else if (extFieldType.equals(ExtTypeFormatter.BOOLEAN)) {
          predicates.add(builder.equal(path, String.valueOf(val).toLowerCase().equals("true")));
         
         
        /*
         * Integer filter
         *
         * Inline stores allow flexibility not directly available in Ext.  Since we
         * control the component tags, we can test for various additional conditions.
         * Since this is server side only, local filtering should not also be applied
         */
        } else if (extFieldType.equals(ExtTypeFormatter.INT)) {
          Expression<Integer> numPath = (Expression<Integer>) path;
          Integer numVal = null;
          try {
            numVal = (val instanceof Integer)
                ? (Integer) val
                : Integer.valueOf(String.valueOf(val));
          } catch (Exception e) {
            log.warn("Adaptrex Store Error: Filter: Couldn't parse integer: " + val);
            continue;
          }
         
          /*
           * Filter based on various conditions
           */
          Integer t = filter.getFilterType();
          if (t == Filter.EQUAL) {
            predicates.add(builder.equal(numPath, numVal));
          } else if (t == Filter.NOT_EQUAL) {
            predicates.add(builder.notEqual(numPath, numVal));
          } else if (t == Filter.LESS_THAN) {
            predicates.add(builder.lt(numPath, numVal));
          } else if (t == Filter.LESS_THAN_EQUAL) {
            predicates.add(builder.le(numPath, numVal));
          } else if (t == Filter.GREATER_THAN) {
            predicates.add(builder.gt(numPath, numVal));
          } else if (t == Filter.GREATER_THAN_EQUAL) {
            predicates.add(builder.ge(numPath, numVal));
          }
         
        /*
         * Float filter
         *
         * Similar to integer filter in that we can test for additional conditions
         * not avaialable in standard Ext filtering.  Do not rely on these filters
         * to work as local filters.  Also, since we can't currently test for float
         * equality, we should only be checking for less than or greater than at
         * this time.
         *
         * In the future, we may add additional filter settings which would allow
         * us to test for float equality up to a certain precision.
         */
        } else if (extFieldType.equals(ExtTypeFormatter.FLOAT)) {
          Expression<Double> numPath = (Expression<Double>) path;
          Double numVal = null;
          try {
            numVal = (val instanceof Double)
                ? (Double) val
                : Double.valueOf(String.valueOf(val));
          } catch (Exception e) {
            log.warn("Adaptrex Store Error: Filter: Couldn't parse float: " + val);
            continue;
          }
         
          Integer t = filter.getFilterType();
          if (t == Filter.EQUAL) {
            log.warn("Adaptrex Store Error: Filter: Float equality not implemented: " + val);
            continue;
          } else if (t == Filter.NOT_EQUAL) {
            log.warn("Adaptrex Store Error: Filter: Float inequality not implemented: " + val);
            continue;
          } else if (t == Filter.LESS_THAN || t == Filter.LESS_THAN_EQUAL) {
            predicates.add(builder.le(numPath, numVal));
          } else if (t == Filter.GREATER_THAN || t == Filter.GREATER_THAN_EQUAL) {
            predicates.add(builder.ge(numPath, numVal));
          }         
         
        /*
         * Date filter
         *
         * Also simililar to integer.  We can test for several date comparison
         * conditions not available in standard Ext filtering.
         */
        } else if (extFieldType.equals(ExtTypeFormatter.DATE)) {
          Expression<Date> datePath = (Expression<Date>) path;
          Date dateVal = (Date) ExtTypeFormatter.parse(val, Date.class);
                   
          Integer t = filter.getFilterType();
          if (t == Filter.EQUAL) {
            predicates.add(builder.equal(datePath, dateVal));
          } else if (t == Filter.NOT_EQUAL) {
            predicates.add(builder.notEqual(datePath, dateVal));
          } else if (t == Filter.LESS_THAN) {
            predicates.add(builder.lessThan(datePath, dateVal));
          } else if (t == Filter.LESS_THAN_EQUAL) {
            predicates.add(builder.lessThanOrEqualTo(datePath, dateVal));
          } else if (t == Filter.GREATER_THAN) {
            predicates.add(builder.greaterThan(datePath, dateVal));
          } else if (t == Filter.GREATER_THAN_EQUAL) {
            predicates.add(builder.greaterThanOrEqualTo(datePath, dateVal));
          }
        }
      }
     
      /*
       * Package all the filters into a final predicate
       */
      Predicate finalPredicate = builder.and((Predicate[]) predicates.toArray((new Predicate[predicates.size()])));
      query.where(finalPredicate);
     
      /*
       * Add sorters to the query
       */
      List<String> spentSort = new ArrayList<String>();
      List<Order> order = new ArrayList<Order>();
      this.getGroupers().addAll(this.getSorters());
      for (Sorter sorter : this.getGroupers()) {
        String prop = sorter.getProperty();
        if (spentSort.contains(prop)) continue;
        spentSort.add(prop);
       
        String[] propParts = StringUtils.split(prop, ".");
        Path<String> currentNode = (Path<String>) root;
        for (String propPart : propParts) {
          currentNode = currentNode.get(propPart);
        }
       
        String d = sorter.getDirection();
        boolean isDesc = d != null && d.toLowerCase().equals("desc");
        order.add(isDesc ? builder.desc(currentNode) : builder.asc(currentNode));
      }
      query.orderBy(order);

     
      /*
       * Create a typed query from the criteria query, set start/limit and process results
       */
      TypedQuery<?> typedQuery = em.createQuery(query);
      if (this.getLimit() != null) typedQuery.setMaxResults(this.getLimit());
      if (this.getStart() != null) typedQuery.setFirstResult(this.getStart());
      for (Object entity : typedQuery.getResultList()) {
        data.add(new ModelInstance(extConfig, entity).getData());
      }

     
      /*
       * Create a CriteriaQuery used to get the total count of records in the resultset
       */
      CriteriaQuery<Long> queryCount = builder.createQuery(Long.class);
      queryCount.select(builder.count(queryCount.from(clazz)));
      queryCount.where(finalPredicate);
      this.setTotalCount(em.createQuery(queryCount).getSingleResult().longValue());
     
    } catch (Exception e) {
      throw new Exception(e);
    } finally {
      em.close();
    }
   
    return data;
  }
 
 
  private List<Map<String, Object>> getWhereFallback(
      JPAPersistenceManager jpa, EntityManager em, Class<?> clazz, List<Map<String, Object>> data) throws Exception {

    String className = clazz.getSimpleName();   
    String alias = className.toLowerCase().substring(0, 1);
   
    /*
     * Start the JPQL
     */
    StringBuffer jpql = new StringBuffer();
    jpql.append("SELECT " + alias + " FROM " + className + " " + alias + " ");
    jpql.append("WHERE " + this.getWhere());
   
    StringBuffer jpqlCount = new StringBuffer();
    jpqlCount.append("SELECT COUNT (" + alias + ") FROM " + className + " " + alias + " ");
    jpqlCount.append("WHERE " + this.getWhere());

   
   
    /*
     * ExtJS sends sorting parameters along with grouping parameters
     * for the same grouper.  Unsure if this behavior makes sense or
     * may change in the future.  Instead, we should filter out the
     * duplicate sort.
     */
    List<String> spentSort = new ArrayList<String>();
    String sortClause = "";
    this.getGroupers().addAll(this.getSorters());
    if (this.getGroupers().size() > 0) {
      for (Sorter sorter : this.getGroupers()) {
        String prop = sorter.getProperty();
        if (spentSort.contains(prop)) continue;
        spentSort.add(prop);
        if (!sortClause.isEmpty()) sortClause += ",";
        sortClause += alias + "." + prop + " " + sorter.getDirection();
      }
    }
   
    if (!sortClause.isEmpty()) {
      jpql.append(" ORDER BY " + sortClause);
    }
   
    /*
     * Create the JPQL typed query
     */
    Query query = em.createQuery(jpql.toString(), clazz);
    Query queryCount = em.createQuery(jpqlCount.toString(), Long.class);
   
   
    /*
     * Add any named parameters
     */
    Map<String,Object> params = this.getParams();
    for (String key : params.keySet()) {
      Class<?> paramType = query.getParameter(key).getParameterType();
      Object paramObj = ExtTypeFormatter.parse(params.get(key), paramType);
      query.setParameter(key, paramObj);
      queryCount.setParameter(key, paramObj);
    }   
   
    if (this.getLimit() != null) query.setMaxResults(this.getLimit());
    if (this.getStart() != null) query.setFirstResult(this.getStart().intValue());
    for (Object entity : query.getResultList()) {
      data.add(new ModelInstance(extConfig, entity).getData());
    }
   
    /*
     * Get the initial list before applying limits
     */
    this.setTotalCount((Long) queryCount.getSingleResult());
    return data;
  }
}
TOP

Related Classes of com.adaptrex.core.persistence.jpa.JPAStoreData

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.