All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jwebmp.entityassist.querybuilder.QueryBuilder Maven / Gradle / Ivy

There is a newer version: 0.68.0.1
Show newest version
package com.jwebmp.entityassist.querybuilder;

import com.google.common.base.Strings;
import com.google.inject.Key;
import com.jwebmp.entityassist.BaseEntity;
import com.jwebmp.entityassist.enumerations.OrderByType;
import com.jwebmp.entityassist.querybuilder.builders.DefaultQueryBuilder;
import com.jwebmp.entityassist.querybuilder.builders.JoinExpression;
import com.jwebmp.guicedinjection.GuiceContext;
import com.jwebmp.guicedpersistence.services.ITransactionHandler;
import com.jwebmp.logger.LogFactory;
import com.oracle.jaxb21.PersistenceUnit;

import javax.persistence.*;
import javax.persistence.criteria.*;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.PluralAttribute;
import javax.persistence.metamodel.SingularAttribute;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;

import static com.jwebmp.entityassist.querybuilder.builders.IFilterExpression.*;
import static com.jwebmp.guicedpersistence.scanners.PersistenceServiceLoadersBinder.*;

@SuppressWarnings("unchecked")
public abstract class QueryBuilder, E extends BaseEntity, I extends Serializable>
		extends DefaultQueryBuilder
{
	/**
	 * The logger
	 */
	private static final Logger log = LogFactory.getLog(QueryBuilder.class.getName());
	/**
	 * Marks if this query is selected
	 */
	private boolean selected;
	/**
	 * Whether or not to detach after select
	 */
	private boolean detach;
	/**
	 * If the first result must be returned from a list
	 */
	private boolean returnFirst;

	/**
	 * Returns a long of the count for the given builder
	 *
	 * @return Long of results - generally never null
	 */
	public Long getCount()
	{
		if (!selected)
		{
			selectCount();
			select();
		}
		TypedQuery query = getEntityManager().createQuery(getCriteriaQuery());
		applyCache(query);
		Long j;
		try
		{
			j = query.getSingleResult();
			return j;
		}
		catch (NoResultException nre)
		{
			log.log(Level.WARNING, "Couldn''t find object with name : " + getEntityClass().getName() + "}\n", nre);
			return 0L;
		}
	}

	/**
	 * Prepares the select statement
	 *
	 * @return This
	 */
	@SuppressWarnings({"UnusedReturnValue", "Duplicates"})
	@NotNull
	private J select()
	{
		if (!selected)
		{

			getJoins().forEach(this::processJoins);
			if (!isDelete() && !isUpdate())
			{
				processCriteriaQuery();
			}
			else if (isDelete())
			{
				CriteriaDelete cq = getCriteriaDelete();
				List allWheres = new ArrayList<>(getFilters());
				Predicate[] preds = new Predicate[allWheres.size()];
				preds = allWheres.toArray(preds);
				cq.where(preds);
			}
			else if (isUpdate())
			{
				CriteriaUpdate cq = getCriteriaUpdate();
				List allWheres = new ArrayList<>(getFilters());
				Predicate[] preds = new Predicate[allWheres.size()];
				preds = allWheres.toArray(preds);
				cq.where(preds);
			}
		}
		selected = true;
		return (J) this;
	}

	/**
	 * Physically applies the cache attributes to the query
	 * 

* Adds cacheable, cache region, and sets persistence cache retrieve mode as use, and store mode as use * * @param query * The query to apply to */ private void applyCache(TypedQuery query) { if (!Strings.isNullOrEmpty(getCacheName())) { query.setHint("org.hibernate.cacheable", true); query.setHint("org.hibernate.cacheRegion", getCacheRegion()); query.setHint("javax.persistence.cache.retrieveMode", "USE"); query.setHint("javax.persistence.cache.storeMode", "USE"); } } /** * Builds up the criteria query to perform (Criteria Query Only) */ private void processCriteriaQuery() { CriteriaQuery cq = getCriteriaQuery(); List allWheres = new ArrayList<>(getFilters()); Predicate[] preds = new Predicate[allWheres.size()]; preds = allWheres.toArray(preds); getCriteriaQuery().where(preds); for (Expression p : getGroupBys()) { cq.groupBy(p); } for (Expression expression : getHavingExpressions()) { cq.having(expression); } if (!getOrderBys().isEmpty()) { List orderBys = new ArrayList<>(); getOrderBys().forEach((key, value) -> orderBys.add(processOrderBys(key, value))); cq.orderBy(orderBys); } if (getSelections().isEmpty()) { getCriteriaQuery().select(getRoot()); } else if (getSelections().size() > 1) { if (getConstruct() != null) { ArrayList aS = new ArrayList(getSelections()); Selection[] selections = aS.toArray(new Selection[0]); CompoundSelection cs = getCriteriaBuilder().construct(getConstruct(), selections); getCriteriaQuery().select(cs); } else { getCriteriaQuery().multiselect(new ArrayList(getSelections())); } } else { getSelections().forEach(a -> getCriteriaQuery().select(a)); } } /** * Processes the order bys into the given query * * @param key * The attribute to apply * @param value * The value to use */ private Order processOrderBys(Attribute key, OrderByType value) { switch (value) { case DESC: { if (isSingularAttribute(key)) { return getCriteriaBuilder().desc(getRoot().get((SingularAttribute) key)); } else if (isPluralOrMapAttribute(key)) { return getCriteriaBuilder().desc(getRoot().get((PluralAttribute) key)); } return getCriteriaBuilder().desc(getRoot().get((SingularAttribute) key)); } case ASC: default: { if (isSingularAttribute(key)) { return getCriteriaBuilder().asc(getRoot().get((SingularAttribute) key)); } else if (isPluralOrMapAttribute(key)) { return getCriteriaBuilder().asc(getRoot().get((PluralAttribute) key)); } return getCriteriaBuilder().asc(getRoot().get((SingularAttribute) key)); } } } /** * Returns the number of rows or an unsupported exception if there are no filters added * * @param updateFields * Allows to use the Criteria Update to run a bulk update on the table * * @return number of rows updated */ @SuppressWarnings({"UnusedReturnValue", "Duplicates"}) public int bulkUpdate(E updateFields, boolean allowEmpty) { if (!allowEmpty && getFilters().isEmpty()) { throw new UnsupportedOperationException("Calling the bulk update method with no filters. This will update the entire table."); } CriteriaUpdate update = getCriteriaUpdate(); Map updateFieldMap = getUpdateFieldMap(updateFields); if (updateFieldMap.isEmpty()) { log.warning("Nothing to update, ignore bulk update"); return 0; } for (Map.Entry entries : updateFieldMap.entrySet()) { SingularAttribute attributeName = entries.getKey(); Object value = entries.getValue(); if (attributeName != null) { update.set(attributeName.getName(), value); } } select(); boolean transactionAlreadyStarted = false; com.oracle.jaxb21.PersistenceUnit unit = GuiceContext.get(Key.get(PersistenceUnit.class, getEntityManagerAnnotation())); for (ITransactionHandler handler : GuiceContext.get(ITransactionHandlerReader)) { if (handler.transactionExists(getEntityManager(), unit)) { transactionAlreadyStarted = true; break; } } for (ITransactionHandler handler : GuiceContext.get(ITransactionHandlerReader)) { if (!transactionAlreadyStarted && handler.active(unit)) { handler.beginTransacation(false, getEntityManager(), unit); } } int results = getEntityManager().createQuery(update) .executeUpdate(); for (ITransactionHandler handler : GuiceContext.get(ITransactionHandlerReader)) { if (!transactionAlreadyStarted && handler.active(unit)) { handler.commitTransacation(false, getEntityManager(), unit); } } return results; } /** * Processors the join section * * @param executor * Processes the joins into the expression */ private void processJoins(JoinExpression executor) { Attribute value = executor.getAttribute(); JoinType jt = executor.getJoinType(); List onClause = new ArrayList<>(); if (executor.getOnBuilder() != null) { executor.getOnBuilder() .select(); onClause.addAll(executor.getOnBuilder() .getFilters()); } Join join; if (executor.getGeneratedRoot() == null) { join = getRoot().join(value.getName(), jt); } else { //join = getRoot().join(value.getName(), jt); join = executor.getGeneratedRoot(); } if (!onClause.isEmpty()) { join = join.on(onClause.toArray(new Predicate[]{})); } QueryBuilder key = executor.getExecutor(); if (key != null) { key.reset(join); key.select(); getSelections().addAll(key.getSelections()); getFilters().addAll(key.getFilters()); getOrderBys().putAll(key.getOrderBys()); } } /** * Returns the result set as a stream * * @param resultType * The result type * @param * The Class for the type to gerenify * * @return A stream of the type */ @SuppressWarnings({"Duplicates", "unused"}) public Stream getResultStream(Class resultType) { if (!selected) { select(); } TypedQuery query = getEntityManager().createQuery(getCriteriaQuery()); applyCache(query); if (getMaxResults() != null) { query.setMaxResults(getMaxResults()); } if (getFirstResults() != null) { query.setFirstResult(getFirstResults()); } return query.getResultStream(); } /** * Returns a non-distinct list and returns an empty optional if a non-unique-result exception is thrown * * @return An optional of the result */ public Optional get() { return get(this.returnFirst); } /** * Returns the first result returned * * @param returnFirst * If the first should be returned in the instance of many results * * @return Optional of the required object */ @NotNull public Optional get(boolean returnFirst) { this.returnFirst = returnFirst; return get(getEntityClass()); } /** * Returns a list (distinct or not) and returns an empty optional if returns a list, or will simply return the first result found from * a list with the same criteria * * @return Optional of the given class type (which should be a select column) */ @SuppressWarnings({"Duplicates", "unused"}) @NotNull public Optional get(@NotNull Class asType) { if (!selected) { select(); } TypedQuery query = getEntityManager().createQuery(getCriteriaQuery()); if (getMaxResults() != null) { query.setMaxResults(getMaxResults()); } if (getFirstResults() != null) { query.setFirstResult(getFirstResults()); } applyCache(query); T j; try { j = query.getSingleResult(); if (j == null) { return Optional.empty(); } if (BaseEntity.class.isAssignableFrom(j.getClass())) { ((BaseEntity) j) .setFake(false); } if (detach) { getEntityManager().detach(j); } return Optional.of(j); } catch (NoResultException | NullPointerException nre) { log.log(Level.FINER, "Couldn't find object : " + getEntityClass().getName() + "}", nre); return Optional.empty(); } catch (NonUniqueResultException nure) { if (isReturnFirst()) { query.setMaxResults(1); List returnedList = query.getResultList(); j = returnedList.get(0); if (j != null) { if (BaseEntity.class.isAssignableFrom(j.getClass())) { ((BaseEntity) j) .setFake(false); } if (detach) { getEntityManager().detach(j); } } return Optional.ofNullable(j); } else { log.log(Level.FINE, "Non Unique Result. Found too many for a get() for class : " + getEntityClass().getName() + "}. Get First Result disabled. Returning empty", nure); return Optional.empty(); } } } /** * If this builder is configured to return the first row * * @return If the first record must be returned */ @SuppressWarnings("WeakerAccess") public boolean isReturnFirst() { return returnFirst; } /** * If a Non-Unique Exception is thrown re-run the query as a list and return the first item * * @param returnFirst * if must return first * * @return J */ @SuppressWarnings({"unchecked", "unused"}) @NotNull public J setReturnFirst(boolean returnFirst) { this.returnFirst = returnFirst; return (J) this; } /** * Goes through the object looking for fields, returns a set where the field name is mapped to the object * * @param updateFields * Returns a map of field to update with the values * * @return A map of SingularAttribute and its object type */ @SuppressWarnings("WeakerAccess") @NotNull public Map getUpdateFieldMap(E updateFields) { Map map = new HashMap<>(); List fieldList = allFields(updateFields.getClass(), new ArrayList<>()); for (Field field : fieldList) { if (Modifier.isAbstract(field.getModifiers()) || Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()) || field.isAnnotationPresent( Id.class)) { continue; } field.setAccessible(true); try { Object o = field.get(updateFields); if (o != null) { String fieldName = field.getName(); String classPathReferenceName = updateFields.getClass() .getName() + "_"; Class clazz = Class.forName(classPathReferenceName); Field f = clazz.getField(fieldName); SingularAttribute at = (SingularAttribute) f.get(null); map.put(at, o); } } catch (IllegalAccessException | ClassNotFoundException | NoSuchFieldException e) { log.log(Level.SEVERE, "Unable to determine if field is populated or not", e); } } return map; } /** * Returns a list of entities from a distinct or non distinct list * * @return A list of entities returned */ public List getAll() { return getAll(getEntityClass()); } /** * Returns the list as the selected class type (for when specifying single select columns) * * @param returnClassType * Returns a list of a given column * @param * The type of the column returned * * @return The type of the column returned */ @SuppressWarnings({"Duplicates", "unused"}) @NotNull public List getAll(Class returnClassType) { if (!selected) { select(); } TypedQuery query = getEntityManager().createQuery(getCriteriaQuery()); applyCache(query); if (getMaxResults() != null) { query.setMaxResults(getMaxResults()); } if (getFirstResults() != null) { query.setFirstResult(getFirstResults()); } List j; j = query.getResultList(); if (!j.isEmpty()) { if (detach) { getEntityManager().detach(j); } if (BaseEntity.class.isAssignableFrom(j.get(0) .getClass())) { ((BaseEntity) j.get(0)) .setFake(false); } } return j; } /** * Sets whether or not to detach the selected entity/ies * * @return This */ public J detach() { detach = true; return (J) this; } /** * Returns the number of rows affected by the delete. *

* Bulk Delete Operation *

* WARNING : Be very careful if you haven't added a filter this will truncate the table or throw a unsupported exception if no filters. * * @return number of results deleted */ public int delete() { if (getFilters().isEmpty()) { throw new UnsupportedOperationException("Calling the delete method with no filters. This will truncate the table. Rather call truncate()"); } CriteriaDelete deletion = getCriteriaBuilder().createCriteriaDelete(getEntityClass()); reset(deletion.from(getEntityClass())); setCriteriaDelete(deletion); select(); return getEntityManager().createQuery(deletion) .executeUpdate(); } /** * Deletes the given entity through the entity manager * * @param entity * Deletes through the entity manager * * @return This */ @SuppressWarnings("Duplicates") public E delete(E entity) { boolean transactionAlreadyStarted = false; com.oracle.jaxb21.PersistenceUnit unit = GuiceContext.get(Key.get(PersistenceUnit.class, getEntityManagerAnnotation())); for (ITransactionHandler handler : GuiceContext.get(ITransactionHandlerReader)) { if (handler.transactionExists(getEntityManager(), unit)) { transactionAlreadyStarted = true; break; } } for (ITransactionHandler handler : GuiceContext.get(ITransactionHandlerReader)) { if (!transactionAlreadyStarted && handler.active(unit)) { handler.beginTransacation(false, getEntityManager(), unit); } } getEntityManager().remove(entity); for (ITransactionHandler handler : GuiceContext.get(ITransactionHandlerReader)) { if (!transactionAlreadyStarted && handler.active(unit)) { handler.commitTransacation(false, getEntityManager(), unit); } } return entity; } /** * Returns the assigned entity manager * * @return The entity manager to use for this run */ @Override public abstract EntityManager getEntityManager(); /** * Returns the number of rows affected by the delete. * WARNING : Be very careful if you haven't added a filter this will truncate the table or throw a unsupported exception if no filters. * * @return The number of records deleted */ @SuppressWarnings({"unused", "Duplicates"}) public int truncate() { CriteriaDelete deletion = getCriteriaBuilder().createCriteriaDelete(getEntityClass()); setCriteriaDelete(deletion); reset(deletion.from(getEntityClass())); getFilters().clear(); select(); boolean transactionAlreadyStarted = false; com.oracle.jaxb21.PersistenceUnit unit = GuiceContext.get(Key.get(PersistenceUnit.class, getEntityManagerAnnotation())); for (ITransactionHandler handler : GuiceContext.get(ITransactionHandlerReader)) { if (handler.transactionExists(getEntityManager(), unit)) { transactionAlreadyStarted = true; break; } } for (ITransactionHandler handler : GuiceContext.get(ITransactionHandlerReader)) { if (!transactionAlreadyStarted && handler.active(unit)) { handler.beginTransacation(false, getEntityManager(), unit); } } int results = getEntityManager().createQuery(deletion) .executeUpdate(); for (ITransactionHandler handler : GuiceContext.get(ITransactionHandlerReader)) { if (!transactionAlreadyStarted && handler.active(unit)) { handler.commitTransacation(false, getEntityManager(), unit); } } return results; } /** * Returns a lsit of all fields for an object recursively * * @param object * THe object class * @param fieldList * The list of fields * * @return A list of type Field */ private List allFields(Class object, List fieldList) { fieldList.addAll(Arrays.asList(object.getDeclaredFields())); if (object.getSuperclass() != Object.class) { allFields(object.getSuperclass(), fieldList); } return fieldList; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy