📄 joinwalker.java
字号:
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, persister.getTableName(), lhsColumns, propertyNullability==null || propertyNullability[i], currentDepth, compositeType.getCascadeStyle(i) ); addAssociationToJoinTreeIfNecessary( associationType, aliasedLhsColumns, alias, subpath, currentDepth, joinType ); } else if ( types[i].isComponentType() ) { String subpath = subPath( path, propertyNames[i] ); walkCompositeElementTree( (AbstractComponentType) types[i], lhsColumns, persister, alias, subpath, currentDepth ); } begin+=length; } } /** * Extend the path by the given property name */ private static String subPath(String path, String property) { if ( path==null || path.length()==0) { return property; } else { return StringHelper.qualify(path, property); } } /** * 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, String lhsTable, String[] lhsColumns, boolean nullable, int currentDepth, CascadeStyle cascadeStyle) throws MappingException { if ( !isJoinedFetchEnabled(type, config, cascadeStyle) ) return -1; if ( isTooDeep(currentDepth) || ( type.isCollectionType() && isTooManyCollections() ) ) return -1; final boolean dupe = isDuplicateAssociation(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; } protected boolean isTooDeep(int currentDepth) { Integer maxFetchDepth = getFactory().getSettings().getMaximumFetchDepth(); return maxFetchDepth!=null && currentDepth >= maxFetchDepth.intValue(); } protected boolean isTooManyCollections() { return false; } /** * Does the mapping, and Hibernate default semantics, specify that * this association should be fetched by outer joining */ protected boolean isJoinedFetchEnabledInMapping(FetchMode config, AssociationType type) throws MappingException { if ( !type.isEntityType() && !type.isCollectionType() ) { return false; } else { if (config==FetchMode.JOIN) return true; if (config==FetchMode.SELECT) return false; if ( type.isEntityType() ) { //TODO: look at the owning property and check that it // isn't lazy (by instrumentation) EntityType entityType =(EntityType) type; EntityPersister persister = getFactory().getEntityPersister( entityType.getAssociatedEntityName() ); return !persister.hasProxy(); } else { return false; } } } /** * Override on subclasses to enable or suppress joining * of certain association types */ protected boolean isJoinedFetchEnabled(AssociationType type, FetchMode config, CascadeStyle cascadeStyle) { return type.isEntityType() && isJoinedFetchEnabledInMapping(config, type) ; } protected String generateTableAlias( final int n, final String path, final Joinable joinable ) { return StringHelper.generateAlias( joinable.getName(), n ); } protected String generateRootAlias(final String description) { return StringHelper.generateAlias(description, 0); } /** * Used to detect circularities in the joined graph, note that * this method is side-effecty */ protected boolean isDuplicateAssociation( final String foreignKeyTable, final String[] foreignKeyColumns ) { AssociationKey associationKey = new AssociationKey(foreignKeyColumns, foreignKeyTable); return !visitedAssociationKeys.add( associationKey ); } /** * Used to detect circularities in the joined graph, note that * this method is side-effecty */ protected boolean isDuplicateAssociation( 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(foreignKeyTable, foreignKeyColumns); } /** * 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 } } /** * 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(lhsTable, lhsColumnNames, type); } protected String orderBy(final List associations, final String orderBy) { return mergeOrderings( orderBy( associations ), orderBy ); } protected static String mergeOrderings(String ordering1, String ordering2) { if ( ordering1.length() == 0 ) { return ordering2; } else if ( ordering2.length() == 0 ) { return ordering1; } else { return ordering1 + ", " + ordering2; } } /** * Generate a sequence of <tt>LEFT OUTER JOIN</tt> clauses for the given associations. */ protected final JoinFragment mergeOuterJoins(List associations) throws MappingException { JoinFragment outerjoin = getDialect().createOuterJoinFragment(); Iterator iter = associations.iterator(); OuterJoinableAssociation last = null; while ( iter.hasNext() ) { OuterJoinableAssociation oj = (OuterJoinableAssociation) iter.next(); if ( last != null && last.isManyToManyWith( oj ) ) { oj.addManyToManyJoin( outerjoin, ( QueryableCollection ) last.getJoinable() ); } else { oj.addJoins(outerjoin); } last = oj; } last = null; return outerjoin; } /** * Count the number of instances of Joinable which are actually * also instances of Loadable, or are one-to-many associations */ protected static final int countEntityPersisters(List associations) throws MappingException { int result = 0; Iterator iter = associations.iterator(); while ( iter.hasNext() ) { OuterJoinableAssociation oj = (OuterJoinableAssociation) iter.next(); if ( oj.getJoinable().consumesEntityAlias() ) { result++; } } return result; } /** * Count the number of instances of Joinable which are actually * also instances of PersistentCollection which are being fetched * by outer join */ protected static final int countCollectionPersisters(List associations) throws MappingException { int result = 0; Iterator iter = associations.iterator(); while ( iter.hasNext() ) { OuterJoinableAssociation oj = (OuterJoinableAssociation) iter.next(); if ( oj.getJoinType()==JoinFragment.LEFT_OUTER_JOIN && oj.getJoinable().isCollection() ) { result++; } } return result; } /** * Get the order by string required for collection fetching */ protected static final String orderBy(List associations) throws MappingException { StringBuffer buf = new StringBuffer(); Iterator iter = associations.iterator(); OuterJoinableAssociation last = null; while ( iter.hasNext() ) { OuterJoinableAssociation oj = (OuterJoinableAssociation) iter.next(); if ( oj.getJoinType() == JoinFragment.LEFT_OUTER_JOIN ) { // why does this matter? if ( oj.getJoinable().isCollection() ) { final QueryableCollection queryableCollection = (QueryableCollection) oj.getJoinable(); if ( queryableCollection.hasOrdering() ) { final String orderByString = queryableCollection.getSQLOrderByString( oj.getRHSAlias() ); buf.append( orderByString ).append(", "); } } else { // it might still need to apply a collection ordering based on a // many-to-many defined order-by... if ( last != null && last.getJoinable().isCollection() ) { final QueryableCollection queryableCollection = (QueryableCollection) last.getJoinable(); if ( queryableCollection.isManyToMany() && last.isManyToManyWith( oj ) ) { if ( queryableCollection.hasManyToManyOrdering() ) { final String orderByString = queryableCollection.getManyToManyOrderByString( oj.getRHSAlias() ); buf.append( orderByString ).append(", "); } } } } } last = oj; } if ( buf.length()>0 ) buf.setLength( buf.length()-2 ); return buf.toString(); } /** * Render the where condition for a (batch) load by identifier / collection key */ protected StringBuffer whereString(String alias, String[] columnNames, int batchSize) { if ( columnNames.length==1 ) { // if not a composite key, use "foo in (?, ?, ?)" for batching // if no batch, and not a composite key, use "foo = ?" InFragment in = new InFragment().setColumn( alias, columnNames[0] ); for ( int i=0; i<batchSize; i++ ) in.addValue("?"); return new StringBuffer( in.toFragmentString() ); } else { //a composite key ConditionFragment byId = new ConditionFragment() .setTableAlias(alias) .setCondition( columnNames, "?" ); StringBuffer whereString = new StringBuffer(); if ( batchSize==1 ) { // if no batch, use "foo = ? and bar = ?" whereString.append( byId.toFragmentString() ); } else { // if a composite key, use "( (foo = ? and bar = ?) or (foo = ? and bar = ?) )" for batching whereString.append('('); //TODO: unnecessary for databases with ANSI-style joins DisjunctionFragment df = new DisjunctionFragment(); for ( int i=0; i<batchSize; i++ ) { df.addCondition(byId); } whereString.append( df.toFragmentString() ); whereString.append(')'); //TODO: unnecessary for databases with ANSI-style joins } return whereString; } } protected void initPersisters(final List associations, final LockMode lockMode) throws MappingException { final int joins = countEntityPersisters(associations); final int collections = countCollectionPersisters(associations); collectionOwners = collections==0 ? null : new int[collections]; collectionPersisters = collections==0 ? null : new CollectionPersister[collections]; collectionSuffixes = BasicLoader.generateSuffixes( joins + 1, collections ); persisters = new Loadable[joins]; aliases = new String[joins]; owners = new int[joins]; ownerAssociationTypes = new EntityType[joins]; lockModeArray = ArrayHelper.fillArray(lockMode, joins); int i=0; int j=0; Iterator iter = associations.iterator(); while ( iter.hasNext() ) { final OuterJoinableAssociation oj = (OuterJoinableAssociation) iter.next(); if ( !oj.isCollection() ) { persisters[i] = (Loadable) oj.getJoinable(); aliases[i] = oj.getRHSAlias(); owners[i] = oj.getOwner(associations); ownerAssociationTypes[i] = (EntityType) oj.getJoinableType(); i++; } else { QueryableCollection collPersister = (QueryableCollection) oj.getJoinable(); if ( oj.getJoinType()==JoinFragment.LEFT_OUTER_JOIN ) { //it must be a collection fetch collectionPersisters[j] = collPersister; collectionOwners[j] = oj.getOwner(associations); j++; } if ( collPersister.isOneToMany() ) { persisters[i] = (Loadable) collPersister.getElementPersister(); aliases[i] = oj.getRHSAlias(); i++; } } } if ( ArrayHelper.isAllNegative(owners) ) owners = null; if ( collectionOwners!=null && ArrayHelper.isAllNegative(collectionOwners) ) { collectionOwners = null; } } /** * Generate a select list of columns containing all properties of the entity classes */ protected final String selectString(List associations) throws MappingException { if ( associations.size()==0 ) { return ""; } else { StringBuffer buf = new StringBuffer( associations.size() * 100 ) .append(", "); int entityAliasCount=0; int collectionAliasCount=0; for ( int i=0; i<associations.size(); i++ ) { OuterJoinableAssociation join = (OuterJoinableAssociation) associations.get(i); OuterJoinableAssociation next = (i == associations.size() - 1) ? null : ( OuterJoinableAssociation ) associations.get( i + 1 ); final Joinable joinable = join.getJoinable(); final String entitySuffix = ( suffixes == null || entityAliasCount >= suffixes.length ) ? null : suffixes[entityAliasCount]; final String collectionSuffix = ( collectionSuffixes == null || collectionAliasCount >= collectionSuffixes.length ) ? null : collectionSuffixes[collectionAliasCount]; final String selectFragment = joinable.selectFragment( next == null ? null : next.getJoinable(), next == null ? null : next.getRHSAlias(), join.getRHSAlias(), entitySuffix, collectionSuffix, join.getJoinType()==JoinFragment.LEFT_OUTER_JOIN ); buf.append(selectFragment); if ( joinable.consumesEntityAlias() ) entityAliasCount++; if ( joinable.consumesCollectionAlias() && join.getJoinType()==JoinFragment.LEFT_OUTER_JOIN ) collectionAliasCount++; if ( i<associations.size()-1 && selectFragment.trim().length()>0 ) { buf.append(", "); } } return buf.toString(); } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -