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

com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties Maven / Gradle / Ivy

package com.avaje.ebeaninternal.server.querydefn;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import com.avaje.ebean.ExpressionFactory;
import com.avaje.ebean.FetchConfig;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.Query;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionFactory;
import com.avaje.ebeaninternal.api.SpiExpressionList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.expression.FilterExprPath;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import com.avaje.ebeaninternal.server.query.SplitName;
import com.avaje.ebeaninternal.util.FilterExpressionList;

/**
 * Represents the Properties of an Object Relational query.
 */
public class OrmQueryProperties implements Serializable {

  private static final long serialVersionUID = -8785582703966455658L;

  private String parentPath;
  private String path;

  private String properties;

  private String trimmedProperties;

  /**
   * NB: -1 means no +query, 0 means use the default batch size.
   */
  private int queryFetchBatch = -1;
  private boolean queryFetchAll;

  /**
   * NB: -1 means no +lazy, 0 means use the default batch size.
   */
  private int lazyFetchBatch = -1;

  private FetchConfig fetchConfig;

  private boolean cache;

  private boolean readOnly;

  /**
   * Note this SHOULD be a LinkedHashSet to preserve order of the properties. This is to make using
   * SqlSelect easier with predictable property/column ordering.
   */
  private Set included;

  /**
   * Included bean joins.
   */
  private Set includedBeanJoin;

  /**
   * Add these properties to the select so that the foreign key columns are included in the query.
   */
  private Set secondaryQueryJoins;

  private List secondaryChildren;

  /**
   * OrderBy properties that where on the main query but moved here as they relate to this (query
   * join).
   */
  @SuppressWarnings("rawtypes")
  private OrderBy orderBy;

  /**
   * A filter that can be applied to the fetch of this path in the object graph.
   */
  @SuppressWarnings("rawtypes")
  private SpiExpressionList filterMany;

  /**
   * Construct with a given path (null == root path).
   */
  public OrmQueryProperties(String path) {
    this.path = path;
    this.parentPath = SplitName.parent(path);
  }

  public OrmQueryProperties() {
    this(null);
  }

  /**
   * Used by query language parser.
   */
  public OrmQueryProperties(String path, String properties) {
    this(path);
    setProperties(properties);
  }

  /**
   * Move a OrderBy.Property from the main query to this query join.
   */
  @SuppressWarnings("rawtypes")
  public void addSecJoinOrderProperty(OrderBy.Property orderProp) {
    if (orderBy == null) {
      orderBy = new OrderBy();
    }
    orderBy.add(orderProp);
  }

  /**
   * Set the Fetch configuration options for this path.
   */
  public void setFetchConfig(FetchConfig fetchConfig) {
    if (fetchConfig != null) {
      this.fetchConfig = fetchConfig;
      lazyFetchBatch = fetchConfig.getLazyBatchSize();
      queryFetchBatch = fetchConfig.getQueryBatchSize();
      queryFetchAll = fetchConfig.isQueryAll();
    }
  }

  public FetchConfig getFetchConfig() {
    return fetchConfig;
  }

