org.hibernate.loader.criteria.CriteriaJoinWalker.java Source code

Java tutorial

Introduction

Here is the source code for org.hibernate.loader.criteria.CriteriaJoinWalker.java

Source

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */
package org.hibernate.loader.criteria;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CriteriaImpl;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.loader.AbstractEntityJoinWalker;
import org.hibernate.loader.PropertyPath;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.JoinType;
import org.hibernate.type.AssociationType;
import org.hibernate.type.Type;

/**
 * A <tt>JoinWalker</tt> for <tt>Criteria</tt> queries.
 *
 * @author Gavin King
 * @see CriteriaLoader
 */
public class CriteriaJoinWalker extends AbstractEntityJoinWalker {

    //TODO: add a CriteriaImplementor interface
    //      this class depends directly upon CriteriaImpl in the impl package...

    private final CriteriaQueryTranslator translator;
    private final Set querySpaces;
    private final Type[] resultTypes;
    private final boolean[] includeInResultRow;

    //the user visible aliases, which are unknown to the superclass,
    //these are not the actual "physical" SQL aliases
    private final String[] userAliases;
    private final List<String> userAliasList = new ArrayList<String>();
    private final List<Type> resultTypeList = new ArrayList<Type>();
    private final List<Boolean> includeInResultRowList = new ArrayList<Boolean>();

    public Type[] getResultTypes() {
        return resultTypes;
    }

    public String[] getUserAliases() {
        return userAliases;
    }

    public boolean[] includeInResultRow() {
        return includeInResultRow;
    }

    public CriteriaJoinWalker(final OuterJoinLoadable persister, final CriteriaQueryTranslator translator,
            final SessionFactoryImplementor factory, final CriteriaImpl criteria, final String rootEntityName,
            final LoadQueryInfluencers loadQueryInfluencers) {
        this(persister, translator, factory, criteria, rootEntityName, loadQueryInfluencers, null);
    }

    public CriteriaJoinWalker(final OuterJoinLoadable persister, final CriteriaQueryTranslator translator,
            final SessionFactoryImplementor factory, final CriteriaImpl criteria, final String rootEntityName,
            final LoadQueryInfluencers loadQueryInfluencers, final String alias) {
        super(persister, factory, loadQueryInfluencers, alias);

        this.translator = translator;

        querySpaces = translator.getQuerySpaces();

        if (translator.hasProjection()) {
            initProjection(translator.getSelect(), translator.getWhereCondition(), translator.getOrderBy(),
                    translator.getGroupBy(), LockOptions.NONE);
            resultTypes = translator.getProjectedTypes();
            userAliases = translator.getProjectedAliases();
            includeInResultRow = new boolean[resultTypes.length];
            Arrays.fill(includeInResultRow, true);
        } else {
            initAll(translator.getWhereCondition(), translator.getOrderBy(), LockOptions.NONE);
            // root entity comes last
            userAliasList.add(criteria.getAlias()); //root entity comes *last*
            resultTypeList.add(translator.getResultType(criteria));
            includeInResultRowList.add(true);
            userAliases = ArrayHelper.toStringArray(userAliasList);
            resultTypes = ArrayHelper.toTypeArray(resultTypeList);
            includeInResultRow = ArrayHelper.toBooleanArray(includeInResultRowList);
        }
    }

    @Override
    protected JoinType getJoinType(OuterJoinLoadable persister, final PropertyPath path, int propertyNumber,
            AssociationType associationType, FetchMode metadataFetchMode, CascadeStyle metadataCascadeStyle,
            String lhsTable, String[] lhsColumns, final boolean nullable, final int currentDepth)
            throws MappingException {
        final JoinType resolvedJoinType;
        if (translator.isJoin(path.getFullPath())) {
            resolvedJoinType = translator.getJoinType(path.getFullPath());
        } else {
            if (translator.hasProjection()) {
                resolvedJoinType = JoinType.NONE;
            } else {
                String fullPathWithAlias = path.getFullPath();
                String rootAlias = translator.getRootCriteria().getAlias();
                String rootAliasPathPrefix = rootAlias + ".";
                if (rootAlias != null && !fullPathWithAlias.startsWith(rootAliasPathPrefix)) {
                    fullPathWithAlias = rootAliasPathPrefix + fullPathWithAlias;
                }

                FetchMode fetchMode = translator.getRootCriteria().getFetchMode(fullPathWithAlias);
                if (isDefaultFetchMode(fetchMode)) {
                    if (persister != null) {
                        if (isJoinFetchEnabledByProfile(persister, path, propertyNumber)) {
                            if (isDuplicateAssociation(lhsTable, lhsColumns, associationType)) {
                                resolvedJoinType = JoinType.NONE;
                            } else if (isTooDeep(currentDepth)
                                    || (associationType.isCollectionType() && isTooManyCollections())) {
                                resolvedJoinType = JoinType.NONE;
                            } else {
                                resolvedJoinType = getJoinType(nullable, currentDepth);
                            }
                        } else {
                            resolvedJoinType = super.getJoinType(persister, path, propertyNumber, associationType,
                                    metadataFetchMode, metadataCascadeStyle, lhsTable, lhsColumns, nullable,
                                    currentDepth);
                        }
                    } else {
                        resolvedJoinType = super.getJoinType(associationType, metadataFetchMode, path, lhsTable,
                                lhsColumns, nullable, currentDepth, metadataCascadeStyle);

                    }
                } else {
                    if (fetchMode == FetchMode.JOIN) {
                        isDuplicateAssociation(lhsTable, lhsColumns, associationType); //deliberately ignore return value!
                        resolvedJoinType = getJoinType(nullable, currentDepth);
                    } else {
                        resolvedJoinType = JoinType.NONE;
                    }
                }
            }
        }
        return resolvedJoinType;
    }

    @Override
    protected JoinType getJoinType(AssociationType associationType, FetchMode config, PropertyPath path,
            String lhsTable, String[] lhsColumns, boolean nullable, int currentDepth, CascadeStyle cascadeStyle)
            throws MappingException {
        return getJoinType(null, path, -1, associationType, config, cascadeStyle, lhsTable, lhsColumns, nullable,
                currentDepth);
    }

    private static boolean isDefaultFetchMode(FetchMode fetchMode) {
        return fetchMode == null || fetchMode == FetchMode.DEFAULT;
    }

    /**
     * Use the discriminator, to narrow the select to instances
     * of the queried subclass, also applying any filters.
     */
    @Override
    protected String getWhereFragment() throws MappingException {
        return super.getWhereFragment() + ((Queryable) getPersister()).filterFragment(getAlias(),
                getLoadQueryInfluencers().getEnabledFilters());
    }

    @Override
    protected String generateTableAlias(int n, PropertyPath path, Joinable joinable) {
        // TODO: deal with side-effects (changes to includeInResultRowList, userAliasList, resultTypeList)!!!

        // for collection-of-entity, we are called twice for given "path"
        // once for the collection Joinable, once for the entity Joinable.
        // the second call will/must "consume" the alias + perform side effects according to consumesEntityAlias()
        // for collection-of-other, however, there is only one call 
        // it must "consume" the alias + perform side effects, despite what consumeEntityAlias() return says
        // 
        // note: the logic for adding to the userAliasList is still strictly based on consumesEntityAlias return value
        boolean checkForSqlAlias = joinable.consumesEntityAlias();

        if (!checkForSqlAlias && joinable.isCollection()) {
            // is it a collection-of-other (component or value) ?
            CollectionPersister collectionPersister = (CollectionPersister) joinable;
            Type elementType = collectionPersister.getElementType();
            if (elementType.isComponentType() || !elementType.isEntityType()) {
                checkForSqlAlias = true;
            }
        }

        String sqlAlias = null;

        if (checkForSqlAlias) {
            final Criteria subcriteria = translator.getCriteria(path.getFullPath());
            sqlAlias = subcriteria == null ? null : translator.getSQLAlias(subcriteria);

            if (joinable.consumesEntityAlias() && !translator.hasProjection()) {
                includeInResultRowList.add(subcriteria != null && subcriteria.getAlias() != null);
                if (sqlAlias != null) {
                    if (subcriteria.getAlias() != null) {
                        userAliasList.add(subcriteria.getAlias());
                        resultTypeList.add(translator.getResultType(subcriteria));
                    }
                }
            }
        }

        if (sqlAlias == null) {
            sqlAlias = super.generateTableAlias(n + translator.getSQLAliasCount(), path, joinable);
        }

        return sqlAlias;
    }

    @Override
    protected String generateRootAlias(String tableName) {
        return CriteriaQueryTranslator.ROOT_SQL_ALIAS;
    }

    public Set getQuerySpaces() {
        return querySpaces;
    }

    @Override
    public String getComment() {
        return "criteria query";
    }

    @Override
    protected String getWithClause(PropertyPath path) {
        return translator.getWithClause(path.getFullPath());
    }

    @Override
    protected boolean hasRestriction(PropertyPath path) {
        return translator.hasRestriction(path.getFullPath());
    }
}