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

com.blazebit.persistence.impl.PaginatedCriteriaBuilderImpl Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014 Blazebit.
 *
 * 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.blazebit.persistence.impl;

import java.util.*;

import javax.persistence.TypedQuery;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.Metamodel;

import com.blazebit.persistence.*;
import com.blazebit.persistence.impl.builder.object.DelegatingKeysetExtractionObjectBuilder;
import com.blazebit.persistence.impl.builder.object.KeysetExtractionObjectBuilder;
import com.blazebit.persistence.impl.keyset.KeysetMode;
import com.blazebit.persistence.impl.keyset.KeysetPaginationHelper;
import com.blazebit.persistence.impl.keyset.SimpleKeysetLink;
import com.blazebit.persistence.impl.query.CTENode;
import com.blazebit.persistence.impl.query.CustomQuerySpecification;
import com.blazebit.persistence.impl.query.EntityFunctionNode;
import com.blazebit.persistence.impl.query.QuerySpecification;
import com.blazebit.persistence.spi.QueryTransformer;

/**
 *
 * @author Christian Beikov
 * @author Moritz Becker
 * @since 1.0
 */
public class PaginatedCriteriaBuilderImpl extends AbstractFullQueryBuilder, PaginatedCriteriaBuilderImpl, PaginatedCriteriaBuilderImpl, BaseFinalSetOperationBuilderImpl> implements PaginatedCriteriaBuilder {

    private static final String ENTITY_PAGE_POSITION_PARAMETER_NAME = "_entityPagePositionParameter";
    private static final String PAGE_POSITION_ID_QUERY_ALIAS_PREFIX = "_page_position_";

    private boolean keysetExtraction;
    private final KeysetPage keysetPage;

    // Mutable state
    private boolean needsCheck = true;

    private final Object entityId;
    private boolean needsNewIdList;
    private KeysetMode keysetMode;

    // Cache
    private String cachedCountQueryString;
    private String cachedExternalCountQueryString;
    private String cachedIdQueryString;
    private String cachedExternalIdQueryString;

    public PaginatedCriteriaBuilderImpl(AbstractFullQueryBuilder, ?, ?, ?> baseBuilder, boolean keysetExtraction, KeysetPage keysetPage, Object entityId, int pageSize) {
        super(baseBuilder);
        if (pageSize <= 0) {
            throw new IllegalArgumentException("pageSize may not be zero or negative");
        }
        this.keysetExtraction = keysetExtraction;
        this.keysetPage = keysetPage;
        this.entityId = entityId;
        this.maxResults = pageSize;
        updateKeysetMode();
    }

    public PaginatedCriteriaBuilderImpl(AbstractFullQueryBuilder, ?, ?, ?> baseBuilder, boolean keysetExtraction, KeysetPage keysetPage, int firstRow, int pageSize) {
        super(baseBuilder);
        if (firstRow < 0) {
            throw new IllegalArgumentException("firstRow may not be negative");
        }
        if (pageSize <= 0) {
            throw new IllegalArgumentException("pageSize may not be zero or negative");
        }
        this.keysetExtraction = keysetExtraction;
        this.keysetPage = keysetPage;
        this.firstResult = firstRow;
        this.entityId = null;
        this.maxResults = pageSize;
        updateKeysetMode();
    }

    @Override
    public PaginatedCriteriaBuilder setFirstResult(int firstResult) {
        super.setFirstResult(firstResult);
        updateKeysetMode();
        return this;
    }

    @Override
    public PaginatedCriteriaBuilder setMaxResults(int maxResults) {
        super.setMaxResults(maxResults);
        updateKeysetMode();
        return this;
    }

    private void updateKeysetMode() {
        KeysetMode oldMode = this.keysetMode;
        this.keysetMode = KeysetPaginationHelper.getKeysetMode(keysetPage, entityId, firstResult, maxResults);
        if (keysetMode == KeysetMode.NONE) {
            this.keysetManager.setKeysetLink(null);
        } else if (keysetMode == KeysetMode.NEXT) {
            this.keysetManager.setKeysetLink(new SimpleKeysetLink(keysetPage.getHighest(), keysetMode));
        } else {
            this.keysetManager.setKeysetLink(new SimpleKeysetLink(keysetPage.getLowest(), keysetMode));
        }

        if (keysetMode != oldMode) {
            clearCache();
        }
    }

    @Override
    public PaginatedCriteriaBuilder withKeysetExtraction(boolean keysetExtraction) {
        this.keysetExtraction = keysetExtraction;
        return this;
    }

    @Override
    public boolean isKeysetExtraction() {
        return keysetExtraction;
    }

    private  TypedQuery getCountQuery(String countQueryString, Class resultType, boolean normalQueryMode, Set keyRestrictedLeftJoins) {
        if (normalQueryMode && isEmpty(keyRestrictedLeftJoins, EnumSet.of(ClauseType.ORDER_BY, ClauseType.SELECT))) {
            TypedQuery countQuery = em.createQuery(countQueryString, resultType);
            parameterManager.parameterizeQuery(countQuery);
            return countQuery;
        }

        TypedQuery baseQuery = em.createQuery(countQueryString, resultType);
        Set parameterListNames = parameterManager.getParameterListNames(baseQuery);
        List keyRestrictedLeftJoinAliases = getKeyRestrictedLeftJoinAliases(baseQuery, keyRestrictedLeftJoins, EnumSet.of(ClauseType.ORDER_BY, ClauseType.SELECT));
        List entityFunctionNodes = getEntityFunctionNodes(baseQuery);
        boolean shouldRenderCteNodes = renderCteNodes(false);
        List ctes = shouldRenderCteNodes ? getCteNodes(baseQuery, false) : Collections.EMPTY_LIST;
        QuerySpecification querySpecification = new CustomQuerySpecification(
                this, baseQuery, parameterListNames, null, null, keyRestrictedLeftJoinAliases, entityFunctionNodes, mainQuery.cteManager.isRecursive(), ctes, shouldRenderCteNodes
        );

        TypedQuery countQuery = new CustomSQLTypedQuery(
                querySpecification,
                baseQuery,
                (CommonQueryBuilder) this,
                cbf.getExtendedQuerySupport(),
                parameterManager.getValuesParameters(),
                parameterManager.getValuesBinders()
        );

        parameterManager.parameterizeQuery(countQuery);
        return countQuery;
    }

    @Override
    public PaginatedTypedQuery getQuery() {
        prepareAndCheck();
        // We can only use the query directly if we have no ctes, entity functions or hibernate bugs
        Set keyRestrictedLeftJoins = joinManager.getKeyRestrictedLeftJoins();
        boolean normalQueryMode = !isMainQuery || (!mainQuery.cteManager.hasCtes() && !joinManager.hasEntityFunctions() && keyRestrictedLeftJoins.isEmpty());
        String countQueryString = getPageCountQueryStringWithoutCheck();

        TypedQuery countQuery;
        if (entityId == null) {
            // No reference entity id, so just do a simple count query
            countQuery = getCountQuery(countQueryString, Long.class, normalQueryMode, keyRestrictedLeftJoins);
        } else {
            countQuery = getCountQuery(countQueryString, Object[].class, normalQueryMode, keyRestrictedLeftJoins);
        }

        TypedQuery idQuery = null;
        TypedQuery objectQuery;
        KeysetExtractionObjectBuilder objectBuilder;
        if (joinManager.hasCollections()) {
            String idQueryString = getPageIdQueryStringWithoutCheck();
            idQuery = getIdQuery(idQueryString, normalQueryMode, keyRestrictedLeftJoins);
            objectQuery = getObjectQueryById(normalQueryMode, keyRestrictedLeftJoins);
            objectBuilder = null;
        } else {
            Map.Entry, KeysetExtractionObjectBuilder> entry = getObjectQuery(normalQueryMode, keyRestrictedLeftJoins);
            objectQuery = entry.getKey();
            objectBuilder = entry.getValue();
        }
        PaginatedTypedQuery query = new PaginatedTypedQuery(countQuery, idQuery, objectQuery, objectBuilder, entityId, firstResult, maxResults, needsNewIdList, keysetExtraction, keysetMode, keysetPage);
        return query;
    }

    @Override
    public PagedList getResultList() {
        return getQuery().getResultList();
    }

    @Override
    public String getPageCountQueryString() {
        prepareAndCheck();
        return getExternalPageCountQueryString();
    }

    private String getPageCountQueryStringWithoutCheck() {
        if (cachedCountQueryString == null) {
            cachedCountQueryString = buildPageCountQueryString(false);
        }

        return cachedCountQueryString;
    }

    protected String getExternalPageCountQueryString() {
        if (cachedExternalCountQueryString == null) {
            cachedExternalCountQueryString = buildPageCountQueryString(true);
        }

        return cachedExternalCountQueryString;
    }

    @Override
    public String getPageIdQueryString() {
        prepareAndCheck();
        return getExternalPageIdQueryString();
    }

    private String getPageIdQueryStringWithoutCheck() {
        if (cachedIdQueryString == null) {
            cachedIdQueryString = buildPageIdQueryString(false);
        }

        return cachedIdQueryString;
    }

    protected String getExternalPageIdQueryString() {
        if (cachedExternalIdQueryString == null) {
            cachedExternalIdQueryString = buildPageIdQueryString(true);
        }

        return cachedExternalIdQueryString;
    }

    @Override
    public String getQueryString() {
        prepareAndCheck();
        return getExternalQueryString();
    }

    @Override
    protected String getBaseQueryString() {
        if (cachedQueryString == null) {
            if (!joinManager.hasCollections()) {
                cachedQueryString = buildObjectQueryString(false);
            } else {
                cachedQueryString = buildBaseQueryString(false);
            }
        }

        return cachedQueryString;
    }

    protected String getExternalQueryString() {
        if (cachedExternalQueryString == null) {
            if (!joinManager.hasCollections()) {
                cachedExternalQueryString = buildObjectQueryString(true);
            } else {
                cachedExternalQueryString = buildBaseQueryString(true);
            }
        }

        return cachedExternalQueryString;
    }

    @Override
    protected void clearCache() {
        super.clearCache();
        cachedCountQueryString = null;
        cachedExternalCountQueryString = null;
        cachedIdQueryString = null;
        cachedExternalIdQueryString = null;
    }

    @Override
    protected void prepareAndCheck() {
        if (!needsCheck) {
            return;
        }

        verifyBuilderEnded();
        if (!orderByManager.hasOrderBys()) {
            throw new IllegalStateException("Pagination requires at least one order by item!");
        }

        applyImplicitJoins();
        applyExpressionTransformers();

        // Paginated criteria builders always need the last order by expression to be unique
        Metamodel m = em.getMetamodel();
        List orderByExpressions = orderByManager.getOrderByExpressions(m);
        if (!orderByExpressions.get(orderByExpressions.size() - 1).isUnique()) {
            throw new IllegalStateException("The last order by item must be unique!");
        }

        if (keysetManager.hasKeyset()) {
            keysetManager.initialize(orderByExpressions);
        }

        needsNewIdList = keysetExtraction || orderByManager.hasComplexOrderBys();

        // No need to do the check again if no mutation occurs
        needsCheck = false;
    }

    @SuppressWarnings("unchecked")
    private Map.Entry, KeysetExtractionObjectBuilder> getObjectQuery(boolean normalQueryMode, Set keyRestrictedLeftJoins) {
        String queryString = getBaseQueryString();
        Class expectedResultType;

        // When the keyset is included the query obviously produces an array
        if (keysetExtraction) {
            expectedResultType = Object[].class;
        } else {
            expectedResultType = selectManager.getExpectedQueryResultType();
        }

        TypedQuery query;

        if (normalQueryMode && isEmpty(keyRestrictedLeftJoins, EnumSet.noneOf(ClauseType.class))) {
            query = (TypedQuery) em.createQuery(queryString, expectedResultType);
            parameterManager.parameterizeQuery(query);
        } else {
            TypedQuery baseQuery = (TypedQuery) em.createQuery(queryString, expectedResultType);
            Set parameterListNames = parameterManager.getParameterListNames(baseQuery);

            List keyRestrictedLeftJoinAliases = getKeyRestrictedLeftJoinAliases(baseQuery, keyRestrictedLeftJoins, EnumSet.noneOf(ClauseType.class));
            List entityFunctionNodes = getEntityFunctionNodes(baseQuery);
            boolean shouldRenderCteNodes = renderCteNodes(false);
            List ctes = shouldRenderCteNodes ? getCteNodes(baseQuery, false) : Collections.EMPTY_LIST;
            QuerySpecification querySpecification = new CustomQuerySpecification(
                    this, baseQuery, parameterListNames, null, null, keyRestrictedLeftJoinAliases, entityFunctionNodes, mainQuery.cteManager.isRecursive(), ctes, shouldRenderCteNodes
            );

            query = new CustomSQLTypedQuery(
                    querySpecification,
                    baseQuery,
                    (CommonQueryBuilder) this,
                    cbf.getExtendedQuerySupport(),
                    parameterManager.getValuesParameters(),
                    parameterManager.getValuesBinders()
            );
            parameterManager.parameterizeQuery(query);
        }

        KeysetExtractionObjectBuilder objectBuilder = null;
        ObjectBuilder transformerObjectBuilder = selectManager.getSelectObjectBuilder();

        if (keysetExtraction) {
            int keysetSize = orderByManager.getOrderByCount();

            if (transformerObjectBuilder == null) {
                objectBuilder = new KeysetExtractionObjectBuilder(keysetSize);
            } else {
                objectBuilder = new DelegatingKeysetExtractionObjectBuilder(transformerObjectBuilder, keysetSize);
            }

            transformerObjectBuilder = objectBuilder;
        }

        if (transformerObjectBuilder != null) {
            for (QueryTransformer transformer : cbf.getQueryTransformers()) {
                query = transformer.transformQuery((TypedQuery) query, transformerObjectBuilder);
            }
        }

        return new AbstractMap.SimpleEntry, KeysetExtractionObjectBuilder>(query, objectBuilder);
    }

    private TypedQuery getIdQuery(String idQueryString, boolean normalQueryMode, Set keyRestrictedLeftJoins) {
        if (normalQueryMode && isEmpty(keyRestrictedLeftJoins, EnumSet.of(ClauseType.SELECT))) {
            TypedQuery idQuery = em.createQuery(idQueryString, Object[].class);

            parameterManager.parameterizeQuery(idQuery);
            return idQuery;
        }

        TypedQuery baseQuery = em.createQuery(idQueryString, Object[].class);
        Set parameterListNames = parameterManager.getParameterListNames(baseQuery);

        List keyRestrictedLeftJoinAliases = getKeyRestrictedLeftJoinAliases(baseQuery, keyRestrictedLeftJoins, EnumSet.of(ClauseType.SELECT));
        List entityFunctionNodes = getEntityFunctionNodes(baseQuery);
        boolean shouldRenderCteNodes = renderCteNodes(false);
        List ctes = shouldRenderCteNodes ? getCteNodes(baseQuery, false) : Collections.EMPTY_LIST;
        QuerySpecification querySpecification = new CustomQuerySpecification(
                this, baseQuery, parameterListNames, null, null, keyRestrictedLeftJoinAliases, entityFunctionNodes, mainQuery.cteManager.isRecursive(), ctes, shouldRenderCteNodes
        );

        TypedQuery idQuery = new CustomSQLTypedQuery(
                querySpecification,
                baseQuery,
                (CommonQueryBuilder) this,
                cbf.getExtendedQuerySupport(),
                parameterManager.getValuesParameters(),
                parameterManager.getValuesBinders()
        );
        parameterManager.parameterizeQuery(idQuery);
        return idQuery;
    }

    @SuppressWarnings("unchecked")
    private TypedQuery getObjectQueryById(boolean normalQueryMode, Set keyRestrictedLeftJoins) {
        if (normalQueryMode && isEmpty(keyRestrictedLeftJoins, EnumSet.complementOf(EnumSet.of(ClauseType.SELECT, ClauseType.ORDER_BY)))) {
            TypedQuery query = (TypedQuery) em.createQuery(getBaseQueryString(), selectManager.getExpectedQueryResultType());
            if (selectManager.getSelectObjectBuilder() != null) {
                query = transformQuery(query);
            }

            parameterManager.parameterizeQuery(query, Collections.singleton(idParamName));
            return query;
        }

        TypedQuery baseQuery = (TypedQuery) em.createQuery(getBaseQueryString(), selectManager.getExpectedQueryResultType());
        Set parameterListNames = parameterManager.getParameterListNames(baseQuery, Collections.singleton(idParamName));
        parameterListNames.add(idParamName);

        List keyRestrictedLeftJoinAliases = getKeyRestrictedLeftJoinAliases(baseQuery, keyRestrictedLeftJoins, EnumSet.complementOf(EnumSet.of(ClauseType.SELECT, ClauseType.ORDER_BY)));
        List entityFunctionNodes = getEntityFunctionNodes(baseQuery);
        boolean shouldRenderCteNodes = renderCteNodes(false);
        List ctes = shouldRenderCteNodes ? getCteNodes(baseQuery, false) : Collections.EMPTY_LIST;
        QuerySpecification querySpecification = new CustomQuerySpecification(
                this, baseQuery, parameterListNames, null, null, keyRestrictedLeftJoinAliases, entityFunctionNodes, mainQuery.cteManager.isRecursive(), ctes, shouldRenderCteNodes
        );

        TypedQuery query = new CustomSQLTypedQuery(
                querySpecification,
                baseQuery,
                (CommonQueryBuilder) this,
                cbf.getExtendedQuerySupport(),
                parameterManager.getValuesParameters(),
                parameterManager.getValuesBinders()
        );

        parameterManager.parameterizeQuery(query, Collections.singleton(idParamName));

        if (selectManager.getSelectObjectBuilder() != null) {
            query = transformQuery(query);
        }

        return query;
    }

