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

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

The newest version!
/*
 * Copyright 2015 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.Query;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;

import com.blazebit.persistence.BaseCTECriteriaBuilder;
import com.blazebit.persistence.CommonQueryBuilder;
import com.blazebit.persistence.SelectBuilder;
import com.blazebit.persistence.impl.expression.PathExpression;
import com.blazebit.persistence.impl.expression.PropertyExpression;
import com.blazebit.persistence.impl.query.CTEQuerySpecification;
import com.blazebit.persistence.impl.query.QuerySpecification;
import com.blazebit.persistence.spi.DbmsStatementType;
import com.blazebit.persistence.spi.SetOperationType;

/**
 *
 * @param  The criteria builder returned after the cte builder
 * @param  The concrete builder type
 * @param  The builder type that should be returned on set operations
 * @author Christian Beikov
 * @since 1.1.0
 */
public abstract class AbstractCTECriteriaBuilder, Z, W> extends AbstractCommonQueryBuilder> implements BaseCTECriteriaBuilder, SelectBuilder, CTEInfoBuilder {
	
	protected static final Integer EMPTY = Integer.valueOf(-1);
	protected final Y result;
	protected final CTEBuilderListener listener;
	protected final String cteName;
	protected final EntityType cteType;
	protected final Map bindingMap;
	protected final CTEBuilderListenerImpl subListener;

    public AbstractCTECriteriaBuilder(MainQuery mainQuery, String cteName, Class clazz, Y result, CTEBuilderListener listener, BaseFinalSetOperationCTECriteriaBuilderImpl finalSetOperationBuilder) {
        super(mainQuery, false, DbmsStatementType.SELECT, clazz, null, finalSetOperationBuilder);
        this.result = result;
        this.listener = listener;

        this.cteType = em.getMetamodel().entity(clazz);
        this.cteName = cteName;
        this.bindingMap = new LinkedHashMap();
        this.subListener = new CTEBuilderListenerImpl();
    }
    
    public CTEBuilderListenerImpl getSubListener() {
        return subListener;
    }

    @Override
    protected void buildExternalQueryString(StringBuilder sbSelectFrom) {
        buildBaseQueryString(sbSelectFrom, true);
        applyJpaLimit(sbSelectFrom);
    }

	@Override
    protected Query getQuery() {
        Query query;
        
        if (hasLimit()) {
            // We need to change the underlying sql when doing a limit
            query = em.createQuery(getBaseQueryStringWithCheck());

            Set parameterListNames = parameterManager.getParameterListNames(query);
            String limit = null;
            String offset = null;

            // The main query will handle that separately
            if (!isMainQuery) {
                if (firstResult != 0) {
                    offset = Integer.toString(firstResult);
                }
                if (maxResults != Integer.MAX_VALUE) {
                    limit = Integer.toString(maxResults);
                }
            }

            QuerySpecification querySpecification = new CTEQuerySpecification(
                    this,
                    query,
                    parameterListNames,
                    limit,
                    offset
            );

            query = new CustomSQLQuery(
                    querySpecification,
                    query,
                    (CommonQueryBuilder) this,
                    cbf.getExtendedQuerySupport(),
                    parameterManager.getValuesParameters(),
                    parameterManager.getValuesBinders()
            );
        } else {
            query = em.createQuery(getBaseQueryStringWithCheck());
        }

        parameterManager.parameterizeQuery(query);
        return query;
    }

    public SelectBuilder bind(String cteAttribute) {
        // NOTE: Since CTEs can't have embeddables right now, we can skip resolving that
		Attribute attribute = cteType.getAttribute(cteAttribute);
		
		if (attribute == null) {
			throw new IllegalArgumentException("The cte attribute [" + cteAttribute + "] does not exist!");
		}
		if (bindingMap.containsKey(cteAttribute)) {
			throw new IllegalArgumentException("The cte attribute [" + cteAttribute + "] has already been bound!");
		}
		
		bindingMap.put(cteAttribute, selectManager.getSelectInfos().size());
		return this;
	}

    public Y end() {
        listener.onBuilderEnded(this);
        return result;
    }
    
    public CTEInfo createCTEInfo() {
        List attributes = prepareAndGetAttributes();
        CTEInfo info = new CTEInfo(cteName, cteType, attributes, false, false, this, null);
        return info;
    }
    
    protected List prepareAndGetAttributes() {
        List attributes = new ArrayList(bindingMap.size());
        for (Map.Entry bindingEntry : bindingMap.entrySet()) {
            final String attributeName = bindingEntry.getKey();
            // NOTE: Since CTEs can't have embeddables right now, we can skip resolving that
            Attribute attribute = cteType.getAttribute(attributeName);
            attributes.add(attributeName);
            
            if (JpaUtils.isJoinable(attribute)) {
                // We have to map *-to-one relationships to their ids
                EntityType type = em.getMetamodel().entity(JpaUtils.resolveFieldClass(cteType.getJavaType(), attribute));
                Attribute idAttribute = JpaUtils.getIdAttribute(type);
                // NOTE: Since we are talking about *-to-ones, the expression can only be a path to an object
                // so it is safe to just append the id to the path
                PathExpression pathExpression = (PathExpression) selectManager.getSelectInfos().get(bindingEntry.getValue()).getExpression();
                pathExpression.getExpressions().add(new PropertyExpression(idAttribute.getName()));
            }
        }
        
        return attributes;
    }
    
    protected BaseFinalSetOperationCTECriteriaBuilderImpl createFinalSetOperationBuilder(SetOperationType operator, boolean nested, boolean isSubquery) {
        FullSelectCTECriteriaBuilderImpl newInitiator = finalSetOperationBuilder == null ? null : finalSetOperationBuilder.getInitiator();
        return createFinalSetOperationBuilder(operator, nested, isSubquery, newInitiator);
    }
    
    @SuppressWarnings("unchecked")
    protected BaseFinalSetOperationCTECriteriaBuilderImpl createFinalSetOperationBuilder(SetOperationType operator, boolean nested, boolean isSubquery, FullSelectCTECriteriaBuilderImpl initiator) {
        CTEBuilderListener newListener = finalSetOperationBuilder == null ? listener : finalSetOperationBuilder.getSubListener();
        Y newResult = finalSetOperationBuilder == null ? result : (Y) finalSetOperationBuilder.getResult();
        
        if (isSubquery) {
            return new OngoingFinalSetOperationCTECriteriaBuilderImpl(mainQuery, (Class) cteType.getJavaType(), newResult, operator, nested, newListener, initiator);
        } else {
            return new FinalSetOperationCTECriteriaBuilderImpl(mainQuery, (Class) cteType.getJavaType(), newResult, operator, nested, newListener, initiator);
        }
    }

    @SuppressWarnings("unchecked")
    protected LeafOngoingSetOperationCTECriteriaBuilderImpl createLeaf(BaseFinalSetOperationCTECriteriaBuilderImpl finalSetOperationBuilder) {
        CTEBuilderListener newListener = finalSetOperationBuilder.getSubListener();
        LeafOngoingSetOperationCTECriteriaBuilderImpl next = new LeafOngoingSetOperationCTECriteriaBuilderImpl(mainQuery, cteName, (Class) cteType.getJavaType(), result, newListener, (FinalSetOperationCTECriteriaBuilderImpl) finalSetOperationBuilder);
        newListener.onBuilderStarted(next);
        return next;
    }

    @SuppressWarnings("unchecked")
    protected  OngoingSetOperationCTECriteriaBuilderImpl createOngoing(BaseFinalSetOperationCTECriteriaBuilderImpl finalSetOperationBuilder, T endSetResult) {
        // TODO: This is such an ugly hack, but I don't know how else to fix this generics issue for now
        finalSetOperationBuilder.setEndSetResult((T) endSetResult);
        
        CTEBuilderListener newListener = finalSetOperationBuilder.getSubListener();
        OngoingSetOperationCTECriteriaBuilderImpl next = new OngoingSetOperationCTECriteriaBuilderImpl(mainQuery, cteName, (Class) cteType.getJavaType(), result, newListener, (OngoingFinalSetOperationCTECriteriaBuilderImpl) finalSetOperationBuilder, endSetResult);
        newListener.onBuilderStarted(next);
        return next;
    }

}