From 4dddc1f3dcf17e12709719bb1fb8771ae1ab6fa9 Mon Sep 17 00:00:00 2001 From: Joern Muehlencord Date: Wed, 25 Dec 2019 23:04:55 +0100 Subject: [PATCH] generalized StandardController --- .../shared/db/CommonAbstractController.java | 1137 +++++++++-------- .../shared/db/StandardController.java | 99 -- 2 files changed, 613 insertions(+), 623 deletions(-) diff --git a/db/src/main/java/de/muehlencord/shared/db/CommonAbstractController.java b/db/src/main/java/de/muehlencord/shared/db/CommonAbstractController.java index 78ecd2c..b34cee5 100644 --- a/db/src/main/java/de/muehlencord/shared/db/CommonAbstractController.java +++ b/db/src/main/java/de/muehlencord/shared/db/CommonAbstractController.java @@ -20,6 +20,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -29,6 +30,7 @@ import javax.ejb.LockType; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.inject.Inject; +import javax.persistence.EntityGraph; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; @@ -38,6 +40,7 @@ import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.transaction.Transactional; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; /** @@ -46,557 +49,643 @@ import org.apache.commons.lang3.StringUtils; */ public abstract class CommonAbstractController { - /** - * the entity manager to use - */ - @Inject - @ApplicationPU - protected EntityManager em; + /** + * the entity manager to use + */ + @Inject + @ApplicationPU + protected EntityManager em; - /** - * - * @param the type of the entity to search for - * @param entityClass the entity class to return - * @return a list of all entities found - * @throws ControllerException if the search cannot be executed. - */ - @Lock(LockType.READ) - public List findAll(Class entityClass) throws ControllerException { - return findAll(entityClass, new ArrayList<>()); + @Lock(LockType.READ) + public T find(Class clazz, Object id) { + return em.find(clazz, id); + } + + private EntityGraph getEntityGraph(Class clazz, String... subGraphItems) { + EntityGraph graph = this.em.createEntityGraph(clazz); + if (subGraphItems != null) { + for (String subGraphItem : subGraphItems) { + graph.addSubgraph(subGraphItem); + } + } + return graph; + } + + @Lock(LockType.READ) + public T find(Class clazz, Object id, String... subGraphItems) { + EntityGraph graph = getEntityGraph(clazz, subGraphItems); + Map hints = new HashMap<>(); + hints.put("javax.persistence.loadgraph", graph); + + T entity = (T) em.find(clazz, id, hints); + return entity; + } + + @Lock(LockType.READ) + public List find(Class clazz, Map filters, List orderFields, String... subGraphItems) { + return find(clazz, filters, orderFields, 0, 0, subGraphItems); + } + + @Lock(LockType.READ) + public List find(Class clazz, Map filters, List orderFields, int limit, int offset, String... subGraphItems) { + final CriteriaBuilder cb = em.getCriteriaBuilder(); + final CriteriaQuery criteria = cb.createQuery(clazz); + + final Root r = criteria.from(clazz); + Predicate filterCondition = getFilterCondition(cb, r, filters); + if (filterCondition != null) { + criteria.where(filterCondition); + } + List orderList = new ArrayList<>(); + orderFields.stream().forEachOrdered(field -> orderList.add(cb.asc(r.get(field)))); + final TypedQuery query = em.createQuery(criteria.orderBy(orderList)) + .setFirstResult(offset); + if (limit > 0) { + query.setMaxResults(limit); } - /** - * - * @param the type of the entity to search for - * @param entityClass the entity class to return - * @param orderFields the fields to order the result by. - * @return a list of all entities found - * @throws ControllerException if the search cannot be executed. - */ - @Lock(LockType.READ) - public List findAll(Class entityClass, String... orderFields) throws ControllerException { - return findAll(entityClass, Arrays.asList(orderFields)); + EntityGraph graph = getEntityGraph(clazz, subGraphItems); + query.setHint("javax.persistence.loadgraph", graph); + + return query.getResultList(); + } + + @Lock(LockType.READ) + public List find(Class clazz, List filters, List orderFields, String... subGraphItems) throws ControllerException { + return find(clazz, filters, orderFields, 0, 0); + } + + @Lock(LockType.READ) + public List find(Class clazz, List filters, List orderFields, int limit, int offset, String... subGraphItems) throws ControllerException { + final CriteriaBuilder cb = em.getCriteriaBuilder(); + final CriteriaQuery criteria = cb.createQuery(clazz); + + final Root r = criteria.from(clazz); + Predicate filterCondition = getFilterCondition(cb, r, filters); + if (filterCondition != null) { + criteria.where(filterCondition); + } + List orderList = new ArrayList<>(); + if (orderFields != null) { + orderFields.stream().forEachOrdered(field -> orderList.add(cb.asc(r.get(field)))); + } + final TypedQuery query = em.createQuery(criteria.orderBy(orderList)).setFirstResult(offset); + if (limit > 0) { + query.setMaxResults(limit); } - /** - * - * @param the type of the entity to search for - * @param entityClass the entity class to return - * @param orderFields the fields to order the result by. - * @return a list of all entities found - * @throws ControllerException if the search cannot be executed. - */ - @Lock(LockType.READ) - public List findAll(Class entityClass, List orderFields) throws ControllerException { - final CriteriaBuilder cb = em.getCriteriaBuilder(); - final CriteriaQuery criteria = cb.createQuery(entityClass); - - final Root r = criteria.from(entityClass); - - if (EndDateable.class.isAssignableFrom(entityClass.getClass())) { - Date now = DateUtil.getCurrentTimeInUTC(); - List searchFilter = new ArrayList<>(); - searchFilter.add(new DefaultSearchFilter("validFrom", Comparator.GREATER_OR_EQUAL_THAN, now)); - searchFilter.add(new DefaultSearchFilter("validTo", Comparator.LESS_THAN, now)); - Predicate filterCondition = getFilterCondition(cb, r, searchFilter); - criteria.where(filterCondition); - } - - List orderList = new ArrayList<>(); - orderFields.stream().forEachOrdered(field -> orderList.add(cb.asc(r.get(field)))); - final TypedQuery query = em.createQuery(criteria.orderBy(orderList)); - - return query.getResultList(); + if (!ArrayUtils.isEmpty(subGraphItems)) { + EntityGraph graph = getEntityGraph(clazz, subGraphItems); + query.setHint("javax.persistence.loadgraph", graph); } - /* **** standard crud options *** */ - /** - * updates the audit field of the entity class. - * - * @param audit the audit to apply - * @param onCreate specifies, whether the the update should be applied for a new entity or not. If yes, then also the createdBy /createdOn fields - * are updated. Otherwise these fields are skipped. - * @param changedBy the username to apply - * @return an updated audit object to use for the updated entity. - * @throws ControllerException if the audit object cannot be updated - */ - public Audit applyAuditChanges(Audit audit, boolean onCreate, String changedBy) throws ControllerException { - if (audit == null) { - audit = new Audit(); - } - if (onCreate) { - audit.setCreatedBy(changedBy); - audit.setCreatedOn(DateUtil.getCurrentTimeInUTC()); - } - audit.setLastUpdatedBy(changedBy); - audit.setLastUpdatedOn(DateUtil.getCurrentTimeInUTC()); + return query.getResultList(); + } - return audit; + /** + * + * @param the type of the entity to search for + * @param entityClass the entity class to return + * @return a list of all entities found + * @throws ControllerException if the search cannot be executed. + */ + @Lock(LockType.READ) + public List findAll(Class entityClass) throws ControllerException { + return findAll(entityClass, new ArrayList<>()); + } + + /** + * + * @param the type of the entity to search for + * @param entityClass the entity class to return + * @param orderFields the fields to order the result by. + * @return a list of all entities found + * @throws ControllerException if the search cannot be executed. + */ + @Lock(LockType.READ) + public List findAll(Class entityClass, String... orderFields) throws ControllerException { + return findAll(entityClass, Arrays.asList(orderFields)); + } + + /** + * + * @param the type of the entity to search for + * @param entityClass the entity class to return + * @param orderFields the fields to order the result by. + * @return a list of all entities found + * @throws ControllerException if the search cannot be executed. + */ + @Lock(LockType.READ) + public List findAll(Class entityClass, List orderFields) throws ControllerException { + final CriteriaBuilder cb = em.getCriteriaBuilder(); + final CriteriaQuery criteria = cb.createQuery(entityClass); + + final Root r = criteria.from(entityClass); + + if (EndDateable.class.isAssignableFrom(entityClass.getClass())) { + Date now = DateUtil.getCurrentTimeInUTC(); + List searchFilter = new ArrayList<>(); + searchFilter.add(new DefaultSearchFilter("validFrom", Comparator.GREATER_OR_EQUAL_THAN, now)); + searchFilter.add(new DefaultSearchFilter("validTo", Comparator.LESS_THAN, now)); + Predicate filterCondition = getFilterCondition(cb, r, searchFilter); + criteria.where(filterCondition); } - /** - * creates an new entity or if the entity already exists updates it - * - * @param the type of the entity to search for - * @param entity the entity to create or update - * @param createdBy the username to apply write into the audit history - * @return the entity after it has been written to the database. - * @throws ControllerException if the the creation or update of the entity fails. - */ - @TransactionAttribute(TransactionAttributeType.REQUIRED) - @Transactional - @Lock(LockType.WRITE) - public T createOrUpdate(T entity, String createdBy) throws ControllerException { - if (entity.getId() == null) { - create(entity, createdBy); - return entity; - } else { - return update(entity, createdBy); - } + List orderList = new ArrayList<>(); + orderFields.stream().forEachOrdered(field -> orderList.add(cb.asc(r.get(field)))); + final TypedQuery query = em.createQuery(criteria.orderBy(orderList)); + + return query.getResultList(); + } + + /* **** standard crud options *** */ + /** + * updates the audit field of the entity class. + * + * @param audit the audit to apply + * @param onCreate specifies, whether the the update should be applied for a new entity or not. If yes, then also the createdBy /createdOn fields + * are updated. Otherwise these fields are skipped. + * @param changedBy the username to apply + * @return an updated audit object to use for the updated entity. + * @throws ControllerException if the audit object cannot be updated + */ + public Audit applyAuditChanges(Audit audit, boolean onCreate, String changedBy) throws ControllerException { + if (audit == null) { + audit = new Audit(); + } + if (onCreate) { + audit.setCreatedBy(changedBy); + audit.setCreatedOn(DateUtil.getCurrentTimeInUTC()); + } + audit.setLastUpdatedBy(changedBy); + audit.setLastUpdatedOn(DateUtil.getCurrentTimeInUTC()); + + return audit; + } + + /** + * creates an new entity or if the entity already exists updates it + * + * @param the type of the entity to search for + * @param entity the entity to create or update + * @param createdBy the username to apply write into the audit history + * @return the entity after it has been written to the database. + * @throws ControllerException if the the creation or update of the entity fails. + */ + @TransactionAttribute(TransactionAttributeType.REQUIRED) + @Transactional + @Lock(LockType.WRITE) + public T createOrUpdate(T entity, String createdBy) throws ControllerException { + if (entity.getId() == null) { + create(entity, createdBy); + return entity; + } else { + return update(entity, createdBy); + } + } + + /** + * creates an new entity. + * + * @param the type of the entity to handle + * @param entity the entity to create or update + * @param createdBy the username to apply write into the audit history + * @return the entity after it has been written to the database. + * @throws ControllerException if the the creation fails + */ + @TransactionAttribute(TransactionAttributeType.REQUIRED) + @Transactional + @Lock(LockType.WRITE) + public T create(T entity, String createdBy) throws ControllerException { + if (Auditable.class.isAssignableFrom(entity.getClass())) { + Audit audit = ((Auditable) entity).getAudit(); + ((Auditable) entity).setAudit(applyAuditChanges(audit, true, createdBy)); + } + if (EndDateable.class.isAssignableFrom(entity.getClass())) { + EndDateable endDateable = (EndDateable) entity; + if (endDateable.getValidity() == null) { + Validity validity = new Validity(); + endDateable.setValidity(validity); + } + if (endDateable.getValidity().getValidFrom() == null) { + endDateable.getValidity().setValidFrom(DateUtil.getCurrentTimeInUTC()); + } + } + em.persist(entity); + return entity; + } + + /** + * updates an existing entity. + * + * @param the type of the entity to handle + * @param entity the entity to update + * @param updatedBy the username to apply write into the audit history + * @return the entity after it has been written to the database. + * @throws ControllerException if the the updates fails + */ + private T executeUpdate(T entity, String updatedBy) throws ControllerException { + T currentEntity = attach(entity); + if (Auditable.class.isAssignableFrom(currentEntity.getClass())) { + Audit audit = ((Auditable) currentEntity).getAudit(); + ((Auditable) currentEntity).setAudit(applyAuditChanges(audit, false, updatedBy)); } - /** - * creates an new entity. - * - * @param the type of the entity to handle - * @param entity the entity to create or update - * @param createdBy the username to apply write into the audit history - * @return the entity after it has been written to the database. - * @throws ControllerException if the the creation fails - */ - @TransactionAttribute(TransactionAttributeType.REQUIRED) - @Transactional - @Lock(LockType.WRITE) - public T create(T entity, String createdBy) throws ControllerException { - if (Auditable.class.isAssignableFrom(entity.getClass())) { - Audit audit = ((Auditable) entity).getAudit(); - ((Auditable) entity).setAudit(applyAuditChanges(audit, true, createdBy)); - } - if (EndDateable.class.isAssignableFrom(entity.getClass())) { - EndDateable endDateable = (EndDateable) entity; - if (endDateable.getValidity() == null) { - Validity validity = new Validity(); - endDateable.setValidity(validity); + if (EndDateable.class.isAssignableFrom(currentEntity.getClass())) { + // end date existing entity + EndDateable endDateable = (EndDateable) currentEntity; + Validity validity = endDateable.getValidity(); + if (validity == null) { + validity = new Validity(); + validity.setValidFrom(DateUtil.getCurrentTimeInUTC()); + } + validity.setValidTo(DateUtil.getCurrentTimeInUTC()); + + endDateable.setValidity(validity); + // and create new entity instead + } + + return currentEntity; + } + + /** + * updates an existing entity. + * + * @param the type of the entity to handle + * @param entity the entity to update + * @param updatedBy the username to apply write into the audit history + * @return the entity after it has been written to the database. + * @throws ControllerException if the the updates fails + */ + @TransactionAttribute(TransactionAttributeType.REQUIRED) + @Transactional + @Lock(LockType.WRITE) + public T update(T entity, String updatedBy) throws ControllerException { + T currentEntity = executeUpdate(entity, updatedBy); + if (EndDateable.class.isAssignableFrom(entity.getClass())) { + T newEntity = EntityUtil.cloneToNewEntity(currentEntity); + em.merge(currentEntity); + return create(newEntity, updatedBy); + } else { + // if it is not enddatable, just update it (already done above) + // and save it + return em.merge(currentEntity); + } + } + + /** + * Deletes an entity from the database. If the entity implements the {@link EndDateable} interface, the entity is not deleted but marked as deleted + * (end date set). + * + * @param the type of the entity to handle + * @param entity the entity to delete + * @param deletedBy the username to apply write into the audit history + * @throws ControllerException if the deletion fails. + */ + @TransactionAttribute(TransactionAttributeType.REQUIRED) + @Transactional + @Lock(LockType.WRITE) + public void delete(T entity, String deletedBy) throws ControllerException { + // if the entity is endDateable just set the validToDate to now intead of executing the deletion + if (EndDateable.class.isAssignableFrom(entity.getClass())) { + T currentEntity = executeUpdate(entity, deletedBy); + em.merge(currentEntity); + } else { + em.remove(attach(entity)); + } + } + + /** + * attaches the given entity + * + * @param the type of the entity to handle + * @param entity the entity to attach + * @return the entity after it has been attached + */ + public T attach(T entity) { + return em.merge(entity); + } + + /** + * Refreshes an entity. + * + * @param the type of the entity to handle + * @param entity the entity after it has been refreshed. + */ + public void refresh(T entity) { + em.refresh(entity); + } + + /* *** filter methods *** */ + /** + * Creates a filter condition. + * + * @param the type of the entity to handle + * @param cb the CriteriaBuilder to use + * @param root the root to use + * @param filters the filters to use + * @return the created filter condition + */ + protected Predicate getFilterCondition(CriteriaBuilder cb, Root root, Map filters) { + return getFilterCondition(cb, root, filters, null); + } + + /** + * Creates a filter condition. + * + * @param the type of the entity to handle + * @param cb the CriteriaBuilder to use + * @param root the root to use + * @param filters the filters to use + * @param excludeFilters the exclude filters to apply. Entities which match these filters, are not taken into the result list. + * @return the created filter condition + */ + protected Predicate getFilterCondition(CriteriaBuilder cb, Root root, Map filters, Map excludeFilters) { + Predicate includeFilterCondition = getFilterCondition(null, cb, root, filters, true); + Predicate excludeFilterCondition = getFilterCondition(null, cb, root, excludeFilters, false); + + if ((includeFilterCondition == null) && (excludeFilterCondition == null)) { + return null; + } else if (includeFilterCondition == null) { + return excludeFilterCondition; + } else if (excludeFilterCondition == null) { + return includeFilterCondition; + } else { + return cb.and(includeFilterCondition, excludeFilterCondition); + } + } + + /** + * extends the given filterCondition by the additional filters + * + * @param the type of the entity to handle + * @param filterCondition the current filter condition + * @param cb the criteria builder to use + * @param root the root of the object to search for + * @param filters the filters to apply + * @param include if set to true, the filter is used as include filter (equals, in). If set to false, the filter is inverted and used as exclude + * filter (not equals, not in etc) + * @return the created filter condition + */ + protected Predicate getFilterCondition(Predicate filterCondition, CriteriaBuilder cb, Root root, Map filters, boolean include) { + String wildCard = "%"; + if (filters != null) { + for (Map.Entry filter : filters.entrySet()) { + if (!"".equals(filter.getValue())) { + Path path = getPathElement(root, filter); + + // check for differnt types + // 1st String, either from Enum Status or from other free text string + if (filter.getValue() == null) { + Predicate predicate; + if (include) { + predicate = cb.isNull(path); + } else { + predicate = cb.isNotNull(path); } - if (endDateable.getValidity().getValidFrom() == null) { - endDateable.getValidity().setValidFrom(DateUtil.getCurrentTimeInUTC()); - } - } - em.persist(entity); - return entity; - } + filterCondition = addFilterCondition(cb, filterCondition, predicate); + } else if (String.class.equals(filter.getValue().getClass())) { + switch (filter.getKey()) { + default: + String filterValue = filter.getValue().toString(); + Predicate predicate; + if (filterValue.equals("{NULL}")) { + if (include) { + predicate = cb.isNull(path); + } else { + predicate = cb.isNotNull(path); + } + } else { + filterValue = filterValue.trim(); + filterValue = filterValue.replace("?", "_"); + filterValue = filterValue.replace("*", "%"); - /** - * updates an existing entity. - * - * @param the type of the entity to handle - * @param entity the entity to update - * @param updatedBy the username to apply write into the audit history - * @return the entity after it has been written to the database. - * @throws ControllerException if the the updates fails - */ - private T executeUpdate(T entity, String updatedBy) throws ControllerException { - T currentEntity = attach(entity); - if (Auditable.class.isAssignableFrom(currentEntity.getClass())) { - Audit audit = ((Auditable) currentEntity).getAudit(); - ((Auditable) currentEntity).setAudit(applyAuditChanges(audit, false, updatedBy)); - } - - if (EndDateable.class.isAssignableFrom(currentEntity.getClass())) { - // end date existing entity - EndDateable endDateable = (EndDateable) currentEntity; - Validity validity = endDateable.getValidity(); - if (validity == null) { - validity = new Validity(); - validity.setValidFrom(DateUtil.getCurrentTimeInUTC()); - } - validity.setValidTo(DateUtil.getCurrentTimeInUTC()); - - endDateable.setValidity(validity); - // and create new entity instead - } - - return currentEntity; - } - - /** - * updates an existing entity. - * - * @param the type of the entity to handle - * @param entity the entity to update - * @param updatedBy the username to apply write into the audit history - * @return the entity after it has been written to the database. - * @throws ControllerException if the the updates fails - */ - @TransactionAttribute(TransactionAttributeType.REQUIRED) - @Transactional - @Lock(LockType.WRITE) - public T update(T entity, String updatedBy) throws ControllerException { - T currentEntity = executeUpdate(entity, updatedBy); - if (EndDateable.class.isAssignableFrom(entity.getClass())) { - T newEntity = EntityUtil.cloneToNewEntity(currentEntity); - em.merge(currentEntity); - return create(newEntity, updatedBy); - } else { - // if it is not enddatable, just update it (already done above) - // and save it - return em.merge(currentEntity); - } - } - - /** - * Deletes an entity from the database. If the entity implements the {@link EndDateable} interface, the entity is not deleted but marked as - * deleted (end date set). - * - * @param the type of the entity to handle - * @param entity the entity to delete - * @param deletedBy the username to apply write into the audit history - * @throws ControllerException if the deletion fails. - */ - @TransactionAttribute(TransactionAttributeType.REQUIRED) - @Transactional - @Lock(LockType.WRITE) - public void delete(T entity, String deletedBy) throws ControllerException { - // if the entity is endDateable just set the validToDate to now intead of executing the deletion - if (EndDateable.class.isAssignableFrom(entity.getClass())) { - T currentEntity = executeUpdate(entity, deletedBy); - em.merge(currentEntity); - } else { - em.remove(attach(entity)); - } - } - - /** - * attaches the given entity - * - * @param the type of the entity to handle - * @param entity the entity to attach - * @return the entity after it has been attached - */ - public T attach(T entity) { - return em.merge(entity); - } - - /** - * Refreshes an entity. - * - * @param the type of the entity to handle - * @param entity the entity after it has been refreshed. - */ - public void refresh(T entity) { - em.refresh(entity); - } - - /* *** filter methods *** */ - /** - * Creates a filter condition. - * - * @param the type of the entity to handle - * @param cb the CriteriaBuilder to use - * @param root the root to use - * @param filters the filters to use - * @return the created filter condition - */ - protected Predicate getFilterCondition(CriteriaBuilder cb, Root root, Map filters) { - return getFilterCondition(cb, root, filters, null); - } - - /** - * Creates a filter condition. - * - * @param the type of the entity to handle - * @param cb the CriteriaBuilder to use - * @param root the root to use - * @param filters the filters to use - * @param excludeFilters the exclude filters to apply. Entities which match these filters, are not taken into the result list. - * @return the created filter condition - */ - protected Predicate getFilterCondition(CriteriaBuilder cb, Root root, Map filters, Map excludeFilters) { - Predicate includeFilterCondition = getFilterCondition(null, cb, root, filters, true); - Predicate excludeFilterCondition = getFilterCondition(null, cb, root, excludeFilters, false); - - if ((includeFilterCondition == null) && (excludeFilterCondition == null)) { - return null; - } else if (includeFilterCondition == null) { - return excludeFilterCondition; - } else if (excludeFilterCondition == null) { - return includeFilterCondition; - } else { - return cb.and(includeFilterCondition, excludeFilterCondition); - } - } - - /** - * extends the given filterCondition by the additional filters - * - * @param the type of the entity to handle - * @param filterCondition the current filter condition - * @param cb the criteria builder to use - * @param root the root of the object to search for - * @param filters the filters to apply - * @param include if set to true, the filter is used as include filter (equals, in). If set to false, the filter is inverted and used as exclude - * filter (not equals, not in etc) - * @return the created filter condition - */ - protected Predicate getFilterCondition(Predicate filterCondition, CriteriaBuilder cb, Root root, Map filters, boolean include) { - String wildCard = "%"; - if (filters != null) { - for (Map.Entry filter : filters.entrySet()) { - if (!"".equals(filter.getValue())) { - Path path = getPathElement(root, filter); - - // check for differnt types - // 1st String, either from Enum Status or from other free text string - if (filter.getValue() == null) { - Predicate predicate; - if (include) { - predicate = cb.isNull(path); - } else { - predicate = cb.isNotNull(path); - } - filterCondition = addFilterCondition(cb, filterCondition, predicate); - } else if (String.class.equals(filter.getValue().getClass())) { - switch (filter.getKey()) { - default: - String filterValue = filter.getValue().toString(); - Predicate predicate; - if (filterValue.equals("{NULL}")) { - if (include) { - predicate = cb.isNull(path); - } else { - predicate = cb.isNotNull(path); - } - } else { - filterValue = filterValue.trim(); - filterValue = filterValue.replace("?", "_"); - filterValue = filterValue.replace("*", "%"); - - String[] values = filterValue.split("\\s+"); // split by whitespaces - Predicate[] partSearchPredicates = new Predicate[values.length]; - for (int i = 0; i < values.length; i++) { - String value = wildCard + values[i] + wildCard; - if (include) { - partSearchPredicates[i] = cb.like(cb.upper(path), value.toUpperCase(Locale.US), '\\'); - } else { - partSearchPredicates[i] = cb.notLike(cb.upper(path), value.toUpperCase(Locale.US), '\\'); - } - } - predicate = cb.and(partSearchPredicates); // all parts must be available - } - filterCondition = addFilterCondition(cb, filterCondition, predicate); - } - } // 2nd for arrays, received from e.g. project selections - else if (filter.getValue().getClass().isArray()) { - Predicate condition = null; - Object[] values = (Object[]) filter.getValue(); - if (values.length > 0) { - for (Object value : values) { - if (include) { - Predicate equalPredicate = cb.equal(path, value); - if (condition == null) { - condition = equalPredicate; - } else { - condition = cb.or(condition, equalPredicate); - } - } else { - Predicate equalPredicate = cb.notEqual(path, value); - if (condition == null) { - condition = equalPredicate; - } else { - condition = cb.and(condition, equalPredicate); - } - } - } - filterCondition = addFilterCondition(cb, filterCondition, condition); - } - } else { // at last object comparison - if (include) { - filterCondition = addFilterCondition(cb, filterCondition, cb.equal(path, filter.getValue())); - } else { - filterCondition = addFilterCondition(cb, filterCondition, cb.notEqual(path, filter.getValue())); - } + String[] values = filterValue.split("\\s+"); // split by whitespaces + Predicate[] partSearchPredicates = new Predicate[values.length]; + for (int i = 0; i < values.length; i++) { + String value = wildCard + values[i] + wildCard; + if (include) { + partSearchPredicates[i] = cb.like(cb.upper(path), value.toUpperCase(Locale.US), '\\'); + } else { + partSearchPredicates[i] = cb.notLike(cb.upper(path), value.toUpperCase(Locale.US), '\\'); } + } + predicate = cb.and(partSearchPredicates); // all parts must be available } + filterCondition = addFilterCondition(cb, filterCondition, predicate); } - } - return filterCondition; - } - - protected Predicate getFilterCondition(CriteriaBuilder cb, Root root, List searchFilter) throws ControllerException { - return getFilterCondition(null, cb, root, searchFilter); - } - - protected Predicate getFilterCondition(final Predicate filterCondition, CriteriaBuilder cb, Root root, List searchFilter) throws ControllerException { - Predicate returnCondition = filterCondition; - for (SearchFilter currentFilter : searchFilter) { - if (currentFilter.getComparator() == null) { - throw new ControllerException(ControllerException.INTERNAL_ERROR, "Comparator must not be null"); + } // 2nd for arrays, received from e.g. project selections + else if (filter.getValue().getClass().isArray()) { + Predicate condition = null; + Object[] values = (Object[]) filter.getValue(); + if (values.length > 0) { + for (Object value : values) { + if (include) { + Predicate equalPredicate = cb.equal(path, value); + if (condition == null) { + condition = equalPredicate; + } else { + condition = cb.or(condition, equalPredicate); + } + } else { + Predicate equalPredicate = cb.notEqual(path, value); + if (condition == null) { + condition = equalPredicate; + } else { + condition = cb.and(condition, equalPredicate); + } + } + } + filterCondition = addFilterCondition(cb, filterCondition, condition); } - if (currentFilter.getFieldName() == null) { - throw new ControllerException(ControllerException.INTERNAL_ERROR, "Fieldname must not be null"); - } - - Path path = getPathElement(root, currentFilter); - - if (currentFilter.getSearchValue() == null) { - Predicate predicate; - switch (currentFilter.getComparator()) { - case EQUAL: - predicate = cb.isNull(path); - break; - case NOT_EQUAL: - predicate = cb.isNotNull(path); - default: - throw new ControllerException(ControllerException.INTERNAL_ERROR, currentFilter.getComparator() + " not supported for searchValue null"); - } - returnCondition = addFilterCondition(cb, returnCondition, predicate); - } else if (currentFilter.getSearchValue() instanceof String) { - String searchValue = (String) currentFilter.getSearchValue(); - String wildCard = "%"; - Predicate predicate; - switch (currentFilter.getComparator()) { - case EQUAL: - predicate = cb.equal(path, searchValue); - break; - case NOT_EQUAL: - predicate = cb.notEqual(path, searchValue); - break; - case CONTAINS: - String likeValue = wildCard + searchValue.toUpperCase(Locale.US) + wildCard; - predicate = cb.like(cb.upper(path), likeValue, '\\'); - break; - default: - throw new ControllerException(ControllerException.INTERNAL_ERROR, currentFilter.getComparator() + "not support for searchValue " + searchValue); - } - returnCondition = addFilterCondition(cb, returnCondition, predicate); - } else if (currentFilter.getSearchValue() instanceof Date) { - Date searchValue = (Date) currentFilter.getSearchValue(); - Predicate predicate; - Path datePath = root.get(currentFilter.getFieldName()); - switch (currentFilter.getComparator()) { - case EQUAL: - predicate = cb.equal(datePath, searchValue); - break; - case NOT_EQUAL: - predicate = cb.notEqual(datePath, searchValue); - break; - case LESS_THAN: - predicate = cb.lessThan(datePath, searchValue); - break; - case LESS_OR_EQUAL_THAN: - predicate = cb.lessThanOrEqualTo(datePath, searchValue); - break; - case GREATER_THAN: - predicate = cb.greaterThan(datePath, searchValue); - break; - case GREATER_OR_EQUAL_THAN: - predicate = cb.greaterThanOrEqualTo(datePath, searchValue); - break; - default: - throw new ControllerException(ControllerException.INTERNAL_ERROR, currentFilter.getComparator() + "not support for searchValue " + searchValue); - } - returnCondition = addFilterCondition(cb, returnCondition, predicate); - } else if (currentFilter.getSearchValue() instanceof Boolean) { - Boolean searchValue = (Boolean) currentFilter.getSearchValue(); - Path booleanPath = root.get(currentFilter.getFieldName()); - Predicate predicate; - switch (currentFilter.getComparator()) { - case EQUAL: - predicate = cb.equal(booleanPath, searchValue); - break; - case NOT_EQUAL: - predicate = cb.notEqual(booleanPath, searchValue); - break; - default: - throw new ControllerException(ControllerException.INTERNAL_ERROR, currentFilter.getComparator() + "not support for searchValue " + searchValue); - } - returnCondition = addFilterCondition(cb, returnCondition, predicate); - } else if (currentFilter.getSearchValue() instanceof UUID) { - UUID searchValue = (UUID) currentFilter.getSearchValue(); - Path booleanPath = root.get(currentFilter.getFieldName()); - Predicate predicate; - switch (currentFilter.getComparator()) { - case EQUAL: - predicate = cb.equal(booleanPath, searchValue); - break; - case NOT_EQUAL: - predicate = cb.notEqual(booleanPath, searchValue); - break; - default: - throw new ControllerException(ControllerException.INTERNAL_ERROR, currentFilter.getComparator() + "not support for searchValue " + searchValue); - } - returnCondition = addFilterCondition(cb, returnCondition, predicate); + } else { // at last object comparison + if (include) { + filterCondition = addFilterCondition(cb, filterCondition, cb.equal(path, filter.getValue())); } else { - throw new ControllerException(ControllerException.INTERNAL_ERROR, "Filter for " + currentFilter.getSearchValue().getClass().getSimpleName() + " not yet implemented"); + filterCondition = addFilterCondition(cb, filterCondition, cb.notEqual(path, filter.getValue())); } - } // for all filters - - return returnCondition; + } + } + } } + return filterCondition; + } - /** - * Adds a filter condition to an existing condition - * - * @param cb the builder to use - * @param filterCondition the existing filter condition - * @param addCondition the condition to add to the existing condition. - * @return an updated filter condition. - */ - protected Predicate addFilterCondition(CriteriaBuilder cb, Predicate filterCondition, Predicate addCondition) { - if (addCondition == null) { - return filterCondition; - } - if (filterCondition == null) { - filterCondition = addCondition; - } else { - filterCondition = cb.and(filterCondition, addCondition); - } - return filterCondition; - } + protected Predicate getFilterCondition(CriteriaBuilder cb, Root root, List searchFilter) throws ControllerException { + return getFilterCondition(null, cb, root, searchFilter); + } - private Path getPathElement(Root root, Map.Entry filter) { - String[] pathElements = StringUtils.split(filter.getKey(), '.'); - Path path = null; - for (String element : pathElements) { - if (path == null) { - path = root.get(element); - } else { - path = path.get(element); - } - } - return path; - } + protected Predicate getFilterCondition(final Predicate filterCondition, CriteriaBuilder cb, Root root, List searchFilter) throws ControllerException { + Predicate returnCondition = filterCondition; + for (SearchFilter currentFilter : searchFilter) { + if (currentFilter.getComparator() == null) { + throw new ControllerException(ControllerException.INTERNAL_ERROR, "Comparator must not be null"); + } + if (currentFilter.getFieldName() == null) { + throw new ControllerException(ControllerException.INTERNAL_ERROR, "Fieldname must not be null"); + } - private Path getPathElement(Root root, SearchFilter filter) { - String[] pathElements = StringUtils.split(filter.getFieldName(), '.'); - Path path = null; - for (String element : pathElements) { - if (path == null) { - path = root.get(element); - } else { - path = path.get(element); - } - } - return path; - } + Path path = getPathElement(root, currentFilter); - /** - * returns null, if the list is empty or null itself.Returns the one element if there is exactly one element in the list. Otherwise an exception - * is thrown - * - * @param the type of the entity to handle - * @param entityList the list to validate - * @return the one and only element of the provided list - * @throws ControllerException if the list contains more than one element. - */ - public T ensureSingleElement(List entityList) throws ControllerException { - if ((entityList == null) || (entityList.isEmpty())) { - return null; + if (currentFilter.getSearchValue() == null) { + Predicate predicate; + switch (currentFilter.getComparator()) { + case EQUAL: + predicate = cb.isNull(path); + break; + case NOT_EQUAL: + predicate = cb.isNotNull(path); + default: + throw new ControllerException(ControllerException.INTERNAL_ERROR, currentFilter.getComparator() + " not supported for searchValue null"); } - if (entityList.size() > 1) { - throw new ControllerException(ControllerException.CAUSE_TOO_MANY_ROWS, "More than one element found in list - expected exactly one"); + returnCondition = addFilterCondition(cb, returnCondition, predicate); + } else if (currentFilter.getSearchValue() instanceof String) { + String searchValue = (String) currentFilter.getSearchValue(); + String wildCard = "%"; + Predicate predicate; + switch (currentFilter.getComparator()) { + case EQUAL: + predicate = cb.equal(path, searchValue); + break; + case NOT_EQUAL: + predicate = cb.notEqual(path, searchValue); + break; + case CONTAINS: + String likeValue = wildCard + searchValue.toUpperCase(Locale.US) + wildCard; + predicate = cb.like(cb.upper(path), likeValue, '\\'); + break; + default: + throw new ControllerException(ControllerException.INTERNAL_ERROR, currentFilter.getComparator() + "not support for searchValue " + searchValue); } - return entityList.get(0); + returnCondition = addFilterCondition(cb, returnCondition, predicate); + } else if (currentFilter.getSearchValue() instanceof Date) { + Date searchValue = (Date) currentFilter.getSearchValue(); + Predicate predicate; + Path datePath = root.get(currentFilter.getFieldName()); + switch (currentFilter.getComparator()) { + case EQUAL: + predicate = cb.equal(datePath, searchValue); + break; + case NOT_EQUAL: + predicate = cb.notEqual(datePath, searchValue); + break; + case LESS_THAN: + predicate = cb.lessThan(datePath, searchValue); + break; + case LESS_OR_EQUAL_THAN: + predicate = cb.lessThanOrEqualTo(datePath, searchValue); + break; + case GREATER_THAN: + predicate = cb.greaterThan(datePath, searchValue); + break; + case GREATER_OR_EQUAL_THAN: + predicate = cb.greaterThanOrEqualTo(datePath, searchValue); + break; + default: + throw new ControllerException(ControllerException.INTERNAL_ERROR, currentFilter.getComparator() + "not support for searchValue " + searchValue); + } + returnCondition = addFilterCondition(cb, returnCondition, predicate); + } else if (currentFilter.getSearchValue() instanceof Boolean) { + Boolean searchValue = (Boolean) currentFilter.getSearchValue(); + Path booleanPath = root.get(currentFilter.getFieldName()); + Predicate predicate; + switch (currentFilter.getComparator()) { + case EQUAL: + predicate = cb.equal(booleanPath, searchValue); + break; + case NOT_EQUAL: + predicate = cb.notEqual(booleanPath, searchValue); + break; + default: + throw new ControllerException(ControllerException.INTERNAL_ERROR, currentFilter.getComparator() + "not support for searchValue " + searchValue); + } + returnCondition = addFilterCondition(cb, returnCondition, predicate); + } else if (currentFilter.getSearchValue() instanceof UUID) { + UUID searchValue = (UUID) currentFilter.getSearchValue(); + Path booleanPath = root.get(currentFilter.getFieldName()); + Predicate predicate; + switch (currentFilter.getComparator()) { + case EQUAL: + predicate = cb.equal(booleanPath, searchValue); + break; + case NOT_EQUAL: + predicate = cb.notEqual(booleanPath, searchValue); + break; + default: + throw new ControllerException(ControllerException.INTERNAL_ERROR, currentFilter.getComparator() + "not support for searchValue " + searchValue); + } + returnCondition = addFilterCondition(cb, returnCondition, predicate); + } else { + throw new ControllerException(ControllerException.INTERNAL_ERROR, "Filter for " + currentFilter.getSearchValue().getClass().getSimpleName() + " not yet implemented"); + } + } // for all filters + + return returnCondition; + } + + /** + * Adds a filter condition to an existing condition + * + * @param cb the builder to use + * @param filterCondition the existing filter condition + * @param addCondition the condition to add to the existing condition. + * @return an updated filter condition. + */ + protected Predicate addFilterCondition(CriteriaBuilder cb, Predicate filterCondition, Predicate addCondition) { + if (addCondition == null) { + return filterCondition; } + if (filterCondition == null) { + filterCondition = addCondition; + } else { + filterCondition = cb.and(filterCondition, addCondition); + } + return filterCondition; + } + + private Path getPathElement(Root root, Map.Entry filter) { + String[] pathElements = StringUtils.split(filter.getKey(), '.'); + Path path = null; + for (String element : pathElements) { + if (path == null) { + path = root.get(element); + } else { + path = path.get(element); + } + } + return path; + } + + private Path getPathElement(Root root, SearchFilter filter) { + String[] pathElements = StringUtils.split(filter.getFieldName(), '.'); + Path path = null; + for (String element : pathElements) { + if (path == null) { + path = root.get(element); + } else { + path = path.get(element); + } + } + return path; + } + + /** + * returns null, if the list is empty or null itself.Returns the one element if there is exactly one element in the list. Otherwise an exception is + * thrown + * + * @param the type of the entity to handle + * @param entityList the list to validate + * @return the one and only element of the provided list + * @throws ControllerException if the list contains more than one element. + */ + public T ensureSingleElement(List entityList) throws ControllerException { + if ((entityList == null) || (entityList.isEmpty())) { + return null; + } + if (entityList.size() > 1) { + throw new ControllerException(ControllerException.CAUSE_TOO_MANY_ROWS, "More than one element found in list - expected exactly one"); + } + return entityList.get(0); + } } diff --git a/db/src/main/java/de/muehlencord/shared/db/StandardController.java b/db/src/main/java/de/muehlencord/shared/db/StandardController.java index b7c3301..0352124 100644 --- a/db/src/main/java/de/muehlencord/shared/db/StandardController.java +++ b/db/src/main/java/de/muehlencord/shared/db/StandardController.java @@ -15,22 +15,7 @@ */ package de.muehlencord.shared.db; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.ejb.Lock; -import javax.ejb.LockType; import javax.ejb.Stateless; -import javax.persistence.EntityGraph; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Order; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; -import org.apache.commons.lang3.ArrayUtils; /** * @@ -39,90 +24,6 @@ import org.apache.commons.lang3.ArrayUtils; @Stateless public class StandardController extends CommonAbstractController { - @Lock(LockType.READ) - public T find(Class clazz, Object id) { - return em.find(clazz, id); - } - private EntityGraph getEntityGraph(Class clazz, String... subGraphItems) { - EntityGraph graph = this.em.createEntityGraph(clazz); - if (subGraphItems != null) { - for (String subGraphItem : subGraphItems) { - graph.addSubgraph(subGraphItem); - } - } - return graph; - } - - @Lock(LockType.READ) - public T find(Class clazz, Object id, String... subGraphItems) { - EntityGraph graph = getEntityGraph(clazz, subGraphItems); - Map hints = new HashMap<>(); - hints.put("javax.persistence.loadgraph", graph); - - T entity = (T) em.find(clazz, id, hints); - return entity; - } - - @Lock(LockType.READ) - public List find(Class clazz, Map filters, List orderFields, String... subGraphItems) { - return find(clazz, filters, orderFields, 0, 0, subGraphItems); - } - - @Lock(LockType.READ) - public List find(Class clazz, Map filters, List orderFields, int limit, int offset, String... subGraphItems) { - final CriteriaBuilder cb = em.getCriteriaBuilder(); - final CriteriaQuery criteria = cb.createQuery(clazz); - - final Root r = criteria.from(clazz); - Predicate filterCondition = getFilterCondition(cb, r, filters); - if (filterCondition != null) { - criteria.where(filterCondition); - } - List orderList = new ArrayList<>(); - orderFields.stream().forEachOrdered(field -> orderList.add(cb.asc(r.get(field)))); - final TypedQuery query = em.createQuery(criteria.orderBy(orderList)) - .setFirstResult(offset); - if (limit > 0) { - query.setMaxResults(limit); - } - - EntityGraph graph = getEntityGraph(clazz, subGraphItems); - query.setHint("javax.persistence.loadgraph", graph); - - return query.getResultList(); - } - - @Lock(LockType.READ) - public List find(Class clazz, List filters, List orderFields, String... subGraphItems) throws ControllerException { - return find(clazz, filters, orderFields, 0, 0); - } - - @Lock(LockType.READ) - public List find(Class clazz, List filters, List orderFields, int limit, int offset, String... subGraphItems) throws ControllerException { - final CriteriaBuilder cb = em.getCriteriaBuilder(); - final CriteriaQuery criteria = cb.createQuery(clazz); - - final Root r = criteria.from(clazz); - Predicate filterCondition = getFilterCondition(cb, r, filters); - if (filterCondition != null) { - criteria.where(filterCondition); - } - List orderList = new ArrayList<>(); - if (orderFields != null) { - orderFields.stream().forEachOrdered(field -> orderList.add(cb.asc(r.get(field)))); - } - final TypedQuery query = em.createQuery(criteria.orderBy(orderList)).setFirstResult(offset); - if (limit > 0) { - query.setMaxResults(limit); - } - - if (!ArrayUtils.isEmpty(subGraphItems)) { - EntityGraph graph = getEntityGraph(clazz, subGraphItems); - query.setHint("javax.persistence.loadgraph", graph); - } - - return query.getResultList(); - } }