    protected String buildPageCountQueryString(boolean externalRepresentation) {
        StringBuilder sbSelectFrom = new StringBuilder();
        if (externalRepresentation && isMainQuery) {
            mainQuery.cteManager.buildClause(sbSelectFrom);
        }
        buildPageCountQueryString(sbSelectFrom, externalRepresentation);
        return sbSelectFrom.toString();
    }

    private String buildPageCountQueryString(StringBuilder sbSelectFrom, boolean externalRepresentation) {
        JoinNode rootNode = joinManager.getRootNodeOrFail("Paginated criteria builders do not support multiple from clause elements!");
        Attribute idAttribute = JpaUtils.getIdAttribute(em.getMetamodel().entity(rootNode.getPropertyClass()));
        String idName = idAttribute.getName();
        StringBuilder idClause = new StringBuilder(100);
        rootNode.appendAlias(idClause, idName);
        // Spaces are important to be able to reuse the string builder without copying
        String countString = jpaProvider.getCustomFunctionInvocation("COUNT_TUPLE", 1) + "'DISTINCT', " + idClause + ")";
        sbSelectFrom.append("SELECT ").append(countString);

        if (entityId != null) {
            parameterManager.addParameterMapping(ENTITY_PAGE_POSITION_PARAMETER_NAME, entityId);

            sbSelectFrom.append(", ");
            sbSelectFrom.append(jpaProvider.getCustomFunctionInvocation("PAGE_POSITION", 2));

            sbSelectFrom.append('(');
            appendSimplePageIdQueryString(sbSelectFrom);
            sbSelectFrom.append("),");

            sbSelectFrom.append(':').append(ENTITY_PAGE_POSITION_PARAMETER_NAME);
            sbSelectFrom.append(")");
        }

        // Collect usage of collection join nodes to optimize away the count distinct
        Set collectionJoinNodes = joinManager.buildClause(sbSelectFrom, EnumSet.of(ClauseType.ORDER_BY, ClauseType.SELECT), null, true, externalRepresentation);
        // TODO: Maybe we can improve this and treat array access joins like non-collection join nodes 
        boolean hasCollectionJoinUsages = collectionJoinNodes.size() > 0;
        
        whereManager.buildClause(sbSelectFrom);
        
        // Count distinct is obviously unnecessary if we have no collection joins
        if (!hasCollectionJoinUsages) {
        	int idx = sbSelectFrom.indexOf(countString);
        	int endIdx = idx + countString.length() - 1;
        	String countStar;
        	if (jpaProvider.supportsCountStar()) {
        		countStar = "COUNT(*";
        	} else {
        		countStar = jpaProvider.getCustomFunctionInvocation("COUNT_STAR", 0);
        	}
        	for (int i = idx, j = 0; i < endIdx; i++, j++) {
        		if (j < countStar.length()) {
        			sbSelectFrom.setCharAt(i, countStar.charAt(j));
        		} else {
        			sbSelectFrom.setCharAt(i, ' ');
        		}
        	}
        }

        return sbSelectFrom.toString();
    }

    private String appendSimplePageIdQueryString(StringBuilder sbSelectFrom) {
        queryGenerator.setAliasPrefix(PAGE_POSITION_ID_QUERY_ALIAS_PREFIX);
        
        JoinNode rootNode = joinManager.getRootNodeOrFail("Paginated criteria builders do not support multiple from clause elements!");
        String idName = JpaUtils.getIdAttribute(em.getMetamodel().entity(rootNode.getPropertyClass())).getName();
        StringBuilder idClause = new StringBuilder(PAGE_POSITION_ID_QUERY_ALIAS_PREFIX);
        rootNode.appendAlias(idClause, idName);

        sbSelectFrom.append("SELECT ").append(idClause);
        // TODO: actually we should add the select clauses needed for order bys
        // TODO: if we do so, the page position function has to omit select items other than the first

        joinManager.buildClause(sbSelectFrom, EnumSet.of(ClauseType.SELECT), PAGE_POSITION_ID_QUERY_ALIAS_PREFIX, false, false);
        whereManager.buildClause(sbSelectFrom);

        Set clauses = new LinkedHashSet();
        clauses.add(idClause.toString());
        orderByManager.buildGroupByClauses(clauses);
        groupByManager.buildGroupBy(sbSelectFrom, clauses);

        boolean inverseOrder = false;
        // Resolve select aliases because we might omit the select items
        orderByManager.buildOrderBy(sbSelectFrom, inverseOrder, true);

        queryGenerator.setAliasPrefix(null);
        return sbSelectFrom.toString();
    }

    private String buildPageIdQueryString(boolean externalRepresentation) {
        StringBuilder sbSelectFrom = new StringBuilder();
        if (externalRepresentation && isMainQuery) {
            mainQuery.cteManager.buildClause(sbSelectFrom);
        }
        buildPageIdQueryString(sbSelectFrom, externalRepresentation);
        return sbSelectFrom.toString();
    }

    private String buildPageIdQueryString(StringBuilder sbSelectFrom, boolean externalRepresentation) {
        JoinNode rootNode = joinManager.getRootNodeOrFail("Paginated criteria builders do not support multiple from clause elements!");
        String idName = JpaUtils.getIdAttribute(em.getMetamodel().entity(rootNode.getPropertyClass())).getName();
        StringBuilder idClause = new StringBuilder(100);
        rootNode.appendAlias(idClause, idName);

        sbSelectFrom.append("SELECT ").append(idClause);

        if (needsNewIdList) {
            orderByManager.buildSelectClauses(sbSelectFrom, keysetExtraction);
        }

        joinManager.buildClause(sbSelectFrom, EnumSet.of(ClauseType.SELECT), null, false, externalRepresentation);

        if (keysetMode == KeysetMode.NONE) {
            whereManager.buildClause(sbSelectFrom);
        } else {
            sbSelectFrom.append(" WHERE ");

            keysetManager.buildKeysetPredicate(sbSelectFrom);

            if (whereManager.hasPredicates()) {
                sbSelectFrom.append(" AND ");
                whereManager.buildClausePredicate(sbSelectFrom);
            }
        }

        Set clauses = new LinkedHashSet();
        clauses.add(idClause.toString());
        orderByManager.buildGroupByClauses(clauses);
        groupByManager.buildGroupBy(sbSelectFrom, clauses);

        boolean inverseOrder = keysetMode == KeysetMode.PREVIOUS;
        // Resolve select aliases to their actual expressions only if the select items aren't included
        orderByManager.buildOrderBy(sbSelectFrom, inverseOrder, !needsNewIdList);

        // execute illegal collection access check
        orderByManager.acceptVisitor(new IllegalSubqueryDetector(aliasManager));

        return sbSelectFrom.toString();
    }

    @Override
    protected String buildBaseQueryString(boolean externalRepresentation) {
        StringBuilder sbSelectFrom = new StringBuilder();
        if (externalRepresentation && isMainQuery) {
            mainQuery.cteManager.buildClause(sbSelectFrom);
        }
        buildBaseQueryString(sbSelectFrom, externalRepresentation);
        return sbSelectFrom.toString();
    }

    @Override
    protected void buildBaseQueryString(StringBuilder sbSelectFrom, boolean externalRepresentation) {
        JoinNode rootNode = joinManager.getRootNodeOrFail("Paginated criteria builders do not support multiple from clause elements!");
        String idName = JpaUtils.getIdAttribute(em.getMetamodel().entity(rootNode.getPropertyClass())).getName();

        selectManager.buildSelect(sbSelectFrom);

        /**
         * we have already selected the IDs so now we only need so select the
         * fields and apply the ordering all other clauses are not required any
         * more and therefore we can also omit any joins which the SELECT or the
         * ORDER_BY clause do not depend on
         */
        joinManager.buildClause(sbSelectFrom, EnumSet.complementOf(EnumSet.of(ClauseType.SELECT, ClauseType.ORDER_BY)), null, false, externalRepresentation);
        sbSelectFrom.append(" WHERE ");
        rootNode.appendAlias(sbSelectFrom, idName);
        sbSelectFrom.append(" IN :").append(idParamName).append("");

        Set clauses = new LinkedHashSet();
        groupByManager.buildGroupByClauses(clauses);
        if (hasGroupBy) {
            selectManager.buildGroupByClauses(em.getMetamodel(), clauses);
            havingManager.buildGroupByClauses(clauses);
            orderByManager.buildGroupByClauses(clauses);
        }
        groupByManager.buildGroupBy(sbSelectFrom, clauses);

        havingManager.buildClause(sbSelectFrom);
        queryGenerator.setResolveSelectAliases(false);
        orderByManager.buildOrderBy(sbSelectFrom, false, false);
        queryGenerator.setResolveSelectAliases(true);
    }

    private String buildObjectQueryString(boolean externalRepresentation) {
        StringBuilder sbSelectFrom = new StringBuilder();
        if (externalRepresentation && isMainQuery) {
            mainQuery.cteManager.buildClause(sbSelectFrom);
        }
        buildObjectQueryString(sbSelectFrom, externalRepresentation);
        return sbSelectFrom.toString();
    }

    private String buildObjectQueryString(StringBuilder sbSelectFrom, boolean externalRepresentation) {
        selectManager.buildSelect(sbSelectFrom);

        if (keysetExtraction) {
            orderByManager.buildSelectClauses(sbSelectFrom, true);
        }

        joinManager.buildClause(sbSelectFrom, EnumSet.noneOf(ClauseType.class), null, false, externalRepresentation);

        if (keysetMode == KeysetMode.NONE) {
            whereManager.buildClause(sbSelectFrom);
        } else {
            sbSelectFrom.append(" WHERE ");

            keysetManager.buildKeysetPredicate(sbSelectFrom);

            if (whereManager.hasPredicates()) {
                sbSelectFrom.append(" AND ");
                whereManager.buildClausePredicate(sbSelectFrom);
            }
        }

        Set clauses = new LinkedHashSet();
        groupByManager.buildGroupByClauses(clauses);
        if (hasGroupBy) {
            selectManager.buildGroupByClauses(em.getMetamodel(), clauses);
            havingManager.buildGroupByClauses(clauses);
            orderByManager.buildGroupByClauses(clauses);
        }
        groupByManager.buildGroupBy(sbSelectFrom, clauses);

        havingManager.buildClause(sbSelectFrom);

        boolean inverseOrder = keysetMode == KeysetMode.PREVIOUS;
        orderByManager.buildOrderBy(sbSelectFrom, inverseOrder, false);

        // execute illegal collection access check
        orderByManager.acceptVisitor(new IllegalSubqueryDetector(aliasManager));

        return sbSelectFrom.toString();
    }

    @Override
    public PaginatedCriteriaBuilder distinct() {
        throw new IllegalStateException("Calling distinct() on a PaginatedCriteriaBuilder is not allowed.");
    }

    @Override
    public PaginatedCriteriaBuilder groupBy(String... paths) {
        throw new IllegalStateException("Calling groupBy() on a PaginatedCriteriaBuilder is not allowed.");
    }

    @Override
    public PaginatedCriteriaBuilder groupBy(String expression) {
        throw new IllegalStateException("Calling groupBy() on a PaginatedCriteriaBuilder is not allowed.");
    }

    @Override
    @SuppressWarnings("unchecked")
    public  SelectObjectBuilder> selectNew(Class clazz) {
        return (SelectObjectBuilder>) super.selectNew(clazz);
    }

    @Override
    @SuppressWarnings("unchecked")
    public  PaginatedCriteriaBuilder selectNew(ObjectBuilder builder) {
        return (PaginatedCriteriaBuilder) super.selectNew(builder);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy