Package com.im.imjutil.dao.jpa

Source Code of com.im.imjutil.dao.jpa.JPADAO

package com.im.imjutil.dao.jpa;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Persistence;
import javax.persistence.TemporalType;

import com.im.imjutil.config.Configurator;
import com.im.imjutil.dao.DAO;
import com.im.imjutil.dao.Transaction;
import com.im.imjutil.exception.JPAException;
import com.im.imjutil.exception.PersistenceException;
import com.im.imjutil.exception.ValidationException;
import com.im.imjutil.logging.Logger;
import com.im.imjutil.query.Hints;
import com.im.imjutil.query.NativeQuery;
import com.im.imjutil.query.Query;
import com.im.imjutil.query.QueryAdapter;
import com.im.imjutil.util.Filter;
import com.im.imjutil.util.Pair;
import com.im.imjutil.validation.Convert;
import com.im.imjutil.validation.Validator;

/**
* Classe que implementa um DAO JAP generico para persistencia basica
* de qualquer modelo JPA que implemente a interface {@link DAO}.
* Configure no arquivo {@code /META-INF/config.property} o nome
* da unidade de persistencia sob a tag: {@code jpa.persistence.unit}
* <br>
* Veja a sintaxe do EJB-QL
* <a href='http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/EJBQL5.html'>
*   AQUI
* </a>
*
* @author Felipe Zappala
*
* @param <T> Tipo do modelo a ser persistido.
*/
public class JPADAO<T> implements DAO<T> {

  /** Mapa com as configuracoes da unidade de persistencia. */
  private Map<String, String> unitProperties;
 
  /** Instancia da classe do tipo especifico do DAO. */
  protected final Class<T> clazz;

  /** Entity Manager Factory padrao do sistema. */
  protected EntityManagerFactory factory;
 
  /** Entity Manager padrao do sistema. */
  protected EntityManager manager;
 
  /** Nome da unidade de persistencia do JPA*/
  public static String PERSISTENCE_UNIT;
 
  /** Mensagem: Erro ao executar operacao. */
  public static final String MSG_ERROR_OPT;
 
  /** Determina se o DAO deve gerenciar automaticamente as transacoes das operacoes. */
  private boolean autoManageTransactions = true;
 
  static {
    PERSISTENCE_UNIT = Configurator.getProperty("jpa.persistence.unit");
    MSG_ERROR_OPT = "Erro ao executar operacao: ";
  }
 
  /**
   * Constroi um DAO genrico passando a instancia da classe do tipo deste.
   *
   * @param clazz Instancia da classe do tipo especifico do DAO.
   */
  public JPADAO(Class<T> clazz) {
    this(clazz, null, null);
  }
 
  /**
   * Constroi um DAO genrico passando a instancia da classe do tipo deste.
   *
   * @param clazz Instancia da classe do tipo especifico do DAO.
   * @param em Instancia do gerenciador de entidades alvo.
   */
  public JPADAO(Class<T> clazz, EntityManager em) {
    this(clazz, em, null);
  }
 
  /**
   * Constroi um DAO genrico passando a instancia da classe do tipo deste.
   *
   * @param clazz Instancia da classe do tipo especifico do DAO.
   * @param unitProperties Propriedades da unidade de persistencia.
   */
  public JPADAO(Class<T> clazz, Map<String, String> unitProperties) {
    this(clazz, null, unitProperties);
  }
 
  /**
   * Constroi um DAO genrico passando a instancia da classe do tipo deste.
   *
   * @param clazz Instancia da classe do tipo especifico do DAO.
   * @param em Instancia do gerenciador de entidades alvo.
   * @param unitProperties Propriedades da unidade de persistencia.
   */
  public JPADAO(Class<T> clazz, EntityManager em,
      Map<String, String> unitProperties) {
   
    if (unitProperties != null) {
      this.unitProperties = unitProperties;
    }
   
    if (clazz == null) {
      throw new ValidationException("Parametro clazz e nulo");
    }
    this.clazz = clazz;
   
    if (em == null) {
      // Inicializa a unidade de persistencia
      initializePersistenceUnit();
     
      // Inicializa o Entity Manager da classe atual.
      this.manager = factory.createEntityManager();
     
    } else {
      // Utiliza o Entity Manager passado
      this.manager = em;
    }
   
  }
 
  @Override
  public T add(T type) throws PersistenceException {
    T merged = type;
    try {
      beginTransaction();
      merged = manager.merge(type);
      manager.flush();
      commitTransaction();
    } catch (Exception e) {
      rollbackTransaction();
      String error = Convert.toString(MSG_ERROR_OPT, "add: ", type);
      Logger.error(error, e);
      throw new JPAException(error, e);
    }
   
    return merged;
  }

  @Override
  public void addAll(List<T> types) throws PersistenceException {
    List<T> mergeds = new ArrayList<T>(types.size());
    try {
      beginTransaction();
      for (T type : types) {
        mergeds.add(manager.merge(type));
      }
      manager.flush();
      commitTransaction();
     
      types.clear();
      types.addAll(mergeds);
    } catch (Exception e) {
      rollbackTransaction();
      String error = Convert.toString(MSG_ERROR_OPT, "addAll");
      Logger.error(error, e);
      throw new JPAException(error, e);
    }
  }

  @Override
  public T find(Filter filter) throws PersistenceException {
    try {
      if (filter == null) {
        throw new ValidationException("O filtro passado e nulo.");
      }
      return this.execute(getQuery(filter), filter);
     
    } catch (Exception e) {
      String error = Convert.toString(MSG_ERROR_OPT, "filter: ", filter);
      Logger.error(error, e);
      throw new JPAException(error, e);
    }
  }

  @Override
  public List<T> findAll(Filter filter) throws PersistenceException {
    try {
      if (filter == null) {
        throw new ValidationException("O filtro passado e nulo.");
      }
      return this.executeAll(getQuery(filter), filter);

    } catch (Exception e) {
      String error = Convert.toString(MSG_ERROR_OPT, "filter: ", filter);
      Logger.error(error, e);
      throw new JPAException(error, e);
    }
  }

  @Override
  public T get(Object id) throws PersistenceException {
    try {
      return manager.find(clazz, id);
    } catch (Exception e) {
      String error = Convert.toString(MSG_ERROR_OPT, "get: ", id);
      Logger.error(error, e);
      throw new JPAException(error, e);
    }
  }

  @Override
  @SuppressWarnings({ "cast", "unchecked" })
  public List<T> getAll() throws PersistenceException {
    try {
      if (hasQueryFindAll()) {
        String sql = Convert.toString(clazz.getSimpleName(),".findAll");
        return (List<T>) manager.createNamedQuery(sql).getResultList()
      }
      return executeAll(new QueryAdapter(Convert.toString(
          "SELECT t FROM ", clazz.getSimpleName(), " AS t"
      )));
    } catch (Exception e) {
      String error = Convert.toString(MSG_ERROR_OPT, "getAll");
      Logger.error(error, e);
      throw new JPAException(error, e);
    }
  }

  @Override
  public void remove(T type) throws PersistenceException {
    try {
      beginTransaction();
      manager.remove(manager.merge(type));
      manager.flush();     
      commitTransaction();
    } catch (Exception e) {
      rollbackTransaction();
      String error = Convert.toString(MSG_ERROR_OPT, "remove: ", type);
      Logger.error(error, e);
      throw new JPAException(error, e);
    }   
  }

  @Override
  public void removeAll(List<T> types) throws PersistenceException {
    try {
      beginTransaction();
      for (T type : types) {
        manager.remove(manager.merge(type));
      }
      manager.flush();     
      commitTransaction();
    } catch (Exception e) {
      rollbackTransaction();
      String error = Convert.toString(MSG_ERROR_OPT, "removeAll");
      Logger.error(error, e);
      throw new JPAException(error, e);
    }
  }

  @Override
  @SuppressWarnings({ "unchecked", "cast" })
  public <R> R execute(Query query, Filter... filters)
      throws PersistenceException {
    try {
      beginTransaction();
     
      // Cria a consulta
      String queryString = query.createQuery(filters);
     
      javax.persistence.Query queryJPA;
      if (query instanceof NativeQuery) {
        NativeQuery nativeQuery = (NativeQuery) query;
        Class<?> target = getTargetClass(nativeQuery);
       
        if (target != null)
          queryJPA = manager.createNativeQuery(queryString, target);
        else
          queryJPA = manager.createNativeQuery(queryString);
      } else {
        queryJPA = manager.createQuery(queryString);
      }
     
      Filter filter = new Filter().addFilters(filters);
     
      // Associa os valores
      for (Pair<String, Object> pair : filter) {
        setQuery(queryJPA, pair);
      }
     
      setQueryHints(queryJPA, query);
     
      // Se for uma consulta de 'delete' ou 'updade',
      // retorna o numero de linhas afetadas.
      // Se for um 'select', retorn o seu objeto resultante.
      if (!queryString.toLowerCase().startsWith("select")) {
        R ret = (R) Integer.valueOf(queryJPA.executeUpdate());
        commitTransaction();
       
        return ret;
      }

      // Caso for uma consulta 'select', obtem o primeiro resultado
      // via 'getResultList' pra evitar o erro 'NonUniqueResultException'
      // configurando o limite dos resultados para obter o primeiro
      queryJPA.setFirstResult(0);
      queryJPA.setMaxResults(1);
     
      List<R> results = (List<R>) queryJPA.getResultList();
      commitTransaction();
     
      if (!Validator.isEmpty(results)) {
        return results.get(0);
      }
      return null;
     
    } catch (Exception e) {
      rollbackTransaction();
      String error = Convert.toString(MSG_ERROR_OPT, "execute");
      Logger.error(error, e);
      throw new JPAException(error, e);
    }
  }

  private Class<?> getTargetClass(NativeQuery nativeQuery) {
    if (nativeQuery.getTarget() != null) {
      if (nativeQuery.getTarget() == Void.class) {
        return null;
      }
      return nativeQuery.getTarget();
    }
    return this.clazz;
  }

  private void setQueryHints(javax.persistence.Query queryJPA, Query query) {
    if (query instanceof Hints) {
      Hints hints = (Hints) query;
      Properties props = hints.getHints();
     
      if (!props.isEmpty()) {
        for (Map.Entry<?, ?> pair : props.entrySet()) {
          queryJPA.setHint((String)pair.getKey(), pair.getValue());
        }
      }
    }
  }

  @Override
  @SuppressWarnings({ "cast", "unchecked" })
  public <R> List<R> executeAll(Query query, Filter... filters)
      throws PersistenceException {
    try {
      beginTransaction();
     
      // Cria a consulta
      String queryString = query.createQuery(filters);
     
      javax.persistence.Query queryJPA;
      if (query instanceof NativeQuery) {
        NativeQuery nativeQuery = (NativeQuery) query;
        Class<?> target = (nativeQuery.getTarget() != null) ? nativeQuery.getTarget() : this.clazz;
        queryJPA = manager.createNativeQuery(queryString, target);
      } else {
        queryJPA = manager.createQuery(queryString);
      }
     
      Filter filter = new Filter().addFilters(filters);
     
      // Associa os valores
      for (Pair<String, Object> pair : filter) {
        setQuery(queryJPA, pair);
      }
      // Configura os limites
      if (filter.isLimited()) {
        queryJPA.setFirstResult(filter.getLimits().getFirst());
        queryJPA.setMaxResults(filter.getLimits().getLast());
      }

      setQueryHints(queryJPA, query);
     
      // Executa a consulta
      List<R> results = (List<R>) queryJPA.getResultList();
      commitTransaction();
     
      return results;
     
    } catch (Exception e) {
      String error = Convert.toString(MSG_ERROR_OPT, "executeAll");
      Logger.error(error, e);
      throw new JPAException(error, e);
    }
  }
 
  private Set<String> getAvailableFields() {
    Set<String> availableFields = new HashSet<String>();
    for (Field field : clazz.getDeclaredFields()) {
      availableFields.add(field.getName());
    }
   
    Class<?> superClass = clazz.getSuperclass();
    while (!superClass.equals(Object.class)) {
      for (Field field : superClass.getDeclaredFields()) {
        availableFields.add(field.getName());
     
      superClass = superClass.getSuperclass();
    }
   
    return availableFields;
  }
 
  /**
   * Obtem a string da consulta do JPA.
   *
   * @param filter Filtros da consulta.
   * @return String da consulta EJB-QL.
   */
  private Query getQuery(Filter filter) {
    String queryString = null;
   
    if (!filter.isQueriable()) {
      // Prepara a consulta
      StringBuilder sql = new StringBuilder();
      sql.append("SELECT o FROM ");
      sql.append(clazz.getSimpleName());
      sql.append(" AS o");
     
      // Obtem os filtros disponiveis
      Set<String> availableFields = getAvailableFields();
     
      // Associa os filtros
      if (!filter.isEmpty()) {
        sql.append(" WHERE ");
       
        for (Pair<String, Object> pair : filter) {
          String key = pair.getFirst().replaceAll("\\!\\%", "");
         
          if (!availableFields.contains(key)) {
            throw new ValidationException(Convert.toString(
                "Campo invalido para filtro da classe ",
                clazz.getSimpleName(), ": ", key));
          }
          sql.append("o.").append(key);
          sql.append((pair.getLast() instanceof String)
              ? " LIKE " : " = " );
          sql.append(":").append(key);
          sql.append(filter.isExclusive() ? " AND " : " OR  ");
        }
        // Remove o ultimo ' AND ' ou ' OR ''
        sql.setLength(sql.length() -5);
      }
      setOrderBy(filter, sql);
      queryString = sql.toString();
    } else {
      // Cria a string da consulta usando a query do filtro.
      queryString = filter.getQuery().createQuery(filter);
    }
    return new QueryAdapter(queryString);
  }
 
  /**
   * Configura a ordenacao na consulta.
   *
   * @param filter Filtro com ordenacao.
   * @param sql Consulta a ser ordenada.
   */
  private void setOrderBy(Filter filter, StringBuilder sql) {
    if (filter.isOrdened()) {
      sql.append(" ORDER BY ");
      for (Pair<String, Boolean> order : filter.getOrders()) {
        sql.append("o.").append(order.getFirst());
        sql.append(order.getLast() ? " ASC, " : " DESC, ");
      }
      sql.setLength(sql.length() -2);
    }
  }

  /**
   * Configura os dados do pair na consulta.
   *
   * @param queryJPA Consulta JPA
   * @param pair Par de dados.
   */
  private void setQuery(javax.persistence.Query queryJPA, Pair<String, Object> pair) {
    String key = pair.getFirst();
    boolean isIntKey = Validator.isNumber(key);
    int ikey = (isIntKey) ? Convert.toInteger(key) : 0;
   
    if (pair.getLast() instanceof String) {
        String value = (String) pair.getLast();
       
        if (key.startsWith("!%") && key.endsWith("!%")) {
          key = key.substring(2, key.length()-2);
         
        } else if (key.startsWith("!%")) {
          key = key.substring(2, key.length());
          value = Convert.toString(value, "%");
         
        } else if (key.endsWith("!%")) {
          key = key.substring(0, key.length()-2);
          value = Convert.toString("%", value);
         
        } else {
          value = Convert.toString("%", value, "%");
        }
       
        if (isIntKey)
          queryJPA.setParameter(ikey, value);
        else
          queryJPA.setParameter(key, value);
       
    } else if (pair.getLast() instanceof Calendar) {
      if (isIntKey)
        queryJPA.setParameter(ikey, (Calendar)
            pair.getLast(), TemporalType.TIMESTAMP);
      else
        queryJPA.setParameter(pair.getFirst(), (Calendar)
            pair.getLast(), TemporalType.TIMESTAMP);
     
    } else if (pair.getLast() instanceof java.sql.Date) {
      if (isIntKey)
        queryJPA.setParameter(ikey, (Date)
            pair.getLast(), TemporalType.DATE);
      else
        queryJPA.setParameter(pair.getFirst(), (Date)
            pair.getLast(), TemporalType.DATE);
     
    } else if (pair.getLast() instanceof java.sql.Time) {
      if (isIntKey)
        queryJPA.setParameter(ikey, (Date)
            pair.getLast(), TemporalType.TIME);
      else
        queryJPA.setParameter(pair.getFirst(), (Date)
            pair.getLast(), TemporalType.TIME);
   
    } else if (pair.getLast() instanceof java.sql.Timestamp) {
      if (isIntKey)
        queryJPA.setParameter(ikey, (Date)
            pair.getLast(), TemporalType.TIMESTAMP);
      else
        queryJPA.setParameter(pair.getFirst(), (Date)
            pair.getLast(), TemporalType.TIMESTAMP);
     
    } else if (pair.getLast() instanceof Date) {
      if (isIntKey)
        queryJPA.setParameter(ikey, (Date)
            pair.getLast(), TemporalType.TIMESTAMP);
      else
        queryJPA.setParameter(pair.getFirst(), (Date)
            pair.getLast(), TemporalType.TIMESTAMP);
   
    } else {
      if (isIntKey)
        queryJPA.setParameter(ikey, pair.getLast());
      else
        queryJPA.setParameter(pair.getFirst(), pair.getLast());
    }
  }
 
  /**
   * Inicializa a unidade de persistencia padrao do sistema.
   */
  private void initializePersistenceUnit() {
    try {
      if (!Validator.isValid(PERSISTENCE_UNIT)) {
        throw new ValidationException(
            "A propriedade 'jpa.persistence.unit' esta nula.");
      }
     
      // Inicializa a fabrica de gerenciadores de entidade.
      if (this.unitProperties != null) {
        factory = Persistence.createEntityManagerFactory(
            PERSISTENCE_UNIT, this.unitProperties);
      } else {
        factory = Persistence.createEntityManagerFactory(
            PERSISTENCE_UNIT);
      }
     
      if (factory == null) {
        throw new PersistenceException(Convert.toString(
            "A EntityManagerFactory para a unidade de persistencia",
            " '", PERSISTENCE_UNIT, "' e nula."));
      }
     
    } catch (Exception e) {
      throw new PersistenceException(Convert.toString(
          "Erro ao obter unidade de persistencia JPA: ",
          PERSISTENCE_UNIT), e);
    }
  }
 
  /**
   * Verifica se a classe alvo do DAO possui uma consulta nomeada no padrao:
   * {@code ClassName.findAll}
   *
   * @return Verdadeiro quando a classe contiver a consulta.
   */
  private boolean hasQueryFindAll() {
    String name = Convert.toString(clazz.getSimpleName(), ".findAll");
       
    NamedQueries nq = clazz.getAnnotation(NamedQueries.class);
    if (nq != null) {
      for (NamedQuery q : nq.value()) {
        if (q.name().equals(name)){
          return true;
        }
      }
    }
    return false;
  }
 
  /**
   * Finaliza o Entity Manager da classe atual.
   */
  @Override
  protected void finalize() throws Throwable {
    if (this.manager != null) {
      this.manager.close();
      this.manager = null;
    }
   
    if (this.factory != null) {
      this.factory.close();
      this.factory = null;
    }
  }
 
  @Override
  public String toString() {
    return Convert.toString(this.getClass().getSimpleName(), "{",
        "Class: ", this.clazz.getName(), ", ",
        "PersistenceUnit: ", PERSISTENCE_UNIT, ", ",
        "EntityManager: ", manager.getClass().getName(), ", ",
        "EntityManagerFactory: ", factory.getClass().getName(),
      "}");
  }
 
  /**
   * Retorna a instancia do gerenciador de entidades da DAO JPA corrente
   *
   * @return Retorna um objeto de {@link EntityManager}
   */
  public EntityManager getEntityManager() {
    return manager;
  }

  @Override
  public Transaction transaction() {
    autoManageTransactions = false;
    return new JPATransactionAdapter(this.manager.getTransaction());
  }
 
  /**
   * Gerencia o rollback das transacoes automaticamente
   */
  private void rollbackTransaction() {
    if (autoManageTransactions)
      try{manager.getTransaction().rollback();}catch(Exception x) {/**/}
  }

  /**
   * Gerencia o commit das transacoes automaticamente
   */
  private void commitTransaction() {
    if (autoManageTransactions)
      manager.getTransaction().commit();
  }

  /**
   * Gerencia o begin das transacoes automaticamente
   */
  private void beginTransaction() {
    if (autoManageTransactions)
      manager.getTransaction().begin();
  }
 
}
TOP

Related Classes of com.im.imjutil.dao.jpa.JPADAO

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.