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

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

There is a newer version: 2.8.1
Show newest version
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.Iterator;
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.OrderBy.Property;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebeaninternal.api.SpiExpressionFactory;
import com.avaje.ebeaninternal.api.SpiExpressionList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.ReferenceOptions;
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; } filterMany.trimPath(trimPath); return filterMany; } /** * 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 autofetch tuned properties. */ public void setTunedProperties(OrmQueryProperties tunedProperties) { this.properties = tunedProperties.properties; this.trimmedProperties = tunedProperties.trimmedProperties; this.included = tunedProperties.included; } /** * 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){ query.setFilterMany(path, filterMany); } } 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){ List orderByProps = orderBy.getProperties(); for (int i = 0; i < orderByProps.size(); i++) { orderByProps.get(i).trim(path); } query.setOrder(orderBy); } } /** * Define the select and joins for this query. */ @SuppressWarnings("unchecked") public void configureManyQuery(SpiQuery query) { if (trimmedProperties != null && trimmedProperties.length() > 0) { query.join(query.getLazyLoadManyPath(), trimmedProperties); } if (filterMany != null){ query.setFilterMany(path, filterMany); } if (secondaryChildren != null) { int trimlen = path.length() - query.getLazyLoadManyPath().length(); for (int i = 0; i < secondaryChildren.size(); i++) { OrmQueryProperties p = secondaryChildren.get(i); String path = p.getPath(); path = path.substring(trimlen); query.fetch(path, p.getProperties(), p.getFetchConfig()); query.setFilterMany(path, p.getFilterManyTrimPath(trimlen)); } } if (orderBy != null){ query.setOrder(orderBy); } } /** * Creates a copy of the OrmQueryProperties. */ public OrmQueryProperties copy() { OrmQueryProperties copy = new OrmQueryProperties(); copy.parentPath = parentPath; copy.path = path; copy.properties = properties; 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 autofetchPlanHash() { 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 int queryPlanHash(BeanQueryRequest request) { int hc = (path != null ? path.hashCode() : 1); if (properties != null){ hc = hc * 31 + properties.hashCode(); } else { hc = hc * 31 + (included != null ? included.hashCode() : 1); } hc = hc * 31 + (filterMany != null ? filterMany.queryPlanHash(request) : 1); return hc; } public String getProperties() { return properties; } public ReferenceOptions getReferenceOptions() { if (cache || readOnly) { return new ReferenceOptions(cache, readOnly, null); } else { return null; } } /** * 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) { if (includedBeanJoin == null) { return false; } else { return 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 Iterator getSelectProperties() { if (secondaryQueryJoins == null) { return included.iterator(); } LinkedHashSet temp = new LinkedHashSet(secondaryQueryJoins.size() + included.size()); temp.addAll(included); temp.addAll(secondaryQueryJoins); return temp.iterator(); } 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; } if (included == null) { // all properties included return true; } return included.contains(propName); } /** * Used to convert this join to a query join. * * @param queryJoinBatch * where -1 means not a query join and 0 means use the default * batch size. */ public OrmQueryProperties setQueryFetchBatch(int queryFetchBatch) { this.queryFetchBatch = queryFetchBatch; return this; } public OrmQueryProperties setQueryFetchAll(boolean queryFetchAll) { this.queryFetchAll = queryFetchAll; return this; } public OrmQueryProperties setQueryFetch(int batch, boolean queryFetchAll) { this.queryFetchBatch = batch; this.queryFetchAll = queryFetchAll; return this; } /** * Set the lazy loading batch size. */ public OrmQueryProperties setLazyFetchBatch(int lazyFetchBatch) { this.lazyFetchBatch = lazyFetchBatch; return this; } 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 = null; for (int i = 0; i < res.length; i++) { temp = res[i].trim(); if (temp.length() > 0) { set.add(temp); } } if (set.isEmpty()){ return null; } return Collections.unmodifiableSet(set); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy