From 7424e2fe238f8220fdf4fd54726c6952a50225d7 Mon Sep 17 00:00:00 2001 From: Igor Dianov <igor@dianov.com> Date: Tue, 10 Sep 2019 21:05:09 -0700 Subject: [PATCH] chore: remove obsolete JpaQueryBuilder class --- .../schema/impl/JpaPredicateBuilder.java | 44 +- .../query/schema/impl/JpaQueryBuilder.java | 456 ------------------ .../impl/QraphQLJpaBaseDataFetcher.java | 2 +- 3 files changed, 32 insertions(+), 470 deletions(-) delete mode 100644 graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/JpaQueryBuilder.java diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/JpaPredicateBuilder.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/JpaPredicateBuilder.java index f5aa6f6b0..f823e021a 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/JpaPredicateBuilder.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/JpaPredicateBuilder.java @@ -28,7 +28,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Date; -import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; import java.util.Set; import java.util.UUID; @@ -74,23 +75,40 @@ */ class JpaPredicateBuilder { + public static final Map<Class<?>, Class<?>> WRAPPERS_TO_PRIMITIVES = new HashMap<Class<?>, Class<?>>(); + public static final Map<Class<?>, Class<?>> PRIMITIVES_TO_WRAPPERS = new HashMap<Class<?>, Class<?>>(); + + static { + PRIMITIVES_TO_WRAPPERS.put(boolean.class, Boolean.class); + PRIMITIVES_TO_WRAPPERS.put(byte.class, Byte.class); + PRIMITIVES_TO_WRAPPERS.put(char.class, Character.class); + PRIMITIVES_TO_WRAPPERS.put(double.class, Double.class); + PRIMITIVES_TO_WRAPPERS.put(float.class, Float.class); + PRIMITIVES_TO_WRAPPERS.put(int.class, Integer.class); + PRIMITIVES_TO_WRAPPERS.put(long.class, Long.class); + PRIMITIVES_TO_WRAPPERS.put(short.class, Short.class); + PRIMITIVES_TO_WRAPPERS.put(void.class, Void.class); + + WRAPPERS_TO_PRIMITIVES.put(Boolean.class, boolean.class); + WRAPPERS_TO_PRIMITIVES.put(Byte.class, byte.class); + WRAPPERS_TO_PRIMITIVES.put(Character.class, char.class); + WRAPPERS_TO_PRIMITIVES.put(Double.class, double.class); + WRAPPERS_TO_PRIMITIVES.put(Float.class, float.class); + WRAPPERS_TO_PRIMITIVES.put(Integer.class, int.class); + WRAPPERS_TO_PRIMITIVES.put(Long.class, long.class); + WRAPPERS_TO_PRIMITIVES.put(Short.class, short.class); + WRAPPERS_TO_PRIMITIVES.put(Void.class, void.class); + } + private final CriteriaBuilder cb; - private final EnumSet<Logical> globalOptions; - /** - * Field name can be prepended with (comma separated list of local options) - * if field is prepended with (), even without options, any global options - * avoided and defaults are used.Defaults: for numbers and booleas - - * equality; for strings case insensitive beginning matches; for dates - * greater or equal; - * + * JpaPredicateBuilder constructor + * * @param cb - * @param globalOptions */ - public JpaPredicateBuilder(CriteriaBuilder cb, EnumSet<Logical> globalOptions) { + public JpaPredicateBuilder(CriteriaBuilder cb) { this.cb = cb; - this.globalOptions = globalOptions; } protected Predicate addOrNull(Path<?> root, Predicate p) { @@ -656,7 +674,7 @@ private Predicate getTypedPredicate(From<?,?> from, Path<?> field, PredicateFilt PredicateFilter predicateFilter = new PredicateFilter(filter.getField(), value, criterias); if (type.isPrimitive()) - type = JpaQueryBuilder.PRIMITIVES_TO_WRAPPERS.get(type); + type = PRIMITIVES_TO_WRAPPERS.get(type); if (type.equals(String.class)) { return getStringPredicate((Path<String>)field, filter); } diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/JpaQueryBuilder.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/JpaQueryBuilder.java deleted file mode 100644 index 0279b07e5..000000000 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/JpaQueryBuilder.java +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright 2017 IntroPro Ventures Inc. and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.introproventures.graphql.jpa.query.schema.impl; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import javax.persistence.EntityManager; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.From; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.Order; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; -import javax.persistence.metamodel.Attribute; -import javax.persistence.metamodel.EntityType; -import javax.persistence.metamodel.Metamodel; -import javax.persistence.metamodel.PluralAttribute; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.introproventures.graphql.jpa.query.annotation.GraphQLDefaultOrderBy; - -/** - * Jpa Criteria Query Builder class used to apply predicate filters and orders and build Criteria Query. - * - * @param <T> - * @todo make inner joins for compound fields garanteed to have joined data - */ -class JpaQueryBuilder<T> { - - private final Logger logger = LoggerFactory.getLogger(this.getClass()); - - private final Class<T> clazz; - - private final CriteriaBuilder criteriaBuilder; - - private final JpaPredicateBuilder predicateBuilder; - - private final EnumSet<Logical> options; - - private final Metamodel metamodel; - - private final List<PredicateFilter> filters = new ArrayList<>(); - - private final LinkedHashMap<String, Boolean> orders = new LinkedHashMap<>(); - - public static final Map<Class<?>, Class<?>> WRAPPERS_TO_PRIMITIVES = new HashMap<Class<?>, Class<?>>(); - public static final Map<Class<?>, Class<?>> PRIMITIVES_TO_WRAPPERS = new HashMap<Class<?>, Class<?>>(); - - static { - PRIMITIVES_TO_WRAPPERS.put(boolean.class, Boolean.class); - PRIMITIVES_TO_WRAPPERS.put(byte.class, Byte.class); - PRIMITIVES_TO_WRAPPERS.put(char.class, Character.class); - PRIMITIVES_TO_WRAPPERS.put(double.class, Double.class); - PRIMITIVES_TO_WRAPPERS.put(float.class, Float.class); - PRIMITIVES_TO_WRAPPERS.put(int.class, Integer.class); - PRIMITIVES_TO_WRAPPERS.put(long.class, Long.class); - PRIMITIVES_TO_WRAPPERS.put(short.class, Short.class); - PRIMITIVES_TO_WRAPPERS.put(void.class, Void.class); - - WRAPPERS_TO_PRIMITIVES.put(Boolean.class, boolean.class); - WRAPPERS_TO_PRIMITIVES.put(Byte.class, byte.class); - WRAPPERS_TO_PRIMITIVES.put(Character.class, char.class); - WRAPPERS_TO_PRIMITIVES.put(Double.class, double.class); - WRAPPERS_TO_PRIMITIVES.put(Float.class, float.class); - WRAPPERS_TO_PRIMITIVES.put(Integer.class, int.class); - WRAPPERS_TO_PRIMITIVES.put(Long.class, long.class); - WRAPPERS_TO_PRIMITIVES.put(Short.class, short.class); - WRAPPERS_TO_PRIMITIVES.put(Void.class, void.class); - } - - /** - * Creates new instance from JPA entity class. - * - * @param em - * @param clazz must be JPA Entity annotated - */ - public JpaQueryBuilder(EntityManager em, Class<T> clazz) { - this.clazz = clazz; - this.criteriaBuilder = em.getCriteriaBuilder(); - //this.query = cb.createQuery(clazz); - //this.root = query.from(clazz); - this.options = EnumSet.noneOf(Logical.class); - this.predicateBuilder = new JpaPredicateBuilder(criteriaBuilder, options); - this.metamodel = em.getMetamodel(); - } - - /** - * Creates new instance from existing query - * - * @param em - * @param query - */ - public JpaQueryBuilder(EntityManager em, CriteriaQuery<T> query) { - this.criteriaBuilder = em.getCriteriaBuilder(); - this.clazz = query.getResultType(); - //this.query = query; - this.options = EnumSet.noneOf(Logical.class); - this.predicateBuilder = new JpaPredicateBuilder(criteriaBuilder, options); - this.metamodel = em.getMetamodel(); - } - - /** - * Checks if filter fieldname is valid for given query. Throws verbose - * IllegalArgumentException if not. If checkValue = true, checks if filter - * value is of type of the field or can be converted. This is to avoid - * unnecessarry joins if filter is invalid - * - * @param filter - * @param checkValue if false than value is not checked - * @return true if filter is valid - * @throws IllegalArgumentException - */ - private boolean checkFilterValid(PredicateFilter filter, boolean checkValue) { - Class<?> fieldType = getJavaType(filter.getField()); - // arrays - if (filter.getValue().getClass().isArray()) { - Object[] arr = (Object[]) filter.getValue(); - if (arr.length == 0) { - return false; - } else { - return !checkValue || ((Object[]) filter.getValue())[0].getClass().equals(fieldType); - } - } - if (fieldType.isPrimitive()) { - return !checkValue || PRIMITIVES_TO_WRAPPERS.get(fieldType).isInstance(filter.getValue()); - } else { - return !checkValue || fieldType.isInstance(filter.getValue()); - } - } - - /** - * This clumsy code is just to get the class of plural attribute mapping - * - * @param et - * @param fieldName - * @return - */ - private Class<?> getPluralJavaType(EntityType<?> et, String fieldName) { - for (PluralAttribute<?,?,?> pa : et.getPluralAttributes()) { - if (pa.getName().equals(fieldName)) { - switch (pa.getCollectionType()) { - case COLLECTION: - return et.getCollection(fieldName).getElementType().getJavaType(); - case LIST: - return et.getList(fieldName).getElementType().getJavaType(); - case SET: - return et.getSet(fieldName).getElementType().getJavaType(); - case MAP: - throw new UnsupportedOperationException("Entity Map mapping unsupported for entity: " + et.getName() + " field name: " + fieldName); - } - } - } - throw new IllegalArgumentException("Field " + fieldName + " of entity " + et.getName() + " is not a plural attribute"); - } - - /** - * Returns Java type of the fieldName - * - * @param fieldName - * @return - * @throws IllegalArgumentException if fieldName isn't valid for given - * entity - */ - public Class<?> getJavaType(String fieldName) { - - String[] compoundField = fieldName.split("\\."); - EntityType<?> et = metamodel.entity(clazz); - - for (int i = 0; i < compoundField.length; i++) { - if (i < (compoundField.length - 1)) { - try { - Attribute<?,?> att = et.getAttribute(compoundField[i]); - if (att.isCollection()) { - et = metamodel.entity(getPluralJavaType(et, compoundField[i])); - } else { - et = metamodel.entity(et.getAttribute(compoundField[i]).getJavaType()); - } - } catch (IllegalArgumentException | IllegalStateException e) { - throw new IllegalArgumentException( - "Illegal field name " + fieldName + " (" + compoundField[i] + ") for root type " + clazz - ); - } - } else { - try { - return et.getAttribute(compoundField[i]).getJavaType(); - } catch (IllegalArgumentException | IllegalStateException e) { - throw new IllegalArgumentException( - "Illegal field name " + fieldName + " (" + compoundField[i] + ") for root type " + clazz - ); - } - } - } - return null; // should never be reached - } - - /** - * Adds filters to the query. Preserves existing filters. - * - * @param conditions - * @return this, itself for chain calls - */ - public JpaQueryBuilder<T> addFilters(List<PredicateFilter> conditions) { - if (conditions != null) { - for (PredicateFilter f : conditions) { - if (checkFilterValid(f, true)) { - filters.add(f); - } else { - logger.error("Could not apply filter for field: " + f.getField() + " value: " + f.getValue() + " of type: " + f.getValue().getClass()); - } - } - } - - // keep it sorted to avoid inner/outer joins messup - Collections.sort(filters); - - return this; - } - - /** - * Adds filter to the query. Preserves existing filters. - * - * @param conditions - * @return this, itself for chain calls - */ - public JpaQueryBuilder<T> addFilter(PredicateFilter filter) { - if (filter != null) { - if (checkFilterValid(filter, true)) { - filters.add(filter); - } else { - logger.error("Could not apply filter for field: " + filter.getField() + " value: " + filter.getValue() + " of type: " + filter.getValue().getClass()); - } - } - - // keep it sorted to avoid inner/outer joins messup - Collections.sort(filters); - - return this; - } - - /** - * @param from - * @param query - */ - private void applyFilters(Root<T> from, CriteriaQuery<?> query) { - - List<Predicate> predicates = new ArrayList<>(); - - for (PredicateFilter filter : filters) { - Path<?> path; - if (filter.getCriterias().contains(PredicateFilter.Criteria.IS_NULL)) { - path = getCompoundJoinedPath(from, filter.getField(), true); - } else { - path = getCompoundJoinedPath(from, filter.getField(), false); - } - - Predicate p = predicateBuilder.getPredicate(from, path, filter); - if (p != null) { - predicates.add(p); - } - } - // this does not work for Hibernate!!! - if (query.getRestriction() != null) { - predicates.add(query.getRestriction()); - } - if (options.contains(Logical.OR)) { - query.where(criteriaBuilder.or(predicates.toArray(new Predicate[0]))); - } else { - query.where(criteriaBuilder.and(predicates.toArray(new Predicate[0]))); - } - - } - - private void applyOrders(Root<T> from, CriteriaQuery<T> query) { - List<Order> orderList = new ArrayList<>(); - - for (Map.Entry<String, Boolean> me : orders.entrySet()) { - Path<?> path = getCompoundJoinedPath(from, me.getKey(), true); - if (me.getValue() == null || me.getValue().equals(true)) { - orderList.add(criteriaBuilder.asc(path)); - } else { - orderList.add(criteriaBuilder.desc(path)); - } - } - query.orderBy(orderList); - } - - /** - * Adds order by expressions to the tail of already existing orders of query - * - * @param orders - * @return - */ - public JpaQueryBuilder<T> addOrders(Map<String, Boolean> orders) { - - for (Map.Entry<String, Boolean> me : orders.entrySet()) { - checkFilterValid(new PredicateFilter(me.getKey(), "", EnumSet.noneOf(PredicateFilter.Criteria.class)), false); - this.orders.put(me.getKey(), me.getValue()); - } - return this; - } - - /** - * Adds order by expressions to the tail of already existing orders of query - * - * @param orders - * @return - */ - public JpaQueryBuilder<T> addOrder(String field, Boolean direction) { - - checkFilterValid(new PredicateFilter(field, "", EnumSet.noneOf(PredicateFilter.Criteria.class)), false); - this.orders.put(field, direction); - - return this; - } - - /** - * @param fieldName - * @return Path of compound field to the primitive type - */ - private Path<?> getCompoundJoinedPath(Root<T> rootPath, String fieldName, boolean outer) { - String[] compoundField = fieldName.split("\\."); - - Join<?,?> join; - - if (compoundField.length == 1) { - return rootPath.get(compoundField[0]); - } else { - join = reuseJoin(rootPath, compoundField[0], outer); - } - - for (int i = 1; i < compoundField.length; i++) { - if (i < (compoundField.length - 1)) { - join = reuseJoin(join, compoundField[i], outer); - } else { - return join.get(compoundField[i]); - } - } - - return null; - } - - // trying to find already existing joins to reuse - private Join<?,?> reuseJoin(From<?, ?> path, String fieldName, boolean outer) { - for (Join<?,?> join : path.getJoins()) { - if (join.getAttribute().getName().equals(fieldName)) { - if ((join.getJoinType() == JoinType.LEFT) == outer) { - logger.debug("Reusing existing join for field " + fieldName); - return join; - } - } - } - return outer ? path.join(fieldName, JoinType.LEFT) : path.join(fieldName); - } - - - /** - * Get sorting field - * - * @param clazz - * @return - * @throws IllegalArgumentException - * @throws IllegalAccessException - */ - private Field getSortAnnotation(Class<?> clazz) throws IllegalArgumentException, IllegalAccessException { - for (Field f : clazz.getDeclaredFields()) { - if (f.getAnnotation(GraphQLDefaultOrderBy.class) != null) { - return f; - } - } - //if not found, search in superclass. todo recursive search - for (Field f : clazz.getSuperclass().getDeclaredFields()) { - if (f.getAnnotation(GraphQLDefaultOrderBy.class) != null) { - return f; - } - } - return null; - } - - - /** - * Resulting query with filters and orders, if orders are empty, than makes - * default ascending ordering by root id to prevent paging confuses - * - * @return - */ - public CriteriaQuery<T> getQuery() { - CriteriaQuery<T> query = criteriaBuilder.createQuery(clazz); - Root<T> from = query.from(clazz); - applyFilters(from, query); - applyOrders(from, query); - - // add default ordering - if (query.getOrderList() == null || query.getOrderList().isEmpty()) { - EntityType<T> entityType = from.getModel(); - try { - Field sortField = getSortAnnotation(entityType.getBindableJavaType()); - if (sortField == null) - query.orderBy(criteriaBuilder.asc(from.get(entityType.getId(entityType.getIdType().getJavaType()).getName()))); - else { - GraphQLDefaultOrderBy order = sortField.getAnnotation(GraphQLDefaultOrderBy.class); - if (order.asc()) { - query.orderBy(criteriaBuilder.asc(from.get(sortField.getName()))); - } else { - query.orderBy(criteriaBuilder.desc(from.get(sortField.getName()))); - } - - } - } catch (Exception ex) { - logger.warn("In" + this.getClass().getName(), ex); - } - } - return query; - } - - /** - * @return - */ - public CriteriaQuery<Long> getCountQuery() { - CriteriaQuery<Long> q = criteriaBuilder.createQuery(Long.class); - Root<T> root = q.from(clazz); - q.select(criteriaBuilder.count(root)); - applyFilters(root, q); - return q; - } - - public EnumSet<Logical> getOptions() { - return options; - } - -} \ No newline at end of file diff --git a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java index 05790b16f..12f866270 100644 --- a/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java +++ b/graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/QraphQLJpaBaseDataFetcher.java @@ -743,7 +743,7 @@ private Predicate getLogicalPredicate(String fieldName, CriteriaBuilder cb, From } // Let's parse simple Criteria expressions, i.e. EQ, LIKE, etc. - JpaPredicateBuilder pb = new JpaPredicateBuilder(cb, EnumSet.of(Logical.AND)); + JpaPredicateBuilder pb = new JpaPredicateBuilder(cb); expressionValue.getObjectFields() .stream()