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

org.bitbucket.brunneng.qb.AbstractJpqlQueryBuilder Maven / Gradle / Ivy

Go to download

Compact tool for building SQL, JPA or hibernate queries. Supports utilities for spring pagination and sorting. Supports extending to other types of query languages. Has no transitive dependencies - use only what you need.

The newest version!
package org.bitbucket.brunneng.qb;

import javax.persistence.Query;
import javax.persistence.TypedQuery;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Abstract query builder for JPQL queries.
 */
public abstract class AbstractJpqlQueryBuilder extends SqlQueryBuilder {

   protected String identifiersPathPattern = "[A-Za-z][A-Za-z0-9_\\.]*";
   protected final Pattern joinPattern;
   protected final Pattern aliasUsagePattern;

   private List dynamicJoins = new ArrayList<>();

   public AbstractJpqlQueryBuilder() {
      joinPattern = Pattern.compile("join\\s+" + identifiersPathPattern + "\\s+(" + identifierPattern + ")",
            Pattern.CASE_INSENSITIVE);
      aliasUsagePattern = Pattern.compile("([\\s=]|^)(" + identifierPattern + ")([.\\s=]|$)",
            Pattern.CASE_INSENSITIVE);
   }

   /**
    * Appends join only in case, if contained alias will be used (or was used) in query.
    * It's helpful, if parts with alias are added by condition, so the same condition should be applied when we
    * add join parts.
    * @param joinPart part of query which contains join in format: join [path] [alias] (can be few joins)
    * @return this query builder
    */
   public AbstractJpqlQueryBuilder appendJoin(String joinPart) {
      Matcher matcher = joinPattern.matcher(joinPart);
      List aliases = new ArrayList<>();
      while (matcher.find()) {
         String alias = matcher.group(1);
         aliases.add(alias);
      }
      if (aliases.isEmpty()) {
         throw new IllegalArgumentException(
               String.format("Pattern 'join  ' is not detected in '%s'", joinPart));
      }

      dynamicJoins.add(new DynamicJoinInfo(joinPart, aliases, query.length()));
      tryInsertDynamicJoinIfNeededForQueryPart(query.toString());

      return this;
   }

   @Override
   public AbstractJpqlQueryBuilder append(String queryPart) {
      super.append(queryPart);
      tryInsertDynamicJoinIfNeededForQueryPart(queryPart);
      return this;
   }

   @Override
   protected void appendInternal(String queryPart, Object value) {
      super.appendInternal(queryPart, value);
      tryInsertDynamicJoinIfNeededForQueryPart(queryPart);
   }

   protected void tryInsertDynamicJoinIfNeededForQueryPart(String queryPart) {
      if (!dynamicJoins.isEmpty()) {
         Matcher matcher = aliasUsagePattern.matcher(queryPart);
         int nextStart = 0;
         while (matcher.find(nextStart)) {
            String alias = matcher.group(2);

            dynamicJoins.stream().filter(dj -> dj.aliases.contains(alias)).findFirst()
                  .ifPresent(this::insertDynamicJoin);
            nextStart = matcher.end() - 1;
         }
      }
   }

   protected void insertDynamicJoin(DynamicJoinInfo dynamicJoin) {
      int insertIndex = dynamicJoin.insertIndex;
      String joinPart = tryFrameWithSpacesIfNeeded(dynamicJoin.joinPart, insertIndex);

      query.insert(insertIndex, joinPart);

      int indexOfInsertedJoin = dynamicJoins.indexOf(dynamicJoin);
      dynamicJoins.remove(dynamicJoin);

      for (int i = indexOfInsertedJoin; i < dynamicJoins.size(); ++i) {
         dynamicJoins.get(i).insertIndex += joinPart.length();
      }

      tryInsertDynamicJoinIfNeededForQueryPart(joinPart);
   }

   /**
    * Builds jpa query with applied parameters
    * @return jpa query ready to be executed
    */
   public Query build() {
      return createQuery(getQueryWithParams());
   }

   /**
    * Builds typed jpa query with applied parameters
    * @param resultClass class of query result
    * @param  type of resulting query
    * @return typed jpa query ready to be executed
    */
   public  TypedQuery build(Class resultClass) {
      return createTypedQuery(getQueryWithParams(), resultClass);
   }


   /**
    * Builds native query with applied parameters
    * @return native query ready to be executed
    */
   public Query buildNative() {
      return createNativeQuery(getQueryWithParams());
   }

   /**
    * Builds jpa query to get a count of objects, selected by current query.
    * For example if your query is `select o from Order o where ...`, then built query will be
    * `select count(o) from Order o where ...`.
    * @return jpa count query, to be executed
    */
   public TypedQuery buildCountQuery() {
      return (TypedQuery)createQuery(buildCountQueryInternal(true));
   }

   /**
    * Builds native query to get a count of objects, selected by current query.
    * For example if your query is `select o.id from orders o where ...`, then built query will be
    * `select count(o.id) from orders o where ...`.
    * @return native count query, to be executed
    */
   public TypedQuery buildCountNativeQuery() {
      return (TypedQuery) createNativeQuery(buildCountQueryInternal(false));
   }

   protected abstract Query createQuery(QueryWithParams queryWithParams);

   protected abstract  TypedQuery createTypedQuery(QueryWithParams queryWithParams, Class targetClass);
   protected abstract Query createNativeQuery(QueryWithParams queryWithParams);

   protected void applyParametersToQuery(Query query, Map preparedParameters) {
      for (Map.Entry entry : preparedParameters.entrySet()) {
         query.setParameter(entry.getKey(), entry.getValue());
      }
   }

   static class DynamicJoinInfo {
      String joinPart;
      List aliases;
      int insertIndex;

      DynamicJoinInfo(String joinPart, List aliases, int insertIndex) {
         this.joinPart = joinPart;
         this.aliases = aliases;
         this.insertIndex = insertIndex;
      }
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy