Java tutorial
/* * Copyright 2012 Lorenzo Gonzlez. * * 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 es.logongas.ix3.dao.impl; import es.logongas.ix3.core.BusinessException; import es.logongas.ix3.dao.GenericDAO; import es.logongas.ix3.core.Order; import es.logongas.ix3.core.Page; import es.logongas.ix3.core.PageRequest; import es.logongas.ix3.dao.DataSession; import es.logongas.ix3.dao.Filter; import es.logongas.ix3.dao.FilterOperator; import es.logongas.ix3.dao.Filters; import es.logongas.ix3.dao.SearchResponse; import es.logongas.ix3.dao.TransactionManager; import es.logongas.ix3.dao.metadata.MetaData; import es.logongas.ix3.dao.metadata.MetaDataFactory; import es.logongas.ix3.util.ReflectionUtil; import java.io.Serializable; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.CacheMode; import org.hibernate.Query; import org.hibernate.Session; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StringUtils; public class GenericDAOImplHibernate<EntityType, PrimaryKeyType extends Serializable> implements GenericDAO<EntityType, PrimaryKeyType> { @Autowired protected MetaDataFactory metaDataFactory; @Autowired protected TransactionManager transactionManager; @Autowired protected ExceptionTranslator exceptionTranslator; private Class<EntityType> entityType = null; protected final Log log = LogFactory.getLog(getClass()); public GenericDAOImplHibernate() { Type type = getClass().getGenericSuperclass(); if (type instanceof ParameterizedType) { entityType = (Class<EntityType>) ((ParameterizedType) getClass().getGenericSuperclass()) .getActualTypeArguments()[0]; } } public GenericDAOImplHibernate(Class<EntityType> entityType) { this.entityType = entityType; } @Override public void setEntityType(Class<EntityType> entityType) { this.entityType = entityType; } @Override public Class<EntityType> getEntityType() { return this.entityType; } private MetaData getEntityMetaData() { return metaDataFactory.getMetaData(entityType); } @Override final public EntityType create(DataSession dataSession, Map<String, Object> initialProperties) throws BusinessException { try { EntityType entity; entity = (EntityType) getEntityMetaData().getType().newInstance(); if (initialProperties != null) { for (String key : initialProperties.keySet()) { ReflectionUtil.setValueToBean(entity, key, initialProperties.get(key)); } } return entity; } catch (RuntimeException ex) { throw ex; } catch (Exception ex) { throw new RuntimeException(ex); } } @Override final public EntityType insert(DataSession dataSession, EntityType entity) throws BusinessException { Session session = (Session) dataSession.getDataBaseSessionImpl(); boolean isActivePreviousTransaction = transactionManager.isActive(dataSession); try { if (isActivePreviousTransaction == false) { transactionManager.begin(dataSession); } session.save(entity); if (isActivePreviousTransaction == false) { transactionManager.commit(dataSession); } return entity; } catch (javax.validation.ConstraintViolationException cve) { try { if ((transactionManager.isActive(dataSession) == true) && (isActivePreviousTransaction == false)) { transactionManager.rollback(dataSession); } } catch (Exception exc) { log.error("Fall al hacer un rollback", exc); } throw new BusinessException(exceptionTranslator.getBusinessMessages(cve)); } catch (org.hibernate.exception.ConstraintViolationException cve) { try { if ((transactionManager.isActive(dataSession) == true) && (isActivePreviousTransaction == false)) { transactionManager.rollback(dataSession); } } catch (Exception exc) { log.error("Fall al hacer un rollback", exc); } throw new BusinessException(exceptionTranslator.getBusinessMessages(cve)); } catch (RuntimeException ex) { try { if ((transactionManager.isActive(dataSession) == true) && (isActivePreviousTransaction == false)) { transactionManager.rollback(dataSession); } } catch (Exception exc) { log.error("Fall al hacer un rollback", exc); } throw ex; } catch (Exception ex) { try { if ((transactionManager.isActive(dataSession) == true) && (isActivePreviousTransaction == false)) { transactionManager.rollback(dataSession); } } catch (Exception exc) { log.error("Fall al hacer un rollback", exc); } throw new RuntimeException(ex); } } @Override final public EntityType update(DataSession dataSession, EntityType entity) throws BusinessException { Session session = (Session) dataSession.getDataBaseSessionImpl(); boolean isActivePreviousTransaction = transactionManager.isActive(dataSession); try { if (isActivePreviousTransaction == false) { transactionManager.begin(dataSession); } session.update(entity); if (isActivePreviousTransaction == false) { transactionManager.commit(dataSession); } return entity; } catch (javax.validation.ConstraintViolationException cve) { try { if ((transactionManager.isActive(dataSession) == true) && (isActivePreviousTransaction == false)) { transactionManager.rollback(dataSession); } } catch (Exception exc) { log.error("Fall al hacer un rollback", exc); } throw new BusinessException(exceptionTranslator.getBusinessMessages(cve)); } catch (org.hibernate.exception.ConstraintViolationException cve) { try { if ((transactionManager.isActive(dataSession) == true) && (isActivePreviousTransaction == false)) { transactionManager.rollback(dataSession); } } catch (Exception exc) { log.error("Fall al hacer un rollback", exc); } throw new BusinessException(exceptionTranslator.getBusinessMessages(cve)); } catch (RuntimeException ex) { try { if ((transactionManager.isActive(dataSession) == true) && (isActivePreviousTransaction == false)) { transactionManager.rollback(dataSession); } } catch (Exception exc) { log.error("Fall al hacer un rollback", exc); } throw ex; } catch (Exception ex) { try { if ((transactionManager.isActive(dataSession) == true) && (isActivePreviousTransaction == false)) { transactionManager.rollback(dataSession); } } catch (Exception exc) { log.error("Fall al hacer un rollback", exc); } throw new RuntimeException(ex); } } @Override final public EntityType read(DataSession dataSession, PrimaryKeyType id) throws BusinessException { Session session = (Session) dataSession.getDataBaseSessionImpl(); try { EntityType entity = (EntityType) session.get(getEntityMetaData().getType(), id); return entity; } catch (javax.validation.ConstraintViolationException cve) { throw new BusinessException(exceptionTranslator.getBusinessMessages(cve)); } catch (org.hibernate.exception.ConstraintViolationException cve) { throw new BusinessException(exceptionTranslator.getBusinessMessages(cve)); } catch (RuntimeException ex) { throw ex; } catch (Exception ex) { throw new RuntimeException(ex); } } @Override final public EntityType readByNaturalKey(DataSession dataSession, Object naturalKey) throws BusinessException { Session session = (Session) dataSession.getDataBaseSessionImpl(); try { EntityType entity = (EntityType) session.bySimpleNaturalId(getEntityMetaData().getType()) .load(naturalKey); return entity; } catch (javax.validation.ConstraintViolationException cve) { throw new BusinessException(exceptionTranslator.getBusinessMessages(cve)); } catch (org.hibernate.exception.ConstraintViolationException cve) { throw new BusinessException(exceptionTranslator.getBusinessMessages(cve)); } catch (RuntimeException ex) { throw ex; } catch (Exception ex) { throw new RuntimeException(ex); } } @Override final public EntityType readOriginalByNaturalKey(DataSession dataSession, Object naturalKey) throws BusinessException { Session session = (Session) dataSession.getDataBaseSessionAlternativeImpl(); try { session.setCacheMode(CacheMode.IGNORE); EntityType entity = (EntityType) session.bySimpleNaturalId(getEntityMetaData().getType()) .load(naturalKey); if (entity != null) { session.evict(entity); } return entity; } catch (javax.validation.ConstraintViolationException cve) { throw new BusinessException(exceptionTranslator.getBusinessMessages(cve)); } catch (org.hibernate.exception.ConstraintViolationException cve) { throw new BusinessException(exceptionTranslator.getBusinessMessages(cve)); } catch (RuntimeException ex) { throw ex; } catch (Exception ex) { throw new RuntimeException(ex); } } @Override final public EntityType readOriginal(DataSession dataSession, PrimaryKeyType id) throws BusinessException { Session session = (Session) dataSession.getDataBaseSessionAlternativeImpl(); try { session.setCacheMode(CacheMode.IGNORE); EntityType entity = (EntityType) session.get(getEntityMetaData().getType(), id); if (entity != null) { session.evict(entity); } return entity; } catch (javax.validation.ConstraintViolationException cve) { throw new BusinessException(exceptionTranslator.getBusinessMessages(cve)); } catch (org.hibernate.exception.ConstraintViolationException cve) { throw new BusinessException(exceptionTranslator.getBusinessMessages(cve)); } catch (RuntimeException ex) { throw ex; } catch (Exception ex) { throw new RuntimeException(ex); } } @Override final public boolean delete(DataSession dataSession, EntityType entity) throws BusinessException { Session session = (Session) dataSession.getDataBaseSessionImpl(); boolean isActivePreviousTransaction = transactionManager.isActive(dataSession); boolean exists; try { if (isActivePreviousTransaction == false) { transactionManager.begin(dataSession); } if (entity == null) { exists = false; if (isActivePreviousTransaction == false) { transactionManager.commit(dataSession); } } else { session.delete(entity); exists = true; if (isActivePreviousTransaction == false) { transactionManager.commit(dataSession); } } return exists; } catch (javax.validation.ConstraintViolationException cve) { try { if ((transactionManager.isActive(dataSession) == true) && (isActivePreviousTransaction == false)) { transactionManager.rollback(dataSession); } } catch (Exception exc) { log.error("Fall al hacer un rollback", exc); } throw new BusinessException(exceptionTranslator.getBusinessMessages(cve)); } catch (org.hibernate.exception.ConstraintViolationException cve) { try { if ((transactionManager.isActive(dataSession) == true) && (isActivePreviousTransaction == false)) { transactionManager.rollback(dataSession); } } catch (Exception exc) { log.error("Fall al hacer un rollback", exc); } throw new BusinessException(exceptionTranslator.getBusinessMessages(cve)); } catch (RuntimeException ex) { try { if ((transactionManager.isActive(dataSession) == true) && (isActivePreviousTransaction == false)) { transactionManager.rollback(dataSession); } } catch (Exception exc) { log.error("Fall al hacer un rollback", exc); } throw ex; } catch (Exception ex) { try { if ((transactionManager.isActive(dataSession) == true) && (isActivePreviousTransaction == false)) { transactionManager.rollback(dataSession); } } catch (Exception exc) { log.error("Fall al hacer un rollback", exc); } throw new RuntimeException(ex); } } @Override final public List<EntityType> search(DataSession dataSession, Filters filters, List<Order> orders, SearchResponse searchResponse) throws BusinessException { return pageableSearch(dataSession, filters, orders, null, searchResponse).getContent(); } @Override public Page<EntityType> pageableSearch(DataSession dataSession, Filters filters, List<Order> orders, PageRequest pageRequest, SearchResponse searchResponse) throws BusinessException { if (searchResponse == null) { searchResponse = new SearchResponse(false); } if (orders == null) { orders = new ArrayList<Order>(); } Session session = (Session) dataSession.getDataBaseSessionImpl(); try { String sqlPartFrom = sqlPartFrom(filters); String sqlPartWhere = sqlPartWhere(filters); String sqlPartOrderBy = sqlPartOrder(orders); String sqlPartSelectObject = sqlPartSelectObject(searchResponse); String sqlPartSelectCount = sqlPartSelectCount(searchResponse); String sqlData = sqlPartSelectObject + " " + sqlPartFrom + " " + sqlPartWhere + " " + sqlPartOrderBy; String sqlCount = sqlPartSelectCount + " " + sqlPartFrom + " " + sqlPartWhere; Page page; if (pageRequest == null) { Query queryDatos = session.createQuery(sqlData); setParameters(queryDatos, new HashMap<Object, Object>(getParameterFromFilters(filters))); List results = queryDatos.list(); page = new PageImpl(results, Integer.MAX_VALUE, 0, 1); } else { page = getPaginatedQuery(dataSession, sqlData, sqlCount, pageRequest, getParameterFromFilters(filters)); } return page; } catch (javax.validation.ConstraintViolationException cve) { throw new BusinessException(exceptionTranslator.getBusinessMessages(cve)); } catch (org.hibernate.exception.ConstraintViolationException cve) { throw new BusinessException(exceptionTranslator.getBusinessMessages(cve)); } catch (RuntimeException ex) { throw ex; } catch (Exception ex) { throw new RuntimeException(ex); } } private String sqlPartSelectObject(SearchResponse searchResponse) { String select; if ((searchResponse != null) && (searchResponse.isDistinct() == true)) { select = "SELECT DISTINCT e "; } else { select = "SELECT e "; } return select; } private String sqlPartSelectCount(SearchResponse searchResponse) { String select; if ((searchResponse != null) && (searchResponse.isDistinct() == true)) { select = "SELECT COUNT(DISTINCT e) "; } else { select = "SELECT COUNT(e) "; } return select; } /** * Obtener la parte de la SQL relativa al ORDER BY * * @param orders * @return */ private String sqlPartFrom(Filters filters) { StringBuilder sbFrom = new StringBuilder(); sbFrom.append(" FROM " + getEntityMetaData().getType().getSimpleName() + " e "); if (filters != null) { List<JoinProperty> joinsProperties = getJoinsProperties(filters); for (int i = 0; i < joinsProperties.size(); i++) { JoinProperty joinProperty = joinsProperties.get(i); if ((joinProperty.join != null) && (joinProperty.join.isEmpty() == false)) { sbFrom.append(" JOIN e." + joinProperty.join + " j" + i + " "); } } } return sbFrom.toString(); } /** * Obtener la parte relativa al WHERE * * @param filters * @return */ private String sqlPartWhere(Filters filters) { StringBuilder sqlWhere = new StringBuilder(); sqlWhere.append(" WHERE 1=1 "); if (filters != null) { List<JoinProperty> joinsProperties = getJoinsProperties(filters); for (int i = 0; i < filters.size(); i++) { Filter filter = filters.get(i); JoinProperty joinProperty = joinsProperties.get(i); Object value = filter.getValue(); String propertyName; if ((joinProperty.join != null) && (joinProperty.join.isEmpty() == false)) { propertyName = "j" + i + "." + joinProperty.property; } else { propertyName = "e." + filter.getPropertyName(); } FilterOperator filterOperator = filter.getFilterOperator(); sqlWhere.append(" AND "); if (filterOperator == FilterOperator.eq) { if (value instanceof Object[]) { sqlWhere.append(propertyName + " in (:bind" + i + ")"); } else if (value instanceof Collection) { sqlWhere.append(propertyName + " in (:bind" + i + ")"); } else { sqlWhere.append(propertyName + " = :bind" + i + ""); } } else if (filterOperator == FilterOperator.ne) { sqlWhere.append(propertyName + " != :bind" + i + ""); } else if (filterOperator == FilterOperator.gt) { sqlWhere.append(propertyName + " > :bind" + i + ""); } else if (filterOperator == FilterOperator.ge) { sqlWhere.append(propertyName + " >= :bind" + i + ""); } else if (filterOperator == FilterOperator.lt) { sqlWhere.append(propertyName + " < :bind" + i + ""); } else if (filterOperator == FilterOperator.le) { sqlWhere.append(propertyName + " <= :bind" + i + ""); } else if (filterOperator == FilterOperator.like) { sqlWhere.append(propertyName + " like :bind" + i + ""); } else if (filterOperator == FilterOperator.llike) { sqlWhere.append(propertyName + " like :bind" + i + ""); } else if (filterOperator == FilterOperator.liker) { sqlWhere.append(propertyName + " like :bind" + i + ""); } else if (filterOperator == FilterOperator.lliker) { sqlWhere.append(propertyName + " like :bind" + i + ""); } else if (filterOperator == FilterOperator.isnull) { if (filter.getValue() == Boolean.TRUE) { sqlWhere.append("(" + propertyName + " IS NULL) "); } else { sqlWhere.append("(" + propertyName + " IS NOT NULL) "); } } else { throw new RuntimeException("El nombre del operador no es vlido:" + filterOperator); } } } return sqlWhere.toString(); } private List<JoinProperty> getJoinsProperties(Filters filters) { List<JoinProperty> joinsProperties = new ArrayList<JoinProperty>(); for (int i = 0; i < filters.size(); i++) { Filter filter = filters.get(i); joinsProperties.add(getJoinProperty(filter)); } return joinsProperties; } private JoinProperty getJoinProperty(Filter filter) { JoinProperty joinProperty = new JoinProperty(); String[] propertiesName = filter.getPropertyName().split("\\."); MetaData currentMetaData = getEntityMetaData(); int splitIndex = -1; for (int i = 0; i < propertiesName.length; i++) { currentMetaData = currentMetaData.getPropertyMetaData(propertiesName[i]); if (currentMetaData.isCollection() == true) { if (splitIndex != -1) { throw new RuntimeException("No se permite mas de una coleccin en un filtro where: " + filter.getPropertyName() + " la primera es:" + propertiesName[splitIndex] + " y la segunda es:" + propertiesName[i]); } splitIndex = i; } } if (splitIndex == -1) { joinProperty.join = null; joinProperty.property = filter.getPropertyName(); } else { joinProperty.join = StringUtils.collectionToDelimitedString( Arrays.asList(Arrays.copyOfRange(propertiesName, 0, splitIndex + 1)), "."); joinProperty.property = StringUtils.collectionToDelimitedString( Arrays.asList(Arrays.copyOfRange(propertiesName, splitIndex + 1, propertiesName.length)), "."); } return joinProperty; } /** * En una HQL contiene los datos del JOIN del FROM y de las propiedades del WHERE Esto se hace pq es necesario para buscar datos en colecciones * http://stackoverflow.com/questions/24750754/org-hibernate-queryexception-illegal-attempt-to-dereference-collection */ private class JoinProperty { String join; String property; } /** * Obtener la parte de la SQL relativa al ORDER BY * * @param orders * @return */ private String sqlPartOrder(List<Order> orders) { StringBuilder sbOrder = new StringBuilder(); if ((orders != null) && (orders.size() > 0)) { sbOrder.append(" ORDER BY "); for (int i = 0; i < orders.size(); i++) { Order order = orders.get(i); if (i > 0) { sbOrder.append(","); } sbOrder.append(order.getFieldName()); switch (order.getOrderDirection()) { case Ascending: sbOrder.append(" ASC "); break; case Descending: sbOrder.append(" DESC "); break; default: throw new RuntimeException("orderField.getOrder() desconocido" + order.getOrderDirection()); } } } return sbOrder.toString(); } /** * Establecer los parmetros de las consultas de Query * * @param query * @param filters */ private Map<String, Object> getParameterFromFilters(Filters filters) { Map<String, Object> namedParameters = new HashMap<String, Object>(); if (filters != null) { for (int i = 0; i < filters.size(); i++) { Filter filter = filters.get(i); Object value = filter.getValue(); if (filter.getFilterOperator() == FilterOperator.llike) { value = "%" + filter.getValue() + ""; namedParameters.put("bind" + i, value); } else if (filter.getFilterOperator() == FilterOperator.liker) { value = "" + filter.getValue() + "%"; namedParameters.put("bind" + i, value); } else if (filter.getFilterOperator() == FilterOperator.lliker) { value = "%" + filter.getValue() + "%"; namedParameters.put("bind" + i, value); } else if (filter.getFilterOperator() == FilterOperator.isnull) { //No hay que aadir nada pq este operador no necesita parmetro } else { value = filter.getValue(); namedParameters.put("bind" + i, value); } } } return namedParameters; } /** * Crea una consulta pagina retornando la pgina que se solicita. * * @param sqlData La SQL para Obtener los datos. * @param sqlCount La SQL para Obtener le N total de filas que retornara sqlData. Se pasa para optimizar. * @param pageRequest La pgina que se solicita. * @param parameters La lista de parmetros se establecen por la posicin en la lista * @return La pagina */ protected Page<EntityType> getPaginatedQuery(DataSession dataSession, String sqlData, String sqlCount, PageRequest pageRequest, List<Object> parameters) { Map<Object, Object> indexParameters = new HashMap<Object, Object>(); for (int i = 0; i < parameters.size(); i++) { indexParameters.put(i, parameters.get(i)); } return getGenericPaginatedQuery(dataSession, sqlData, sqlCount, pageRequest, indexParameters); } /** * Crea una consulta pagina retornando la pgina que se solicita. * * @param sqlData La SQL para Obtener los datos. * @param sqlCount La SQL para Obtener le N total de filas que retornara sqlData. Se pasa para optimizar. * @param pageRequest La pgina que se solicita. * @param parameters La lista de parmetros se por el nombre del parmetro de la clave del Map. * @return La pagina */ protected Page<EntityType> getPaginatedQuery(DataSession dataSession, String sqlData, String sqlCount, PageRequest pageRequest, Map<String, Object> parameters) { Map<Object, Object> namedParameters = new HashMap<Object, Object>(); namedParameters.putAll(parameters); return getGenericPaginatedQuery(dataSession, sqlData, sqlCount, pageRequest, namedParameters); } /** * Crea una consulta pagina retornando la pgina que se solicita. * * @param sqlData La SQL para Obtener los datos. * @param sqlCount La SQL para Obtener le N total de filas que retornara sqlData. Se pasa para optimizar. * @param pageRequest La pgina que se solicita. * @param parameters El map debe ser del tipo Map<String,Object> o Map<Integer,Object>. Y se pondrn los parmetros por nombre o por posicin. * @return La pagina */ private Page<EntityType> getGenericPaginatedQuery(DataSession dataSession, String sqlData, String sqlCount, PageRequest pageRequest, Map<Object, Object> parameters) { Session session = (Session) dataSession.getDataBaseSessionImpl(); Query queryDatos = session.createQuery(sqlData); queryDatos.setMaxResults(pageRequest.getPageSize()); queryDatos.setFirstResult(pageRequest.getPageSize() * pageRequest.getPageNumber()); setParameters(queryDatos, parameters); List<EntityType> results = (List<EntityType>) queryDatos.list(); //Vamos ahora a calcular el total de pginas Query queryCount = session.createQuery(sqlCount); setParameters(queryCount, parameters); Long totalCount = (Long) queryCount.uniqueResult(); int totalPages; if (totalCount == 0) { totalPages = 0; } else { totalPages = (int) (Math.ceil(((double) totalCount) / ((double) pageRequest.getPageSize()))); } Page<EntityType> page = new PageImpl<EntityType>(results, pageRequest.getPageSize(), pageRequest.getPageNumber(), totalPages); return page; } /** * Pone los parmetros en una Query * * @param query La Query a la que se le pone los parmetros. * @param parameters El map debe ser del tipo Map<String,Object> o Map<Integer,Object>. Y se pondrn los parmetros por nombre o por posicin. */ private void setParameters(Query query, Map<Object, Object> parameters) { if (parameters != null) { for (Map.Entry<Object, Object> entry : parameters.entrySet()) { Object value = entry.getValue(); Object parameterKey = entry.getKey(); if (parameterKey == null) { throw new NullPointerException("El nombre de un parmetro no puede ser null"); } if (parameterKey instanceof Number) { Number parameterIndex = (Number) parameterKey; query.setParameter(parameterIndex.intValue(), value); } else if (parameterKey instanceof String) { String parameterName = (String) parameterKey; if (value instanceof Object[]) { query.setParameterList(parameterName, (Object[]) value); } else if (value instanceof Collection) { query.setParameterList(parameterName, (Collection) value); } else { query.setParameter(parameterName, value); } } else { throw new RuntimeException("La clave debe ser de tipo String o Number"); } } } } }