KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > loader > JoinWalker


1 //$Id: JoinWalker.java,v 1.3 2005/06/17 04:48:08 oneovthafew Exp $
2
package org.hibernate.loader;
3
4 import java.util.ArrayList JavaDoc;
5 import java.util.Arrays JavaDoc;
6 import java.util.HashSet JavaDoc;
7 import java.util.Iterator JavaDoc;
8 import java.util.List JavaDoc;
9 import java.util.Map JavaDoc;
10 import java.util.Set JavaDoc;
11
12 import org.hibernate.FetchMode;
13 import org.hibernate.LockMode;
14 import org.hibernate.MappingException;
15 import org.hibernate.dialect.Dialect;
16 import org.hibernate.engine.JoinHelper;
17 import org.hibernate.engine.SessionFactoryImplementor;
18 import org.hibernate.persister.collection.CollectionPersister;
19 import org.hibernate.persister.collection.QueryableCollection;
20 import org.hibernate.persister.entity.EntityPersister;
21 import org.hibernate.persister.entity.Joinable;
22 import org.hibernate.persister.entity.Loadable;
23 import org.hibernate.persister.entity.OuterJoinLoadable;
24 import org.hibernate.sql.ConditionFragment;
25 import org.hibernate.sql.DisjunctionFragment;
26 import org.hibernate.sql.InFragment;
27 import org.hibernate.sql.JoinFragment;
28 import org.hibernate.type.AbstractComponentType;
29 import org.hibernate.type.AssociationType;
30 import org.hibernate.type.EntityType;
31 import org.hibernate.type.ForeignKeyDirection;
32 import org.hibernate.type.Type;
33 import org.hibernate.util.ArrayHelper;
34 import org.hibernate.util.StringHelper;
35
36 /**
37  * Walks the metamodel, searching for joins, and collecting
38  * together information needed by <tt>OuterJoinLoader</tt>.
39  *
40  * @see OuterJoinLoader
41  * @author Gavin King, Jon Lipsky
42  */

