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

org.hibernate.loader.criteria.CriteriaJoinWalker Maven / Gradle / Ivy

There is a newer version: 6.5.0.CR2
Show newest version
/*
 * 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 .
 */
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 JoinWalker for Criteria 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 userAliasList = new ArrayList();
	private final List resultTypeList = new ArrayList();
	private final List includeInResultRowList = new ArrayList();

	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() );
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy