Package com.adaptrex.core.persistence.jpa

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

/*
* 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 com.adaptrex.core.ext.data.DataConfig;
import com.adaptrex.core.ext.data.Filter;
import com.adaptrex.core.ext.data.Sorter;
import com.adaptrex.core.ext.data.Store;
import com.adaptrex.core.persistence.AdaptrexPersistence;
import com.adaptrex.core.persistence.AdaptrexSession;
import com.adaptrex.core.utilities.StringUtilities;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;

import javax.persistence.Cache;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.Metamodel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JpaPersistence implements AdaptrexPersistence {

    private EntityManagerFactory factory;
   
    private Map<String, Class<?>> entityClassCache = new HashMap<String, Class<?>>();
    private Map<String, List<String>> memberAnnotationCache = new HashMap<String, List<String>>();
    private Map<String, String> fieldTypeCache = new HashMap<String, String>();
   
    private Logger log = LoggerFactory.getLogger(JpaPersistence.class);
   
    private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
   
    public JpaPersistence(EntityManagerFactory factory) {
  this.factory = factory;
    }

    @Override
    public Class<?> getEntityClass(String className) {
  try {
      if (entityClassCache.containsKey(className)) {
    return entityClassCache.get(className);
      }

      EntityManager em = factory.createEntityManager();
      Metamodel meta = em.getMetamodel();

      for (EntityType<?> et : meta.getEntities()) {
    if (et.getJavaType().getName().endsWith("." + className)) {
        Class<?> clazz = et.getJavaType();
        entityClassCache.put(className, clazz);
        return clazz;
    }
      }
  } catch (Exception e) {
      log.warn("Error", e);
  }
  return null;
    }
   
   
    /* *******************************************************************
     *
     * Get Single Entity
     *
     */
    @Override
    public Object getEntity(AdaptrexSession session, String entityName, Integer id) {
  EntityManager em = (EntityManager) session.getPersistence().getNativeSession();
  return em.find(getEntityClass(entityName), id);
    }

    @Override
    public Object getEntity(AdaptrexSession session, Class<?> clazz, Integer id) {
  EntityManager em = (EntityManager) session.getPersistence().getNativeSession();
  return em.find(clazz, id);
    }

   
    /* *******************************************************************
     *
     * Save Single Entity
     *
     */
    @Override
    public Object saveEntity(AdaptrexSession session, Object entity) {
    EntityManager em = (EntityManager) session.getPersistence().getNativeSession();
    Integer id = null;
    try {
      Method idGetter = entity.getClass().getDeclaredMethod("getId");
      id = (Integer) idGetter.invoke(entity);
    } catch (Exception e) {
      log.debug("Couldn't find id for entity");
      return entity;
    }
   
    EntityTransaction tx = em.getTransaction();
    tx.begin();
   
    if (id != null && em.find(entity.getClass(), id) != null) {
      log.debug("Merging entity: " + entity.getClass().getSimpleName());
      entity = em.merge(entity);
    } else {
      em.persist(entity);
      log.debug("Persisting entity: " + entity.getClass().getSimpleName());
    }
   
    /*
     * Evict related objects from cache
     */
    try {
      evictAssociated(em, entity);
    } catch (Exception e) {
      log.warn("Error: ", e);
    }
   
    tx.commit();
    return entity;
    }

 
  private void evictAssociated(EntityManager em, Object entity) {
    Class<?> clazz = entity.getClass();
    for (Field field : clazz.getDeclaredFields()) {
     
      /*
       * Evict ManyToOne
       */
      if (isManyToOne(clazz, field.getName())) {
        try {
          Method getter = clazz.getDeclaredMethod("get" + StringUtilities.capitalize(field.getName()));
          Object associated = getter.invoke(entity);
          if (associated == null) {continue;}
          Class<?> associatedClazz = associated.getClass();
          Method idGetter = associatedClazz.getDeclaredMethod("getId");
          EntityManagerFactory emf = em.getEntityManagerFactory();
          Cache emfCache = emf.getCache();
          emfCache.evict(associated.getClass(), idGetter.invoke(associated));
        } catch (Exception e) {
          log.warn("Error", e);
        }
      }
     
      /*
       * Evict OneToMany
       * TODO: Test This
       */
      if (isOneToMany(clazz, field.getName())) {
        try {
          Method getter = clazz.getDeclaredMethod("get" + StringUtilities.capitalize(field.getName()));
          Set<?> associatedSet = (Set<?>) getter.invoke(entity);
         
          for (Object associated : associatedSet) {
            Class<?> associatedClazz = associated.getClass();
            Method idGetter = associatedClazz.getDeclaredMethod("getId");
            em.getEntityManagerFactory().getCache().evict(associated.getClass(), idGetter.invoke(associated));
          }
        } catch (Exception e) {
          log.warn("Error", e);
        }
      }     
    }
  }
 



    /* *******************************************************************
     *
     * Delete Single Entity
     *
     */
    @Override
    public Object deleteEntity(AdaptrexSession session, Object entity) {
  EntityManager em = (EntityManager) session.getPersistence().getNativeSession();
  EntityTransaction tx = em.getTransaction();
  tx.begin();
  em.remove(em.merge(entity));
 
  /*
   * Evict related objects from cache
   */
  evictAssociated(em, entity);
 
  tx.commit();
  return entity;
    }

    /* *******************************************************************
     *
     * Entity List
     *
     */
    @SuppressWarnings("unchecked")
    @Override
    public List<Object> getEntityList(AdaptrexSession session, Store store) {
  DataConfig config = store.getConfig();
  List<Object> list = new ArrayList<Object>();

  try {
      EntityManager em = (EntityManager) session.getPersistence().getNativeSession();

      Map<String, Object> parameters = new HashMap<String, Object>(config.getParams());

     
      Class<?> clazz = config.getClazz();
      String className = clazz.getSimpleName();
      String alias = className.toLowerCase().substring(0, 1);

      /*
       * Core JPQL
       */
      String jpql = "SELECT " + alias + " FROM " + className + " " + alias;

      /*
       * Where
       */
      String where = "";
      if (config.getWhere() != null) {
    where += config.getWhere();
      }

      /*
       * Filter
       */
      for (Filter filter : config.getFilters()) {
      if (!where.isEmpty()) {
        where += " AND ";
      }

      String val = String.valueOf(filter.getValue());
      String prop = filter.getProperty();
      String param = prop.replace(".", "_");

      /*
       * Handle between inclusive
       */
      if (val.contains(">=<")) {
        String[] filterParts = val.split(">=<");

        where += "(" + alias + "." + prop + " BETWEEN :"
          + param + "_low AND :" + param + "_high)"
          + "";
        parameters.put(param + "_low", filterParts[0]);
        parameters.put(param + "_high", filterParts[1]);

      /*
       * Handle "OR"
       */
      } else if (val.contains("||")) {
        String whereOr = "";
        int i = 1;

        for (String orPart : val.split("\\|\\|")) {
        whereOr += whereOr.isEmpty() ? "(" : " OR ";
        whereOr += alias + "." + prop + " = :" + param + i;
        parameters.put(param + i, orPart);
        i++;
        }
        whereOr += ")";
        where += whereOr;

      } else {
        String eqType = val.contains("%") ? " like " : " = ";
        if (eqType.contains("like")) {
          where += "lower(" + alias + "." + prop + ")" + eqType + ":" + param;

        } else {
          where += alias + "." + prop + eqType + ":" + param;
        }

        parameters.put(param, val);
      }
      }


      /*
       * Add where clause
       */
      if (!where.isEmpty()) {
    jpql += " WHERE " + where;
      }


      if (config.getSorters().size() > 0) {
    String sortClause = "";
    for (Sorter sorter : config.getSorters()) {
        if (!sortClause.isEmpty()) {
      sortClause += ",";
        }
        sortClause += alias + "." + sorter.getProperty() + " " + sorter.getDirection();
    }
    jpql += " ORDER BY " + sortClause;
      }

      log.debug("JPQL: " + jpql);
      Query q = em.createQuery(jpql, clazz);

      /*
       * Add params
       * TODO: This is tedius... we want to automatically determine the
       * type for the field we're trying to set the parameter for
       */
      for (String key : parameters.keySet()) {
    Object paramObj = parameters.get(key);

    Class<?> paramType = q.getParameter(key).getParameterType();
    String typeName = paramType.getSimpleName().toLowerCase();

    try {
        if (typeName.equals("boolean")) {
      String stringVal = (String) paramObj;
      paramObj = stringVal.equals("1") || stringVal.equals("true") || stringVal.equals("Y");

        } else if (typeName.equals("date")) {
      paramObj = dateFormat.parse((String) paramObj);

        } else {
      Method m = paramType.getDeclaredMethod("valueOf", String.class);
      paramObj = m.invoke(null, paramObj);
        }
    } catch (Exception e) {
    }

    log.debug("  Param: " + key + " = " + paramObj);
    q.setParameter(key, paramObj);
      }

      store.setTotalCount(q.getResultList().size());

      if (config.getPageSize() != null) {
    q.setMaxResults(config.getPageSize());
      }
      if (config.getStart() != null) {
    q.setFirstResult(config.getStart());
      }
      list = q.getResultList();
  } catch (Exception e) {
      log.warn("Error", e);
  }

  return list;
    }

    /* ************************************************************************************
     *
     * Everything below this is experimental.  Possibly moved under a separate interface
     *
     */
    @Override
    public boolean isIdField(Class<?> clazz, String fieldName) {
  return hasMemberAnnotation(clazz, fieldName, "Id");
    }

    /*
     * TODO: Do we want to move ManyToMany to a separate method?
     */
    @Override
    public boolean isOneToMany(Class<?> clazz, String fieldName) {
  return hasMemberAnnotation(clazz, fieldName, "OneToMany")
    || hasMemberAnnotation(clazz, fieldName, "ManyToMany");
    }

    @Override
    public boolean isManyToOne(Class<?> clazz, String fieldName) {
  return hasMemberAnnotation(clazz, fieldName, "ManyToOne");
    }

    @Override
    public Object getFieldValue(Object entity, String fieldName) {
  Method getter = null;
  Class<?> clazz = entity.getClass();
  try {
      getter = clazz.getMethod("get" + capitalize(fieldName));
  } catch (Exception e) {
  }
  if (getter == null) {
      try {
    getter = clazz.getMethod("is" + capitalize(fieldName));
      } catch (Exception e) {
      }
  }
  if (getter == null) {
      return null;
  }

  try {
      return getter.invoke(entity);
  } catch (Exception e) {
      log.warn("Error", e);
      return null;
  }
    }

   
   
    @Override
    public String getFieldType(Class<?> clazz, String fieldName) {
  String fieldType = fieldTypeCache.get(clazz.getName() + "." + fieldName);
  if (fieldType != null) return fieldType;
 
  Field field = null;
  try {
      field = clazz.getDeclaredField(fieldName);
  } catch (Exception e) {}
  if (field == null) return null;
  String columnType = field.getType().getSimpleName().toLowerCase();

  if (columnType.equals("integer") || columnType.equals("long")) {
      columnType = "int";
  } else if (columnType.equals("double")) {
      columnType = "float";
  } else if (columnType.equals("time")) {
      columnType = "time";
  } else if (columnType.equals("calendar") || columnType.equals("date")) {
      columnType = "date";
  }
 
  if (columnType.equals("date")) {
      Temporal a = field.getAnnotation(Temporal.class);
      if (a == null) {
    try {
        Method getter = clazz.getMethod("get" + StringUtilities.capitalize(fieldName));
        if (getter != null) {
      a = getter.getAnnotation(Temporal.class);
        }
    } catch (Exception e) {}
      }
      if (a != null && a.value().equals(TemporalType.TIME)) {
    columnType = "time";
      }     
  }
 
  fieldTypeCache.put(clazz.getName() + "." + fieldName, columnType);
  return columnType;
    }
   
   
   
   
   
   
   
   
   
   
    private boolean hasMemberAnnotation(Class<?> clazz, String member, String annotationName) {
  List<String> memberAnnotations = memberAnnotationCache.get(clazz.getName() + "." + member);
  if (memberAnnotations == null) {
      memberAnnotations = getMemberAnnotations(clazz, member);
  }
  return memberAnnotations.contains(annotationName);
    }

   
    /*
     * This gets a list of all annotations on a class field (or it's getter)
     */
    private List<String> getMemberAnnotations(Class<?> clazz, String member) {
  List<String> list = memberAnnotationCache.get(clazz.getName() + "." + member);
  if (list != null) {
      return list;
  }
 
  list = new ArrayList<String>();
  try {
      Field field = clazz.getDeclaredField(member);
      if (field != null) {
    for (Annotation annotation : field.getDeclaredAnnotations()) {
        list.add(annotation.annotationType().getSimpleName());
    }
      }
  } catch (Exception e) {
  }

  try {
      Method getter = clazz.getMethod("get" + StringUtilities.capitalize(member));
      if (getter != null) {
    for (Annotation annotation : getter.getDeclaredAnnotations()) {
        list.add(annotation.annotationType().getSimpleName());
    }
      }
  } catch (Exception e) {
  }

  memberAnnotationCache.put(clazz.getName() + "." + member, list);
  return list;
    }
   
    private static String capitalize(String string) {
  return string.substring(0, 1).toUpperCase() + string.substring(1);
    }

   
    @Override
    public Object getFactory() {
  return this.factory;
    }
   

    @Override
    public Object getNativeSession() {
  return this.factory.createEntityManager();
    }

    @Override
    public void closeNativeSession(Object entityManager) {
  ((EntityManager) entityManager).close();
    }
}
TOP

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

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.