43 public class JoinWalker {
44     
45     private final SessionFactoryImplementor factory;
46     protected final List JavaDoc associations = new ArrayList JavaDoc();
47     private final Set JavaDoc visitedAssociationKeys = new HashSet JavaDoc();
48     private final Map JavaDoc enabledFilters;
49
50     protected String JavaDoc[] suffixes;
51     protected String JavaDoc[] collectionSuffixes;
52     protected Loadable[] persisters;
53     protected int[] owners;
54     protected EntityType[] ownerAssociationTypes;
55     protected CollectionPersister[] collectionPersisters;
56     protected int[] collectionOwners;
57     protected String JavaDoc[] aliases;
58     protected LockMode[] lockModeArray;
59     protected String JavaDoc sql;
60     
61     public String JavaDoc[] getCollectionSuffixes() {
62         return collectionSuffixes;
63     }
64
65     public void setCollectionSuffixes(String JavaDoc[] collectionSuffixes) {
66         this.collectionSuffixes = collectionSuffixes;
67     }
68
69     public LockMode[] getLockModeArray() {
70         return lockModeArray;
71     }
72
73     public void setLockModeArray(LockMode[] lockModeArray) {
74         this.lockModeArray = lockModeArray;
75     }
76
77     public String JavaDoc[] getSuffixes() {
78         return suffixes;
79     }
80
81     public void setSuffixes(String JavaDoc[] suffixes) {
82         this.suffixes = suffixes;
83     }
84
85     public String JavaDoc[] getAliases() {
86         return aliases;
87     }
88
89     public void setAliases(String JavaDoc[] aliases) {
90         this.aliases = aliases;
91     }
92
93     public int[] getCollectionOwners() {
94         return collectionOwners;
95     }
96
97     public void setCollectionOwners(int[] collectionOwners) {
98         this.collectionOwners = collectionOwners;
99     }
100
101     public CollectionPersister[] getCollectionPersisters() {
102         return collectionPersisters;
103     }
104
105     public void setCollectionPersisters(CollectionPersister[] collectionPersisters) {
106         this.collectionPersisters = collectionPersisters;
107     }
108
109     public EntityType[] getOwnerAssociationTypes() {
110         return ownerAssociationTypes;
111     }
112
113     public void setOwnerAssociationTypes(EntityType[] ownerAssociationType) {
114         this.ownerAssociationTypes = ownerAssociationType;
115     }
116
117     public int[] getOwners() {
118         return owners;
119     }
120
121     public void setOwners(int[] owners) {
122         this.owners = owners;
123     }
124
125     public Loadable[] getPersisters() {
126         return persisters;
127     }
128
129     public void setPersisters(Loadable[] persisters) {
130         this.persisters = persisters;
131     }
132
133     public String JavaDoc getSQLString() {
134         return sql;
135     }
136
137     public void setSql(String JavaDoc sql) {
138         this.sql = sql;
139     }
140
141     protected SessionFactoryImplementor getFactory() {
142         return factory;
143     }
144
145     protected Dialect getDialect() {
146         return factory.getDialect();
147     }
148     
149     protected Map JavaDoc getEnabledFilters() {
150         return enabledFilters;
151     }
152
153     protected JoinWalker(SessionFactoryImplementor factory, Map JavaDoc enabledFilters) {
154         this.factory = factory;
155         this.enabledFilters = enabledFilters;
156     }
157
158     /**
159      * Add on association (one-to-one, many-to-one, or a collection) to a list
160      * of associations to be fetched by outerjoin (if necessary)
161      */

162     private void addAssociationToJoinTreeIfNecessary(
163         final AssociationType type,
164         final String JavaDoc[] aliasedLhsColumns,
165         final String JavaDoc alias,
166         final String JavaDoc path,
167         int currentDepth,
168         final int joinType)
169     throws MappingException {
170         
171         if (joinType>=0) {
172             addAssociationToJoinTree(
173                     type,
174                     aliasedLhsColumns,
175                     alias,
176                     path,
177                     currentDepth,
178                     joinType
179                 );
180         }
181
182     }
183
184     /**
185      * Add on association (one-to-one, many-to-one, or a collection) to a list
186      * of associations to be fetched by outerjoin
187      */

188     private void addAssociationToJoinTree(
189         final AssociationType type,
190         final String JavaDoc[] aliasedLhsColumns,
191         final String JavaDoc alias,
192         final String JavaDoc path,
193         final int currentDepth,
194         final int joinType)
195     throws MappingException {
196
197         Joinable joinable = type.getAssociatedJoinable( getFactory() );
198
199         String JavaDoc subalias = generateTableAlias(
200                 associations.size()+1, //before adding to collection!
201
path,
202                 joinable
203             );
204
205         OuterJoinableAssociation assoc = new OuterJoinableAssociation(
206                 type,
207                 alias,
208                 aliasedLhsColumns,
209                 subalias,
210                 joinType,
211                 getFactory(),
212                 enabledFilters
213             );
214         assoc.validateJoin(path);
215         associations.add(assoc);
216
217         int nextDepth = currentDepth+1;
218         if ( !joinable.isCollection() ) {
219             if (joinable instanceof OuterJoinLoadable) {
220                 walkEntityTree(
221                     (OuterJoinLoadable) joinable,
222                     subalias,
223                     path,
224                     nextDepth
225                 );
226             }
227         }
228         else {
229             if (joinable instanceof QueryableCollection) {
230                 walkCollectionTree(
231                     (QueryableCollection) joinable,
232                     subalias,
233                     path,
234                     nextDepth
235                 );
236             }
237         }
238
239     }
240
241     /**
242      * For an entity class, return a list of associations to be fetched by outerjoin
243      */

244     protected final void walkEntityTree(OuterJoinLoadable persister, String JavaDoc alias)
245     throws MappingException {
246         walkEntityTree(persister, alias, "", 0);
247     }
248
249     /**
250      * For a collection role, return a list of associations to be fetched by outerjoin
251      */

252     protected final void walkCollectionTree(QueryableCollection persister, String JavaDoc alias)
253     throws MappingException {
254         walkCollectionTree(persister, alias, "", 0);
255         //TODO: when this is the entry point, we should use an INNER_JOIN for fetching the many-to-many elements!
256
}
257
258     /**
259      * For a collection role, return a list of associations to be fetched by outerjoin
260      */

261     private void walkCollectionTree(
262         final QueryableCollection persister,
263         final String JavaDoc alias,
264         final String JavaDoc path,
265         final int currentDepth)
266     throws MappingException {
267
268         if ( persister.isOneToMany() ) {
269             walkEntityTree(
270                     (OuterJoinLoadable) persister.getElementPersister(),
271                     alias,
272                     path,
273                     currentDepth
274                 );
275         }
276         else {
277             Type type = persister.getElementType();
278             if ( type.isAssociationType() ) {
279                 // a many-to-many;
280
// decrement currentDepth here to allow join across the association table
281
// without exceeding MAX_FETCH_DEPTH (i.e. the "currentDepth - 1" bit)
282
AssociationType associationType = (AssociationType) type;
283                 String JavaDoc[] aliasedLhsColumns = persister.getElementColumnNames(alias);
284                 String JavaDoc[] lhsColumns = persister.getElementColumnNames();
285                 // if the current depth is 0, the root thing being loaded is the
286
// many-to-many collection itself. Here, it is alright to use
287
// an inner join...
288
boolean useInnerJoin = currentDepth == 0;
289                 final int joinType = getJoinType(
290                         associationType,
291                         persister.getFetchMode(),
292                         path,
293                         persister.getTableName(),
294                         lhsColumns,
295                         !useInnerJoin,
296                         currentDepth - 1
297                     );
298                 addAssociationToJoinTreeIfNecessary(
299                         associationType,
300                         aliasedLhsColumns,
301                         alias,
302                         path,
303                         currentDepth - 1,
304                         joinType
305                     );
306             }
307             else if ( type.isComponentType() ) {
308                 walkCompositeElementTree(
309                         (AbstractComponentType) type,
310                         persister.getElementColumnNames(),
311                         persister,
312                         alias,
313                         path,
314                         currentDepth
315                     );
316             }
317         }
318
319     }
320     
321     /**
322      * Walk the tree for a particular entity association
323      */

324     private final void walkEntityAssociationTree(
325         final AssociationType associationType,
326         final OuterJoinLoadable persister,
327         final int propertyNumber,
328         final String JavaDoc alias,
329         final String JavaDoc path,
330         final boolean nullable,
331         final int currentDepth)
332     throws MappingException {
333
334         String JavaDoc[] aliasedLhsColumns = JoinHelper.getAliasedLHSColumnNames(
335                 associationType, alias, propertyNumber, persister, getFactory()
336             );
337
338         String JavaDoc[] lhsColumns = JoinHelper.getLHSColumnNames(
339                 associationType, propertyNumber, persister, getFactory()
340             );
341         String JavaDoc lhsTable = JoinHelper.getLHSTableName(associationType, propertyNumber, persister);
342
343         String JavaDoc subpath = subPath( path, persister.getSubclassPropertyName(propertyNumber) );
344         int joinType = getJoinType(
345                 associationType,
346                 persister.getFetchMode(propertyNumber),
347                 subpath,
348                 lhsTable,
349                 lhsColumns,
350                 nullable,
351                 currentDepth
352             );
353         addAssociationToJoinTreeIfNecessary(
354                 associationType,
355                 aliasedLhsColumns,
356                 alias,
357                 subpath,
358                 currentDepth,
359                 joinType
360             );
361
362     }
363
364     /**
365      * For an entity class, add to a list of associations to be fetched
366      * by outerjoin
367      */

368     private final void walkEntityTree(
369         final OuterJoinLoadable persister,
370         final String JavaDoc alias,
371         final String JavaDoc path,
372         final int currentDepth)
373     throws MappingException {
374
375         int n = persister.countSubclassProperties();
376         for ( int i=0; i<n; i++ ) {
377             Type type = persister.getSubclassPropertyType(i);
378             if ( type.isAssociationType() ) {
379                 walkEntityAssociationTree(
380                     (AssociationType) type,
381                     persister,
382                     i,
383                     alias,
384                     path,
385                     persister.isSubclassPropertyNullable(i),
386                     currentDepth
387                 );
388             }
389             else if ( type.isComponentType() ) {
390                 walkComponentTree(
391                     (AbstractComponentType) type,
392                     i,
393                     0,
394                     persister,
395                     alias,
396                     subPath( path, persister.getSubclassPropertyName(i) ),
397                     currentDepth
398                 );
399             }
400         }
401     }
402
403     /**
404      * For a component, add to a list of associations to be fetched by outerjoin
405      */

406     private void walkComponentTree(
407         final AbstractComponentType componentType,
408         final int propertyNumber,
409         int begin,
410         final OuterJoinLoadable persister,
411         final String JavaDoc alias,
412         final String JavaDoc path,
413         final int currentDepth
414     ) throws MappingException {
415
416         Type[] types = componentType.getSubtypes();
417         String JavaDoc[] propertyNames = componentType.getPropertyNames();
418         for ( int i=0; i <types.length; i++ ) {
419
420             if ( types[i].isAssociationType() ) {
421                 AssociationType associationType = (AssociationType) types[i];
422
423                 String JavaDoc[] aliasedLhsColumns = JoinHelper.getAliasedLHSColumnNames(
424                     associationType, alias, propertyNumber, begin, persister, getFactory()
425                 );
426
427                 String JavaDoc[] lhsColumns = JoinHelper.getLHSColumnNames(
428                     associationType, propertyNumber, begin, persister, getFactory()
429                 );
430                 String JavaDoc lhsTable = JoinHelper.getLHSTableName(associationType, propertyNumber, persister);
431
432                 String JavaDoc subpath = subPath( path, propertyNames[i] );
433                 final boolean[] propertyNullability = componentType.getPropertyNullability();
434                 final int joinType = getJoinType(
435                         associationType,
436                         componentType.getFetchMode(i),
437                         subpath,
438                         lhsTable,
439                         lhsColumns,
440                         propertyNullability==null || propertyNullability[i],
441                         currentDepth
442                     );
443                 addAssociationToJoinTreeIfNecessary(
444                         associationType,
445                         aliasedLhsColumns,
446                         alias,
447                         subpath,
448                         currentDepth,
449                         joinType
450                     );
451
452             }
453             else if ( types[i].isComponentType() ) {
454                 String JavaDoc subpath = subPath( path, propertyNames[i] );
455                 walkComponentTree(
456                         (AbstractComponentType) types[i],
457                         propertyNumber,
458                         begin,
459                         persister,
460                         alias,
461                         subpath,
462                         currentDepth
463                     );
464             }
465             
466             begin+=types[i].getColumnSpan( getFactory() );
467         }
468
469     }
470
471     /**
472      * For a composite element, add to a list of associations to be fetched by outerjoin
473      */

474     private void walkCompositeElementTree(
475         final AbstractComponentType compositeType,
476         final String JavaDoc[] cols,
477         final QueryableCollection persister,
478         final String JavaDoc alias,
479         final String JavaDoc path,
480         final int currentDepth)
481     throws MappingException {
482
483         Type[] types = compositeType.getSubtypes();
484         String JavaDoc[] propertyNames = compositeType.getPropertyNames();
485         int begin = 0;
486         for ( int i=0; i <types.length; i++ ) {
487             int length = types[i].getColumnSpan( getFactory() );
488             String JavaDoc[] lhsColumns = ArrayHelper.slice(cols, begin, length);
489
490             if ( types[i].isAssociationType() ) {
491                 AssociationType associationType = (AssociationType) types[i];
492
493                 // simple, because we can't have a one-to-one or a collection
494
// (or even a property-ref) in a composite-element:
495
String JavaDoc[] aliasedLhsColumns = StringHelper.qualify(alias, lhsColumns);
496
497                 String JavaDoc subpath = subPath( path, propertyNames[i] );
498                 final boolean[] propertyNullability = compositeType.getPropertyNullability();
499                 final int joinType = getJoinType(
500                         associationType,
501                         compositeType.getFetchMode(i),
502                         subpath,
503                         persister.getTableName(),
504                         lhsColumns,
505                         propertyNullability==null || propertyNullability[i],
506                         currentDepth
507                     );
508                 addAssociationToJoinTreeIfNecessary(
509                         associationType,
510                         aliasedLhsColumns,
511                         alias,
512                         subpath,
513                         currentDepth,
514                         joinType
515                     );
516             }
517             else if ( types[i].isComponentType() ) {
518                 String JavaDoc subpath = subPath( path, propertyNames[i] );
519                 walkCompositeElementTree(
520                         (AbstractComponentType) types[i],
521                         lhsColumns,
522                         persister,
523                         alias,
524                         subpath,
525                         currentDepth
526                     );
527             }
528             begin+=length;
529         }
530
531     }
532
533     /**
534      * Extend the path by the given property name
535      */

536     private static String JavaDoc subPath(String JavaDoc path, String JavaDoc property) {
537         if ( path==null || path.length()==0) {
538             return property;
539         }
540         else {
541             return StringHelper.qualify(path, property);
542         }
543     }
544
545     /**
546      * Get the join type (inner, outer, etc) or -1 if the
547      * association should not be joined. Override on
548      * subclasses.
549      */

550     protected int getJoinType(
551             AssociationType type,
552             FetchMode config,
553             String JavaDoc path,
554             String JavaDoc lhsTable,
555             String JavaDoc[] lhsColumns,
556             boolean nullable,
557             int currentDepth)
558     throws MappingException {
559         
560         if ( !isJoinedFetchEnabled(type, config) ) return -1;
561         
562         if ( isTooDeep(currentDepth) ) return -1;
563         
564         final boolean dupe = isDuplicateAssociation(lhsTable, lhsColumns, type);
565         if (dupe) return -1;
566         
567         return getJoinType(nullable, currentDepth);
568         
569     }
570     
571     /**
572      * Use an inner join if it is a non-null association and this
573      * is the "first" join in a series
574      */

575     protected int getJoinType(boolean nullable, int currentDepth) {
576         //TODO: this is too conservative; if all preceding joins were
577
// also inner joins, we could use an inner join here
578
return !nullable && currentDepth==0 ?
579                     JoinFragment.INNER_JOIN :
580                     JoinFragment.LEFT_OUTER_JOIN;
581     }
582
583     protected boolean isTooDeep(int currentDepth) {
584         Integer JavaDoc maxFetchDepth = getFactory().getSettings().getMaximumFetchDepth();
585         return maxFetchDepth!=null && currentDepth >= maxFetchDepth.intValue();
586     }
587     
588     /**
589      * Does the mapping, and Hibernate default semantics, specify that
590      * this association should be fetched by outer joining
591      */

592     protected boolean isJoinedFetchEnabledInMapping(FetchMode config, AssociationType type)
593     throws MappingException {
594         if ( !type.isEntityType() && !type.isCollectionType() ) {
595             return false;
596         }
597         else {
598             if (config==FetchMode.JOIN) return true;
599             if (config==FetchMode.SELECT) return false;
600             if ( type.isEntityType() ) {
601                 //TODO: look at the owning property and check that it
602
// isn't lazy (by instrumentation)
603
EntityType entityType =(EntityType) type;
604                 EntityPersister persister = getFactory().getEntityPersister( entityType.getAssociatedEntityName() );
605                 return !persister.hasProxy();
606             }
607             else {
608                 return false;
609             }
610         }
611     }
612
613     /**
614      * Override on subclasses to enable or suppress joining
615      * of certain association types
616      */

617     protected boolean isJoinedFetchEnabled(AssociationType type, FetchMode config) {
618         return type.isEntityType() && isJoinedFetchEnabledInMapping(config, type) ;
619     }
620     
621     protected String JavaDoc generateTableAlias(
622             final int n,
623             final String JavaDoc path,
624             final Joinable joinable
625     ) {
626         return StringHelper.generateAlias( joinable.getName(), n );
627     }
628
629     protected String JavaDoc generateRootAlias(final String JavaDoc description) {
630         return StringHelper.generateAlias(description, 0);
631     }
632
633     /**
634      * Used to detect circularities in the joined graph, note that
635      * this method is side-effecty
636      */

637     protected boolean isDuplicateAssociation(
638         final String JavaDoc foreignKeyTable,
639         final String JavaDoc[] foreignKeyColumns
640     ) {
641         AssociationKey associationKey = new AssociationKey(foreignKeyColumns, foreignKeyTable);
642         return !visitedAssociationKeys.add( associationKey );
643     }
644     
645     /**
646      * Used to detect circularities in the joined graph, note that
647      * this method is side-effecty
648      */

649     protected boolean isDuplicateAssociation(
650         final String JavaDoc lhsTable,
651         final String JavaDoc[] lhsColumnNames,
652         final AssociationType type
653     ) {
654         final String JavaDoc foreignKeyTable;
655         final String JavaDoc[] foreignKeyColumns;
656         if ( type.getForeignKeyDirection()==ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT ) {
657             foreignKeyTable = lhsTable;
658             foreignKeyColumns = lhsColumnNames;
659         }
660         else {
661             foreignKeyTable = type.getAssociatedJoinable( getFactory() ).getTableName();
662             foreignKeyColumns = JoinHelper.getRHSColumnNames( type, getFactory() );
663         }
664         return isDuplicateAssociation(foreignKeyTable, foreignKeyColumns);
665     }
666     
667     /**
668      * Uniquely identifier a foreign key, so that we don't
669      * join it more than once, and create circularities
670      */

671     private static final class AssociationKey {
672         private String JavaDoc[] columns;
673         private String JavaDoc table;
674         private AssociationKey(String JavaDoc[] columns, String JavaDoc table) {
675             this.columns = columns;
676             this.table = table;
677         }
678         public boolean equals(Object JavaDoc other) {
679             AssociationKey that = (AssociationKey) other;
680             return that.table.equals(table) && Arrays.equals(columns, that.columns);
681         }
682         public int hashCode() {
683             return table.hashCode(); //TODO: inefficient
684
}
685     }
686     
687     /**
688      * Should we join this association?
689      */

690     protected boolean isJoinable(
691         final int joinType,
692         final Set JavaDoc visitedAssociationKeys,
693         final String JavaDoc lhsTable,
694         final String JavaDoc[] lhsColumnNames,
695         final AssociationType type,
696         final int depth
697     ) {
698         if (joinType<0) return false;
699         
700         if (joinType==JoinFragment.INNER_JOIN) return true;
701         
702         Integer JavaDoc maxFetchDepth = getFactory().getSettings().getMaximumFetchDepth();
703         final boolean tooDeep = maxFetchDepth!=null &&
704             depth >= maxFetchDepth.intValue();
705         
706         return !tooDeep && !isDuplicateAssociation(lhsTable, lhsColumnNames, type);
707     }
708     
709     protected String JavaDoc orderBy(final List JavaDoc associations, final String JavaDoc orderBy) {
710         String JavaDoc fullOrderBy = orderBy(associations);
711         if ( fullOrderBy.length()== 0 ) {
712             fullOrderBy = orderBy;
713         }
714         else if ( orderBy.length()!=0 ) {
715             fullOrderBy = fullOrderBy + ", " + orderBy;
716         }
717         return fullOrderBy;
718     }
719     
720     /**
721      * Generate a sequence of <tt>LEFT OUTER JOIN</tt> clauses for the given associations.
722      */

723     protected final JoinFragment mergeOuterJoins(List JavaDoc associations)
724     throws MappingException {
725         JoinFragment outerjoin = getDialect().createOuterJoinFragment();
726         Iterator JavaDoc iter = associations.iterator();
727         OuterJoinableAssociation last = null;
728         while ( iter.hasNext() ) {
729             OuterJoinableAssociation oj = (OuterJoinableAssociation) iter.next();
730             if ( last != null && last.isManyToManyWith( oj ) ) {
731                 oj.addManyToManyJoin( outerjoin, ( QueryableCollection ) last.getJoinable() );
732             }
733             else {
734                 oj.addJoins(outerjoin);
735             }
736             last = oj;
737         }
738         last = null;
739         return outerjoin;
740     }
741
742     /**
743      * Count the number of instances of Joinable which are actually
744      * also instances of Loadable, or are one-to-many associations
745      */

746     protected static final int countEntityPersisters(List JavaDoc associations)
747     throws MappingException {
748         int result = 0;
749         Iterator JavaDoc iter = associations.iterator();
750         while ( iter.hasNext() ) {
751             OuterJoinableAssociation oj = (OuterJoinableAssociation) iter.next();
752             if ( oj.getJoinable().consumesEntityAlias() ) {
753                 result++;
754             }
755         }
756         return result;
757     }
758     
759     /**
760      * Count the number of instances of Joinable which are actually
761      * also instances of PersistentCollection which are being fetched
762      * by outer join
763      */

764     protected static final int countCollectionPersisters(List JavaDoc associations)
765     throws MappingException {
766         int result = 0;
767         Iterator JavaDoc iter = associations.iterator();
768         while ( iter.hasNext() ) {
769             OuterJoinableAssociation oj = (OuterJoinableAssociation) iter.next();
770             if ( oj.getJoinType()==JoinFragment.LEFT_OUTER_JOIN && oj.getJoinable().isCollection() ) {
771                 result++;
772             }
773         }
774         return result;
775     }
776     
777     /**
778      * Get the order by string required for collection fetching
779      */

780     protected static final String JavaDoc orderBy(List JavaDoc associations)
781     throws MappingException {
782         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
783         Iterator JavaDoc iter = associations.iterator();
784         while ( iter.hasNext() ) {
785             OuterJoinableAssociation oj = (OuterJoinableAssociation) iter.next();
786             if ( oj.getJoinType()==JoinFragment.LEFT_OUTER_JOIN && oj.getJoinable().isCollection() ) {
787                 final QueryableCollection queryableCollection = (QueryableCollection) oj.getJoinable();
788                 if ( queryableCollection.hasOrdering() ) {
789                     final String JavaDoc orderByString = queryableCollection.getSQLOrderByString( oj.getRHSAlias() );
790                     buf.append( orderByString ).append(", ");
791                 }
792             }
793         }
794         if ( buf.length()>0 ) buf.setLength( buf.length()-2 );
795         return buf.toString();
796     }
797     
798     /**
799      * Render the where condition for a (batch) load by identifier / collection key
800      */

801     protected StringBuffer JavaDoc whereString(String JavaDoc alias, String JavaDoc[] columnNames, int batchSize) {
802         if ( columnNames.length==1 ) {
803             // if not a composite key, use "foo in (?, ?, ?)" for batching
804
// if no batch, and not a composite key, use "foo = ?"
805
InFragment in = new InFragment().setColumn( alias, columnNames[0] );
806             for ( int i=0; i<batchSize; i++ ) in.addValue("?");
807             return new StringBuffer JavaDoc( in.toFragmentString() );
808         }
809         else {
810             //a composite key
811
ConditionFragment byId = new ConditionFragment()
812                 .setTableAlias(alias)
813                 .setCondition( columnNames, "?" );
814     
815             StringBuffer JavaDoc whereString = new StringBuffer JavaDoc();
816             if ( batchSize==1 ) {
817                 // if no batch, use "foo = ? and bar = ?"
818
whereString.append( byId.toFragmentString() );
819             }
820             else {
821                 // if a composite key, use "( (foo = ? and bar = ?) or (foo = ? and bar = ?) )" for batching
822
whereString.append('('); //TODO: unnecessary for databases with ANSI-style joins
823
DisjunctionFragment df = new DisjunctionFragment();
824                 for ( int i=0; i<batchSize; i++ ) {
825                     df.addCondition(byId);
826                 }
827                 whereString.append( df.toFragmentString() );
828                 whereString.append(')'); //TODO: unnecessary for databases with ANSI-style joins
829
}
830             return whereString;
831         }
832     }
833
834     protected void initPersisters(final List JavaDoc associations, final LockMode lockMode) throws MappingException {
835         
836         final int joins = countEntityPersisters(associations);
837         final int collections = countCollectionPersisters(associations);
838
839         collectionOwners = collections==0 ? null : new int[collections];
840         collectionPersisters = collections==0 ? null : new CollectionPersister[collections];
841         collectionSuffixes = BasicLoader.generateSuffixes( joins + 1, collections );
842
843         persisters = new Loadable[joins];
844         aliases = new String JavaDoc[joins];
845         owners = new int[joins];
846         ownerAssociationTypes = new EntityType[joins];
847         lockModeArray = ArrayHelper.fillArray(lockMode, joins);
848         
849         int i=0;
850         int j=0;
851         Iterator JavaDoc iter = associations.iterator();
852         while ( iter.hasNext() ) {
853             final OuterJoinableAssociation oj = (OuterJoinableAssociation) iter.next();
854             if ( !oj.isCollection() ) {
855                 
856                 persisters[i] = (Loadable) oj.getJoinable();
857                 aliases[i] = oj.getRHSAlias();
858                 owners[i] = oj.getOwner(associations);
859                 ownerAssociationTypes[i] = (EntityType) oj.getJoinableType();
860                 i++;
861                 
862             }
863             else {
864                 
865                 QueryableCollection collPersister = (QueryableCollection) oj.getJoinable();
866                 if ( oj.getJoinType()==JoinFragment.LEFT_OUTER_JOIN ) {
867                     //it must be a collection fetch
868
collectionPersisters[j] = collPersister;
869                     collectionOwners[j] = oj.getOwner(associations);
870                     j++;
871                 }
872     
873                 if ( collPersister.isOneToMany() ) {
874                     persisters[i] = (Loadable) collPersister.getElementPersister();
875                     aliases[i] = oj.getRHSAlias();
876                     i++;
877                 }
878             }
879         }
880     
881         if ( ArrayHelper.isAllNegative(owners) ) owners = null;
882         if ( collectionOwners!=null && ArrayHelper.isAllNegative(collectionOwners) ) {
883             collectionOwners = null;
884         }
885     }
886
887     /**
888      * Generate a select list of columns containing all properties of the entity classes
889      */

890     protected final String JavaDoc selectString(List JavaDoc associations)
891     throws MappingException {
892
893         if ( associations.size()==0 ) {
894             return "";
895         }
896         else {
897             StringBuffer JavaDoc buf = new StringBuffer JavaDoc( associations.size() * 100 )
898                 .append(", ");
899             int entityAliasCount=0;
900             int collectionAliasCount=0;
901             for ( int i=0; i<associations.size(); i++ ) {
902                 OuterJoinableAssociation join = (OuterJoinableAssociation) associations.get(i);
903                 OuterJoinableAssociation next = (i == associations.size() - 1)
904                         ? null
905                         : ( OuterJoinableAssociation ) associations.get( i + 1 );
906                 final Joinable joinable = join.getJoinable();
907                 final String JavaDoc entitySuffix = ( suffixes == null || entityAliasCount >= suffixes.length )
908                         ? null
909                         : suffixes[entityAliasCount];
910                 final String JavaDoc collectionSuffix = ( collectionSuffixes == null || collectionAliasCount >= collectionSuffixes.length )
911                         ? null
912                         : collectionSuffixes[collectionAliasCount];
913                 final String JavaDoc selectFragment = joinable.selectFragment(
914                         next == null ? null : next.getJoinable(),
915                         next == null ? null : next.getRHSAlias(),
916                         join.getRHSAlias(),
917                         entitySuffix,
918                         collectionSuffix,
919                         join.getJoinType()==JoinFragment.LEFT_OUTER_JOIN
920                 );
921                 buf.append(selectFragment);
922                 if ( joinable.consumesEntityAlias() ) entityAliasCount++;
923                 if ( joinable.consumesCollectionAlias() && join.getJoinType()==JoinFragment.LEFT_OUTER_JOIN ) collectionAliasCount++;
924                 if (
925                     i<associations.size()-1 &&
926                     selectFragment.trim().length()>0
927                 ) {
928                     buf.append(", ");
929                 }
930             }
931             return buf.toString();
932         }
933     }
934
935 }
936
Popular Tags