  /**
   * Set the comma delimited properties to fetch for this path.
   * 

* This can include the +query and +lazy type hints. *

*/ public void setProperties(String properties) { this.properties = properties; this.trimmedProperties = properties; parseProperties(); if (!isAllProperties()) { Set parsed = parseIncluded(trimmedProperties); if (parsed.contains("*")) { this.included = null; } else { this.included = parsed; } } else { this.included = null; } } private boolean isAllProperties() { return (trimmedProperties == null) || (trimmedProperties.length() == 0) || "*".equals(trimmedProperties); } /** * Return the expressions used to filter on this path. This should be a many path to use this * method. */ @SuppressWarnings({"rawtypes", "unchecked"}) public SpiExpressionList filterMany(Query rootQuery) { if (filterMany == null) { FilterExprPath exprPath = new FilterExprPath(path); SpiExpressionFactory queryEf = (SpiExpressionFactory) rootQuery.getExpressionFactory(); ExpressionFactory filterEf = queryEf.createExpressionFactory();// exprPath); filterMany = new FilterExpressionList(exprPath, filterEf, rootQuery); // by default we need to make this a 'query join' now queryFetchAll = true; queryFetchBatch = 100; lazyFetchBatch = 100; } return filterMany; } /** * Return the filterMany expression list (can be null). */ public SpiExpressionList getFilterManyTrimPath(int trimPath) { if (filterMany == null) { return null; } return filterMany.trimPath(trimPath); } /** * Return the filterMany expression list (can be null). */ public SpiExpressionList getFilterMany() { return filterMany; } /** * Set the filterMany expression list. */ public void setFilterMany(SpiExpressionList filterMany) { this.filterMany = filterMany; } /** * Set the properties from deployment default FetchTypes. */ public void setDefaultProperties(String properties, Set included) { this.properties = properties; this.trimmedProperties = properties; this.included = included; } /** * Set the properties from a matching AutoTune tuned properties. */ public void setTunedProperties(OrmQueryProperties tunedProperties) { if (tunedProperties.hasProperties()) { this.properties = tunedProperties.properties; this.trimmedProperties = tunedProperties.trimmedProperties; this.included = tunedProperties.included; this.queryFetchBatch = Math.max(queryFetchBatch, tunedProperties.queryFetchBatch); this.lazyFetchBatch = Math.max(lazyFetchBatch, tunedProperties.lazyFetchBatch); } } /** * Define the select and joins for this query. */ @SuppressWarnings("unchecked") public void configureBeanQuery(SpiQuery query) { if (trimmedProperties != null && trimmedProperties.length() > 0) { query.select(trimmedProperties); } if (filterMany != null) { SpiExpressionList trimPath = filterMany.trimPath(path.length() + 1); List underlyingList = trimPath.getUnderlyingList(); for (SpiExpression spiExpression : underlyingList) { query.where().add(spiExpression); } } if (secondaryChildren != null) { int trimPath = path.length() + 1; for (int i = 0; i < secondaryChildren.size(); i++) { OrmQueryProperties p = secondaryChildren.get(i); String path = p.getPath(); path = path.substring(trimPath); query.fetch(path, p.getProperties(), p.getFetchConfig()); query.setFilterMany(path, p.getFilterManyTrimPath(trimPath)); } } if (orderBy != null) { query.setOrder(orderBy.copyWithTrim(path)); } } /** * Creates a copy of the OrmQueryProperties. */ public OrmQueryProperties copy() { OrmQueryProperties copy = new OrmQueryProperties(); copy.parentPath = parentPath; copy.path = path; copy.properties = properties; copy.trimmedProperties = trimmedProperties; copy.cache = cache; copy.readOnly = readOnly; copy.queryFetchAll = queryFetchAll; copy.queryFetchBatch = queryFetchBatch; copy.lazyFetchBatch = lazyFetchBatch; copy.filterMany = filterMany; if (included != null) { copy.included = new HashSet(included); } if (includedBeanJoin != null) { copy.includedBeanJoin = new HashSet(includedBeanJoin); } return copy; } public boolean hasSelectClause() { if ("*".equals(trimmedProperties)) { // explicitly selected all properties return true; } // explicitly selected some properties return included != null; } public String toString() { String s = ""; if (path != null) { s += path + " "; } if (properties != null) { s += "(" + properties + ") "; } else if (included != null) { s += "(" + included + ") "; } return s; } public boolean isChild(OrmQueryProperties possibleChild) { return possibleChild.getPath().startsWith(path + "."); } /** * For secondary queries add a child element. */ public void add(OrmQueryProperties child) { if (secondaryChildren == null) { secondaryChildren = new ArrayList(); } secondaryChildren.add(child); } public int autoTunePlanHash() { int hc = (path != null ? path.hashCode() : 1); if (properties != null) { hc = hc * 31 + properties.hashCode(); } else { hc = hc * 31 + (included != null ? included.hashCode() : 1); } return hc; } /** * Calculate the query plan hash. */ @SuppressWarnings("unchecked") public void queryPlanHash(BeanQueryRequest request, HashQueryPlanBuilder builder) { builder.add(path); if (properties != null) { builder.add(properties); } else { builder.add(included); } builder.add(filterMany != null); if (filterMany != null) { filterMany.queryPlanHash(request, builder); } builder.add(lazyFetchBatch); builder.add(queryFetchBatch); builder.add(queryFetchAll); } public String getProperties() { return properties; } /** * Return true if this has properties. */ public boolean hasProperties() { return properties != null || included != null; } /** * Return true if this property is included as a bean join. *

* If a property is included as a bean join then it should not be included as a reference/proxy to * avoid duplication. *

*/ public boolean isIncludedBeanJoin(String propertyName) { return includedBeanJoin != null && includedBeanJoin.contains(propertyName); } /** * Add a bean join property. */ public void includeBeanJoin(String propertyName) { if (includedBeanJoin == null) { includedBeanJoin = new HashSet(); } includedBeanJoin.add(propertyName); } public boolean allProperties() { return included == null; } /** * This excludes the bean joined properties. *

* This is because bean joins will have there own node in the SqlTree. *

*/ public Set getSelectProperties() { if (secondaryQueryJoins == null) { return included; } LinkedHashSet temp = new LinkedHashSet(secondaryQueryJoins.size() + included.size()); temp.addAll(included); temp.addAll(secondaryQueryJoins); return temp; } public void addSecondaryQueryJoin(String property) { if (secondaryQueryJoins == null) { secondaryQueryJoins = new HashSet(4); } secondaryQueryJoins.add(property); } /** * Return all the properties including the bean joins. This is the set that will be used by * EntityBeanIntercept to determine if a property needs to be lazy loaded. */ public Set getAllIncludedProperties() { if (included == null) { return null; } if (includedBeanJoin == null && secondaryQueryJoins == null) { return new LinkedHashSet(included); } LinkedHashSet s = new LinkedHashSet(2 * (included.size() + 5)); if (included != null) { s.addAll(included); } if (includedBeanJoin != null) { s.addAll(includedBeanJoin); } if (secondaryQueryJoins != null) { s.addAll(secondaryQueryJoins); } return s; } public boolean isIncluded(String propName) { if (includedBeanJoin != null && includedBeanJoin.contains(propName)) { return false; } // all properties included return included == null || included.contains(propName); } public void setQueryFetch(int batch, boolean queryFetchAll) { this.queryFetchBatch = batch; this.queryFetchAll = queryFetchAll; } public boolean isFetchJoin() { return !isQueryFetch() && !isLazyFetch(); } public boolean isQueryFetch() { return queryFetchBatch > -1; } public int getQueryFetchBatch() { return queryFetchBatch; } public boolean isQueryFetchAll() { return queryFetchAll; } public boolean isLazyFetch() { return lazyFetchBatch > -1; } public int getLazyFetchBatch() { return lazyFetchBatch; } public boolean isReadOnly() { return readOnly; } public boolean isCache() { return cache; } public String getParentPath() { return parentPath; } public String getPath() { return path; } private void parseProperties() { if (trimmedProperties == null) { return; } int pos = trimmedProperties.indexOf("+readonly"); if (pos > -1) { trimmedProperties = StringHelper.replaceString(trimmedProperties, "+readonly", ""); this.readOnly = true; } pos = trimmedProperties.indexOf("+cache"); if (pos > -1) { trimmedProperties = StringHelper.replaceString(trimmedProperties, "+cache", ""); this.cache = true; } pos = trimmedProperties.indexOf("+query"); if (pos > -1) { queryFetchBatch = parseBatchHint(pos, "+query"); } pos = trimmedProperties.indexOf("+lazy"); if (pos > -1) { lazyFetchBatch = parseBatchHint(pos, "+lazy"); } trimmedProperties = trimmedProperties.trim(); while (trimmedProperties.startsWith(",")) { trimmedProperties = trimmedProperties.substring(1).trim(); } } private int parseBatchHint(int pos, String option) { int startPos = pos + option.length(); int endPos = findEndPos(startPos, trimmedProperties); if (endPos == -1) { trimmedProperties = StringHelper.replaceString(trimmedProperties, option, ""); return 0; } else { String batchParam = trimmedProperties.substring(startPos + 1, endPos); if (endPos + 1 >= trimmedProperties.length()) { trimmedProperties = trimmedProperties.substring(0, pos); } else { trimmedProperties = trimmedProperties.substring(0, pos) + trimmedProperties.substring(endPos + 1); } return Integer.parseInt(batchParam); } } private int findEndPos(int pos, String props) { if (pos < props.length()) { if (props.charAt(pos) == '(') { int endPara = props.indexOf(')', pos + 1); if (endPara == -1) { String m = "Error could not find ')' in " + props + " after position " + pos; throw new RuntimeException(m); } return endPara; } } return -1; } /** * Parse the include separating by comma or semicolon. */ private static Set parseIncluded(String rawList) { String[] res = rawList.split(","); LinkedHashSet set = new LinkedHashSet(res.length + 3); String temp; for (int i = 0; i < res.length; i++) { temp = res[i].trim(); if (temp.length() > 0) { set.add(temp); } } return Collections.unmodifiableSet(set); } public boolean isSame(OrmQueryProperties p2) { if (included == null) { return p2.included == null; } return included.equals(p2.included); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy