/******************************************************************************* * =========================================================== * Ankush : Big Data Cluster Management Solution * =========================================================== * * (C) Copyright 2014, by Impetus Technologies * * This is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License (LGPL v3) as * published by the Free Software Foundation; * * This software is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ******************************************************************************/ package com.impetus.ankush.common.dao.impl; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.persistence.EntityManager; import javax.persistence.EntityNotFoundException; import javax.persistence.PersistenceContext; import javax.persistence.Query; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.impetus.ankush.common.dao.GenericDao; /** * This class serves as the Base class for all other DAOs - namely to hold * common CRUD methods that they might all use. You should only need to extend * this class when your require custom CRUD logic. * * To register this class in your Spring context file, use the following XML. * * @param <T> * a type variable * @param <P> * the primary key for that type */ public class GenericDaoJpa<T, P extends Serializable> implements GenericDao<T, P> { /** The Constant UNCHECKED. */ private static final String UNCHECKED = "unchecked"; /** The Constant SELECT_OBJ_FROM. */ private static final String SELECT_OBJ_FROM = "select obj from "; /** * Log variable for all child classes. Uses LogFactory.getLog(getClass()) * from Commons Logging */ protected final Logger log = LoggerFactory.getLogger(getClass()); /** * Entity manager, injected by Spring using @PersistenceContext annotation * on setEntityManager(). */ @PersistenceContext private EntityManager entityManager; /** The persistent class. */ private final Class<T> persistentClass; /** * Constructor that takes in a class to see which type of entity to persist. * Use this constructor when subclassing or using dependency injection. * * @param persistentClass * the class type you'd like to persist */ public GenericDaoJpa(final Class<T> persistentClass) { this.persistentClass = persistentClass; } /** * Constructor that takes in a class to see which type of entity to persist. * Use this constructor when subclassing or using dependency injection. * * @param persistentClass * the class type you'd like to persist * @param entityManager * the configured EntityManager for JPA implementation. */ public GenericDaoJpa(final Class<T> persistentClass, EntityManager entityManager) { this.persistentClass = persistentClass; this.entityManager = entityManager; } /** * Gets the entity manager. * * @return the entity manager */ public EntityManager getEntityManager() { return this.entityManager; } /* * (non-Javadoc) * * @see com.impetus.ankush.common.dao.GenericDao#getAll(int, int, * java.lang.String[]) */ public List<T> getAll(int start, int maxResults, String... orderBy) { Query query = this.entityManager.createQuery(getAllQueryString() + createOrderByClause(orderBy)); query.setFirstResult(start); query.setMaxResults(maxResults); return query.getResultList(); } /** * Gets the all query string. * * @return the all query string */ private String getAllQueryString() { return SELECT_OBJ_FROM + this.persistentClass.getName() + " obj"; } /* * (non-Javadoc) * * @see com.impetus.ankush.common.dao.GenericDao#getAllCount() */ public int getAllCount() { return getCount(convertToCountQuery(getAllQueryString()), (List<Object>) null); } /** * {@inheritDoc} */ @SuppressWarnings(UNCHECKED) public List<T> getAllDistinct() { return new ArrayList(new LinkedHashSet(getAll(0, Integer.MAX_VALUE))); } /** * {@inheritDoc} */ public T get(P id) { T entity = this.entityManager.find(this.persistentClass, id); if (entity == null) { String msg = "Uh oh, '" + this.persistentClass + "' object with id '" + id + "' not found..."; log.warn(msg); throw new EntityNotFoundException(msg); } return entity; } /** * {@inheritDoc} */ public T getGuarded(P id) { return this.entityManager.find(this.persistentClass, id); } /** * {@inheritDoc} */ public T getReference(P id) { try { T entity = this.entityManager .getReference(this.persistentClass, id); if (entity == null) { throw new RuntimeException(); } return entity; } catch (Exception e) { String msg = "Uh oh, '" + this.persistentClass + "' object with id '" + id + "' not found..."; log.warn(msg); throw new EntityNotFoundException(msg); } } /** * {@inheritDoc} */ public boolean exists(P id) { T entity = this.entityManager.find(this.persistentClass, id); return entity != null; } /** * {@inheritDoc} */ public T save(T object) { return this.entityManager.merge(object); } /** * {@inheritDoc} */ public void remove(P id) { this.entityManager.remove(this.get(id)); } /** * {@inheritDoc} */ @SuppressWarnings(UNCHECKED) public T getByPropertyValue(Map<String, Object> propertyValueMap) { return (T) createSelectQuery(propertyValueMap).getSingleResult(); } /* * (non-Javadoc) * * @see * com.impetus.ankush.common.dao.GenericDao#getAllByPropertyValue(java.util * .Map, int, int, java.lang.String[]) */ @SuppressWarnings(UNCHECKED) public List<T> getAllByPropertyValue(Map<String, Object> propertyValueMap, int start, int maxResults, String... orderBy) { Query query = createSelectQuery(propertyValueMap, orderBy); query.setFirstResult(start); query.setMaxResults(maxResults); return query.getResultList(); } /* * (non-Javadoc) * * @see * com.impetus.ankush.common.dao.GenericDao#getAllByPropertyValueCount(java * .util.Map) */ public int getAllByPropertyValueCount(Map<String, Object> propertyValueMap) { LinkedHashMap<String, Object> orderedMap = getOrderedMap(propertyValueMap); String selectQueryString = createSelectQueryString(orderedMap) .toString(); return this .getCount(convertToCountQuery(selectQueryString), orderedMap); } /* * (non-Javadoc) * * @see * com.impetus.ankush.common.dao.GenericDao#getAllByNativeQuery(java.lang * .String) */ public List<T> getAllByNativeQuery(String sql) { return entityManager.createNativeQuery(sql, persistentClass) .getResultList(); } /* * (non-Javadoc) * * @see * com.impetus.ankush.common.dao.GenericDao#executeNativeQuery(java.lang * .String) */ public int executeNativeQuery(String sql) { return entityManager.createNativeQuery(sql, persistentClass) .executeUpdate(); } /** * {@inheritDoc} */ public int deleteAllByPropertyValue(Map<String, Object> propertyValueMap) { List<T> objects = getAllByPropertyValue(propertyValueMap, 0, Integer.MAX_VALUE); this.removeAll(objects); this.entityManager.flush(); return objects.size(); } /** * Create a select query from the given property value map. * * @param propertyValueMap * query key value * @param orderBy * the order by * @return Query object */ private Query createSelectQuery(Map<String, Object> propertyValueMap, String... orderBy) { LinkedHashMap<String, Object> orderedMap = this .getOrderedMap(propertyValueMap); StringBuilder builder = createSelectQueryString(orderedMap).append( this.createOrderByClause(orderBy)); Query query = this.entityManager.createQuery(builder.toString()); // fill parameters this.assignQueryParameters(query, this.flattenParameterList(Collections .singletonList(orderedMap))); return query; } /** * Creates the select query string. * * @param orderedMap * the ordered map * @return the string builder */ private StringBuilder createSelectQueryString( LinkedHashMap<String, Object> orderedMap) { return new StringBuilder(SELECT_OBJ_FROM) .append(this.persistentClass.getName()).append(" obj where ") .append(this.createAndClause(orderedMap, 1)); } /** * Removes the all. * * @param objects * the objects */ private void removeAll(List<T> objects) { for (Object o : objects) { entityManager.remove(o); } } /* * (non-Javadoc) * * @see * com.impetus.ankush.common.dao.GenericDao#getAllByNamedQuery(java.lang * .String, java.util.Map, int, int) */ @SuppressWarnings(UNCHECKED) public List<T> getAllByNamedQuery(String queryName, Map<String, Object> propertyValueMap, int start, int maxResults) { Query query = getEntityManager().createNamedQuery(queryName); for (Entry<String, Object> entry : propertyValueMap.entrySet()) { query.setParameter(entry.getKey(), entry.getValue()); } query.setFirstResult(start); query.setMaxResults(maxResults); return query.getResultList(); } /* * (non-Javadoc) * * @see * com.impetus.ankush.common.dao.GenericDao#getAllOfOrMatch(java.util.Map, * int, int, java.lang.String[]) */ @SuppressWarnings(UNCHECKED) public List<T> getAllOfOrMatch(Map<String, Object> queryMap, int start, int maxResults, String... orderBy) { LinkedHashMap<String, Object> orderedMap = this.getOrderedMap(queryMap); StringBuilder builder = createSelectOrQueryString(orderedMap).append( this.createOrderByClause(orderBy)); Query query = this.entityManager.createQuery(builder.toString()); // fill parameters this.assignQueryParameters(query, this.flattenParameterList(Collections .singletonList(orderedMap))); query.setFirstResult(start); query.setMaxResults(maxResults); return query.getResultList(); } /** * Creates the select or query string. * * @param orderedMap * the ordered map * @return the string builder */ private StringBuilder createSelectOrQueryString( LinkedHashMap<String, Object> orderedMap) { return new StringBuilder(SELECT_OBJ_FROM) .append(this.persistentClass.getName()).append(" obj where ") .append(this.createOrClause(orderedMap, 1)); } /* * (non-Javadoc) * * @see * com.impetus.ankush.common.dao.GenericDao#getAllOfOrMatchCount(java.util * .Map) */ public int getAllOfOrMatchCount(Map<String, Object> queryMap) { LinkedHashMap<String, Object> orderedMap = getOrderedMap(queryMap); return getCount( convertToCountQuery(createSelectOrQueryString(orderedMap) .toString()), orderedMap); } /* * (non-Javadoc) * * @see * com.impetus.ankush.common.dao.GenericDao#getAllByDisjunctionveNormalQuery * (java.util.List, int, int, java.lang.String[]) */ @SuppressWarnings(UNCHECKED) public List<T> getAllByDisjunctionveNormalQuery( List<Map<String, Object>> disjunctionMaps, int start, int maxResults, String... orderBy) { List<LinkedHashMap<String, Object>> orderedMaps = this .getOrderedMap(disjunctionMaps); StringBuilder builder = createDisjunctionNormalQueryString(orderedMaps) .append(this.createOrderByClause(orderBy)); Query query = this.entityManager.createQuery(builder.toString()); // fill parameters this.assignQueryParameters(query, this.flattenParameterList(orderedMaps)); query.setFirstResult(start); query.setMaxResults(maxResults); return query.getResultList(); } /** * Creates the disjunction normal query string. * * @param orderedMaps * the ordered maps * @return the string builder */ private StringBuilder createDisjunctionNormalQueryString( List<LinkedHashMap<String, Object>> orderedMaps) { List<String> andClauses = new ArrayList<String>(orderedMaps.size()); int startIndex = 1; for (LinkedHashMap<String, Object> queryMap : orderedMaps) { andClauses.add(this.createAndClause(queryMap, startIndex)); startIndex += queryMap.size(); } String disjunction = new StringBuilder("(") .append(StringUtils.join(andClauses, ") or (")).append(") ") .toString(); StringBuilder builder = new StringBuilder(SELECT_OBJ_FROM) .append(this.persistentClass.getName()).append(" obj where ") .append(disjunction); return builder; } /* * (non-Javadoc) * * @see com.impetus.ankush.common.dao.GenericDao# * getAllByDisjunctionveNormalQueryCount(java.util.List) */ public int getAllByDisjunctionveNormalQueryCount( List<Map<String, Object>> disjunctionMaps) { List<LinkedHashMap<String, Object>> orderedDisjunctionMaps = this .getOrderedMap(disjunctionMaps); return getCount( convertToCountQuery(createDisjunctionNormalQueryString( orderedDisjunctionMaps).toString()), this.flattenParameterList(orderedDisjunctionMaps)); } /** * Gets the ordered map. * * @param <K> * the key type * @param <V> * the value type * @param maps * the maps * @return the ordered map */ private <K, V> List<LinkedHashMap<K, V>> getOrderedMap(List<Map<K, V>> maps) { List<LinkedHashMap<K, V>> orderedMaps = new ArrayList<LinkedHashMap<K, V>>( maps.size()); for (Map<K, V> map : maps) { orderedMaps.add(this.getOrderedMap(map)); } return orderedMaps; } /** * Gets the ordered map. * * @param <K> * the key type * @param <V> * the value type * @param map * the map * @return the ordered map */ private <K, V> LinkedHashMap<K, V> getOrderedMap(Map<K, V> map) { return new LinkedHashMap<K, V>(map); } /** * Flatten parameter list. * * @param parameterMaps * the parameter maps * @return the list */ private List<Object> flattenParameterList( List<LinkedHashMap<String, Object>> parameterMaps) { List<Object> parameterList = new ArrayList<Object>(); for (HashMap<String, Object> parameterMap : parameterMaps) { for (Entry<String, Object> entry : parameterMap.entrySet()) { parameterList.add(entry.getValue()); } } return parameterList; } /** * Assign query parameters. * * @param query * the query * @param parameters * the parameters */ private void assignQueryParameters(Query query, List<Object> parameters) { for (int i = 0; i < parameters.size(); i++) { query.setParameter(i + 1, parameters.get(i)); } } /** * Creates the and clause. * * @param queryMap * the query map * @param startIndex * the start index * @return the string */ private String createAndClause(LinkedHashMap<String, Object> queryMap, int startIndex) { return createClauseList(queryMap, startIndex, " and "); } /** * Creates the or clause. * * @param queryMap * the query map * @param startIndex * the start index * @return the string */ private String createOrClause(LinkedHashMap<String, Object> queryMap, int startIndex) { return createClauseList(queryMap, startIndex, " or "); } /** * Creates the clause list. * * @param queryMap * the query map * @param startIndex * the start index * @param joiner * the joiner * @return the string */ private String createClauseList(LinkedHashMap<String, Object> queryMap, int startIndex, String joiner) { List<StringBuilder> clauses = new ArrayList<StringBuilder>( queryMap.size()); for (String property : queryMap.keySet()) { clauses.add(new StringBuilder("obj.").append(property) .append(" = ?").append(startIndex++)); } return StringUtils.join(clauses, joiner); } /** * Creates the order by clause. * * @param orderBy * the order by * @return the string */ private String createOrderByClause(String[] orderBy) { if (orderBy == null || orderBy.length == 0) { return StringUtils.EMPTY; } List<StringBuilder> clauses = new ArrayList<StringBuilder>( orderBy.length); for (String order : orderBy) { StringBuilder builder = new StringBuilder("obj."); if (order.charAt(0) == '-') { builder.append(order.substring(1)).append(" DESC"); } else { builder.append(order); } clauses.add(builder); } return " order by " + StringUtils.join(clauses, ','); } /** * Convert to count query. * * @param query * the query * @return the string */ private String convertToCountQuery(String query) { Pattern pattern = Pattern.compile("select(.+)from"); Matcher matcher = pattern.matcher(query); if (matcher.find()) { String s = matcher.group(1).trim(); query = StringUtils.replaceOnce(query, s, "count(" + s + ")"); } return query; } /** * Gets the count. * * @param queryString * the query string * @param orderedMap * the ordered map * @return the count */ private int getCount(String queryString, LinkedHashMap<String, Object> orderedMap) { Query query = this.entityManager.createQuery(queryString); if (orderedMap != null) { assignQueryParameters(query, flattenParameterList(Collections.singletonList(orderedMap))); } return ((Long) query.getSingleResult()).intValue(); } /** * Gets the count. * * @param queryString * the query string * @param parameterList * the parameter list * @return the count */ private int getCount(String queryString, List<Object> parameterList) { Query query = this.entityManager.createQuery(queryString); if (parameterList != null) { assignQueryParameters(query, parameterList); } return ((Long) query.getSingleResult()).intValue(); } public List getCustomQuery(String queryString) { Query query = this.entityManager.createQuery(queryString); return query.getResultList(); } }