outerjoinloader.java
来自「hibernate-3.0.5 中文文档」· Java 代码 · 共 989 行 · 第 1/2 页
JAVA
989 行
//$Id: OuterJoinLoader.java,v 1.46 2005/05/19 07:28:59 steveebersole Exp $package org.hibernate.loader;import java.util.ArrayList;import java.util.Arrays;import java.util.HashSet;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.Set;import org.hibernate.FetchMode;import org.hibernate.LockMode;import org.hibernate.MappingException;import org.hibernate.dialect.Dialect;import org.hibernate.engine.JoinHelper;import org.hibernate.engine.SessionFactoryImplementor;import org.hibernate.persister.collection.CollectionPersister;import org.hibernate.persister.collection.QueryableCollection;import org.hibernate.persister.entity.EntityPersister;import org.hibernate.persister.entity.Joinable;import org.hibernate.persister.entity.Loadable;import org.hibernate.persister.entity.OuterJoinLoadable;import org.hibernate.sql.ConditionFragment;import org.hibernate.sql.DisjunctionFragment;import org.hibernate.sql.InFragment;import org.hibernate.sql.JoinFragment;import org.hibernate.type.AbstractComponentType;import org.hibernate.type.AssociationType;import org.hibernate.type.EntityType;import org.hibernate.type.ForeignKeyDirection;import org.hibernate.type.Type;import org.hibernate.util.ArrayHelper;import org.hibernate.util.StringHelper;/** * Implements logic for walking a tree of associated classes. * * Generates an SQL select string containing all properties of those classes. * Tables are joined using an ANSI-style left outer join. * * @author Gavin King, Jon Lipsky */public abstract class OuterJoinLoader extends BasicLoader { protected Loadable[] persisters; protected CollectionPersister[] collectionPersisters; protected int[] collectionOwners; protected String[] aliases; protected LockMode[] lockModeArray; protected int[] owners; protected EntityType[] ownerAssociationType; protected String sql; protected String[] suffixes; protected String[] collectionSuffixes; private Map enabledFilters; protected final Dialect getDialect() { return getFactory().getDialect(); } public OuterJoinLoader(SessionFactoryImplementor factory, Map enabledFilters) { super(factory); this.enabledFilters = enabledFilters; } /** * Override on subclasses to enable or suppress joining * of certain association types */ protected boolean isJoinedFetchEnabled(AssociationType type, FetchMode config) { return type.isEntityType() && isJoinedFetchEnabledInMapping(config, type) ; } /** * Uniquely identifier a foreign key, so that we don't * join it more than once, and create circularities */ private static final class AssociationKey { private String[] columns; private String table; private AssociationKey(String[] columns, String table) { this.columns = columns; this.table = table; } public boolean equals(Object other) { AssociationKey that = (AssociationKey) other; return that.table.equals(table) && Arrays.equals(columns, that.columns); } public int hashCode() { return table.hashCode(); //TODO: inefficient } } /** * Used to detect circularities in the joined graph */ protected boolean isDuplicateAssociation( final Set visitedAssociationKeys, final String lhsTable, final String[] lhsColumnNames, final AssociationType type ) { final String foreignKeyTable; final String[] foreignKeyColumns; if ( type.getForeignKeyDirection()==ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT ) { foreignKeyTable = lhsTable; foreignKeyColumns = lhsColumnNames; } else { foreignKeyTable = type.getAssociatedJoinable( getFactory() ).getTableName(); foreignKeyColumns = JoinHelper.getRHSColumnNames( type, getFactory() ); } return isDuplicateAssociation(visitedAssociationKeys, foreignKeyTable, foreignKeyColumns); } /** * Should we join this association? */ protected boolean isJoinable( final int joinType, final Set visitedAssociationKeys, final String lhsTable, final String[] lhsColumnNames, final AssociationType type, final int depth ) { if (joinType<0) return false; if (joinType==JoinFragment.INNER_JOIN) return true; Integer maxFetchDepth = getFactory().getSettings().getMaximumFetchDepth(); final boolean tooDeep = maxFetchDepth!=null && depth >= maxFetchDepth.intValue(); return !tooDeep && !isDuplicateAssociation( visitedAssociationKeys, lhsTable, lhsColumnNames, type ); } /** * Used to detect circularities in the joined graph */ protected boolean isDuplicateAssociation( final Set visitedAssociationKeys, final String foreignKeyTable, final String[] foreignKeyColumns ) { return !visitedAssociationKeys.add( new AssociationKey(foreignKeyColumns, foreignKeyTable) ); } protected boolean isTooDeep(int currentDepth) { Integer maxFetchDepth = getFactory().getSettings().getMaximumFetchDepth(); return maxFetchDepth!=null && currentDepth >= maxFetchDepth.intValue(); } /** * Get the join type (inner, outer, etc) or -1 if the * association should not be joined. Override on * subclasses. */ protected int getJoinType( AssociationType type, FetchMode config, String path, Set visitedAssociations, String lhsTable, String[] lhsColumns, boolean nullable, int currentDepth) throws MappingException { if ( !isJoinedFetchEnabled(type, config) ) return -1; if ( isTooDeep(currentDepth) ) return -1; final boolean dupe = isDuplicateAssociation(visitedAssociations, lhsTable, lhsColumns, type); if (dupe) return -1; return getJoinType(nullable, currentDepth); } /** * Use an inner join if it is a non-null association and this * is the "first" join in a series */ protected int getJoinType(boolean nullable, int currentDepth) { //TODO: this is too conservative; if all preceding joins were // also inner joins, we could use an inner join here return !nullable && currentDepth==0 ? JoinFragment.INNER_JOIN : JoinFragment.LEFT_OUTER_JOIN; } /** * For an entity class, return a list of associations to be fetched by outerjoin */ protected final List walkEntityTree(OuterJoinLoadable persister, String alias) throws MappingException { List associations = new ArrayList(); walkEntityTree(persister, alias, associations, new HashSet(), "", 0); return associations; } /** * For a collection role, return a list of associations to be fetched by outerjoin */ protected final List walkCollectionTree(QueryableCollection persister, String alias) throws MappingException { return walkCollectionTree(persister, alias, new ArrayList(), new HashSet(), "", 0); //TODO: when this is the entry point, we should use an INNER_JOIN for fetching the many-to-many elements! } /** * For a collection role, return a list of associations to be fetched by outerjoin */ private final List walkCollectionTree( final QueryableCollection persister, final String alias, final List associations, final Set visitedAssociations, final String path, final int currentDepth) throws MappingException { if ( persister.isOneToMany() ) { walkEntityTree( (OuterJoinLoadable) persister.getElementPersister(), alias, associations, visitedAssociations, path, currentDepth ); } else { Type type = persister.getElementType(); if ( type.isAssociationType() ) { // a many-to-many; // decrement currentDepth here to allow join across the association table // without exceeding MAX_FETCH_DEPTH (i.e. the "currentDepth - 1" bit) AssociationType associationType = (AssociationType) type; String[] aliasedLhsColumns = persister.getElementColumnNames(alias); String[] lhsColumns = persister.getElementColumnNames(); // if the current depth is 0, the root thing being loaded is the // many-to-many collection itself. Here, it is alright to use // an inner join... boolean useInnerJoin = currentDepth == 0; final int joinType = getJoinType( associationType, persister.getFetchMode(), path, visitedAssociations, persister.getTableName(), lhsColumns, !useInnerJoin, currentDepth - 1 ); addAssociationToJoinTreeIfNecessary( associationType, aliasedLhsColumns, alias, associations, visitedAssociations, path, currentDepth - 1, joinType ); } else if ( type.isComponentType() ) { walkCompositeElementTree( (AbstractComponentType) type, persister.getElementColumnNames(), persister, alias, associations, new HashSet(), path, currentDepth ); } } return associations; } /** * Walk the tree for a particular entity association */ private final void walkEntityAssociationTree( final AssociationType associationType, final OuterJoinLoadable persister, final int propertyNumber, final String alias, final List associations, final Set visitedAssociations, final String path, final boolean nullable, final int currentDepth) throws MappingException { String[] aliasedLhsColumns = JoinHelper.getAliasedLHSColumnNames( associationType, alias, propertyNumber, persister, getFactory() ); String[] lhsColumns = JoinHelper.getLHSColumnNames( associationType, propertyNumber, persister, getFactory() ); String lhsTable = JoinHelper.getLHSTableName(associationType, propertyNumber, persister); String subpath = subPath( path, persister.getSubclassPropertyName(propertyNumber) ); int joinType = getJoinType( associationType, persister.getFetchMode(propertyNumber), subpath, visitedAssociations, lhsTable, lhsColumns, nullable, currentDepth ); addAssociationToJoinTreeIfNecessary( associationType, aliasedLhsColumns, alias, associations, visitedAssociations, subpath, currentDepth, joinType ); } /** * For an entity class, add to a list of associations to be fetched * by outerjoin */ private final void walkEntityTree( final OuterJoinLoadable persister, final String alias, final List associations, final Set visitedAssociations, final String path, final int currentDepth) throws MappingException { int n = persister.countSubclassProperties(); for ( int i=0; i<n; i++ ) { Type type = persister.getSubclassPropertyType(i); if ( type.isAssociationType() ) { walkEntityAssociationTree( (AssociationType) type, persister, i, alias, associations, visitedAssociations, path, persister.isSubclassPropertyNullable(i), currentDepth ); } else if ( type.isComponentType() ) { walkComponentTree( (AbstractComponentType) type, i, 0, persister, alias, associations, visitedAssociations, subPath( path, persister.getSubclassPropertyName(i) ), currentDepth ); } } } /** * For a component, add to a list of associations to be fetched by outerjoin */ private void walkComponentTree( final AbstractComponentType componentType, final int propertyNumber, int begin, final OuterJoinLoadable persister, final String alias, final List associations, final Set visitedAssociations, final String path, final int currentDepth ) throws MappingException { Type[] types = componentType.getSubtypes(); String[] propertyNames = componentType.getPropertyNames(); for ( int i=0; i <types.length; i++ ) { if ( types[i].isAssociationType() ) { AssociationType associationType = (AssociationType) types[i]; String[] aliasedLhsColumns = JoinHelper.getAliasedLHSColumnNames( associationType, alias, propertyNumber, begin, persister, getFactory() ); String[] lhsColumns = JoinHelper.getLHSColumnNames( associationType, propertyNumber, begin, persister, getFactory() ); String lhsTable = JoinHelper.getLHSTableName(associationType, propertyNumber, persister); String subpath = subPath( path, propertyNames[i] ); final boolean[] propertyNullability = componentType.getPropertyNullability(); final int joinType = getJoinType( associationType, componentType.getFetchMode(i), subpath, visitedAssociations, lhsTable, lhsColumns, propertyNullability==null || propertyNullability[i], currentDepth ); addAssociationToJoinTreeIfNecessary( associationType, aliasedLhsColumns, alias, associations, visitedAssociations, subpath, currentDepth, joinType ); } else if ( types[i].isComponentType() ) { String subpath = subPath( path, propertyNames[i] ); walkComponentTree( (AbstractComponentType) types[i], propertyNumber, begin, persister, alias, associations, visitedAssociations, subpath, currentDepth ); } begin+=types[i].getColumnSpan( getFactory() ); } } /** * For a composite element, add to a list of associations to be fetched by outerjoin */ private void walkCompositeElementTree( final AbstractComponentType compositeType, final String[] cols, final QueryableCollection persister, final String alias, final List associations, final Set visitedAssociations, final String path, final int currentDepth) throws MappingException { Type[] types = compositeType.getSubtypes(); String[] propertyNames = compositeType.getPropertyNames(); int begin = 0; for ( int i=0; i <types.length; i++ ) { int length = types[i].getColumnSpan( getFactory() ); String[] lhsColumns = ArrayHelper.slice(cols, begin, length); if ( types[i].isAssociationType() ) { AssociationType associationType = (AssociationType) types[i]; // simple, because we can't have a one-to-one or a collection // (or even a property-ref) in a composite-element: String[] aliasedLhsColumns = StringHelper.qualify(alias, lhsColumns); String subpath = subPath( path, propertyNames[i] ); final boolean[] propertyNullability = compositeType.getPropertyNullability(); final int joinType = getJoinType( associationType, compositeType.getFetchMode(i), subpath,
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?