KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > hql > ast > tree > FromElementFactory


1 // $Id: FromElementFactory.java,v 1.4 2005/07/20 19:35:21 steveebersole Exp $
2
package org.hibernate.hql.ast.tree;
3
4 import org.hibernate.engine.JoinSequence;
5 import org.hibernate.hql.antlr.SqlTokenTypes;
6 import org.hibernate.hql.ast.util.ASTUtil;
7 import org.hibernate.hql.ast.util.AliasGenerator;
8 import org.hibernate.hql.ast.util.PathHelper;
9 import org.hibernate.hql.ast.util.SessionFactoryHelper;
10 import org.hibernate.persister.collection.QueryableCollection;
11 import org.hibernate.persister.entity.EntityPersister;
12 import org.hibernate.persister.entity.Joinable;
13 import org.hibernate.persister.entity.Queryable;
14 import org.hibernate.sql.JoinFragment;
15 import org.hibernate.type.AssociationType;
16 import org.hibernate.type.CollectionType;
17 import org.hibernate.type.EntityType;
18 import org.hibernate.type.Type;
19
20 import antlr.ASTFactory;
21 import antlr.SemanticException;
22 import antlr.collections.AST;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26
27 /**
28  * Encapsulates the creation of FromElements and JoinSequences.
29  *
30  * @author josh Oct 12, 2004 4:54:25 AM
31  */

32 class FromElementFactory implements SqlTokenTypes {
33
34     private static final Log log = LogFactory.getLog( FromElementFactory.class );
35
36     private FromClause fromClause;
37     private FromElement origin;
38     private String JavaDoc path;
39
40     private String JavaDoc classAlias;
41     private String JavaDoc[] columns;
42     private boolean implied;
43     private boolean inElementsFunction;
44     private boolean collection;
45     private QueryableCollection queryableCollection;
46     private CollectionType collectionType;
47
48     /**
49      * Creates entity from elements.
50      */

51     public FromElementFactory(FromClause fromClause, FromElement origin, String JavaDoc path) {
52         this.fromClause = fromClause;
53         this.origin = origin;
54         this.path = path;
55         collection = false;
56     }
57
58     /**
59      * Creates collection from elements.
60      */

61     public FromElementFactory(
62             FromClause fromClause,
63             FromElement origin,
64             String JavaDoc path,
65             String JavaDoc classAlias,
66             String JavaDoc[] columns,
67             boolean implied) {
68         this( fromClause, origin, path );
69         this.classAlias = classAlias;
70         this.columns = columns;
71         this.implied = implied;
72         collection = true;
73     }
74
75     FromElement addFromElement() throws SemanticException {
76         FromClause parentFromClause = fromClause.getParentFromClause();
77         if ( parentFromClause != null ) {
78             // Look up class name using the first identifier in the path.
79
String JavaDoc pathAlias = PathHelper.getAlias( path );
80             FromElement parentFromElement = parentFromClause.getFromElement( pathAlias );
81             if ( parentFromElement != null ) {
82                 return createFromElementInSubselect( path, pathAlias, parentFromElement, classAlias );
83             }
84         }
85
86         EntityPersister entityPersister = fromClause.getSessionFactoryHelper().requireClassPersister( path );
87
88         FromElement elem = createAndAddFromElement( path,
89                 classAlias,
90                 entityPersister,
91                 ( EntityType ) ( ( Queryable ) entityPersister ).getType(),
92                 null );
93
94         // Add to the query spaces.
95
fromClause.getWalker().addQuerySpaces( entityPersister.getQuerySpaces() );
96
97         return elem;
98     }
99
100     private FromElement createFromElementInSubselect(
101             String JavaDoc path,
102             String JavaDoc pathAlias,
103             FromElement parentFromElement,
104             String JavaDoc classAlias) throws SemanticException {
105         if ( log.isDebugEnabled() ) {
106             log.debug( "createFromElementInSubselect() : path = " + path );
107         }
108         // Create an DotNode AST for the path and resolve it.
109
FromElement fromElement = evaluateFromElementPath( path, classAlias );
110         EntityPersister entityPersister = fromElement.getEntityPersister();
111
112         // If the first identifier in the path referrs to the class alias (not the class name), then this
113
// is a correlated subselect. If it's a correlated sub-select, use the existing table alias. Otherwise
114
// generate a new one.
115
String JavaDoc tableAlias = null;
116         boolean correlatedSubselect = pathAlias.equals( parentFromElement.getClassAlias() );
117         if ( correlatedSubselect ) {
118             tableAlias = fromElement.getTableAlias();
119         }
120         else {
121             tableAlias = null;
122         }
123
124         // If the from element isn't in the same clause, create a new from element.
125
if ( fromElement.getFromClause() != fromClause ) {
126             if ( log.isDebugEnabled() ) {
127                 log.debug( "createFromElementInSubselect() : creating a new FROM element..." );
128             }
129             fromElement = createFromElement( entityPersister );
130             initializeAndAddFromElement( fromElement,
131                     path,
132                     classAlias,
133                     entityPersister,
134                     ( EntityType ) ( ( Queryable ) entityPersister ).getType(),
135                     tableAlias
136             );
137         }
138         if ( log.isDebugEnabled() ) {
139             log.debug( "createFromElementInSubselect() : " + path + " -> " + fromElement );
140         }
141         return fromElement;
142     }
143
144     private FromElement evaluateFromElementPath(String JavaDoc path, String JavaDoc classAlias) throws SemanticException {
145         ASTFactory factory = fromClause.getASTFactory();
146         FromReferenceNode pathNode = ( FromReferenceNode ) PathHelper.parsePath( path, factory );
147         pathNode.recursiveResolve( FromReferenceNode.ROOT_LEVEL, // This is the root level node.
148
false, // Generate an explicit from clause at the root.
149
classAlias,
150                 null
151         );
152         if ( pathNode.getImpliedJoin() != null ) {
153             return pathNode.getImpliedJoin();
154         }
155         else {
156             return pathNode.getFromElement();
157         }
158     }
159
160     FromElement createCollectionElementsJoin(
161             QueryableCollection queryableCollection,
162             String JavaDoc collectionName) throws SemanticException {
163         JoinSequence collectionJoinSequence = fromClause.getSessionFactoryHelper()
164                 .createCollectionJoinSequence( queryableCollection, collectionName );
165         this.queryableCollection = queryableCollection;
166         return createCollectionJoin( collectionJoinSequence, null );
167     }
168
169     FromElement createCollection(
170             QueryableCollection queryableCollection,
171             String JavaDoc role,
172             int joinType,
173             boolean fetchFlag,
174             boolean indexed)
175             throws SemanticException {
176         if ( !collection ) {
177             throw new IllegalStateException JavaDoc( "FromElementFactory not initialized for collections!" );
178         }
179         this.inElementsFunction = indexed;
180         FromElement elem;
181         this.queryableCollection = queryableCollection;
182         collectionType = queryableCollection.getCollectionType();
183         String JavaDoc roleAlias = fromClause.getAliasGenerator().createName( role );
184
185         // Subqueries create 'special' implied from nodes
186
// because correlated subselects can't use an
187
// ANSI-style join
188
boolean explicitSubqueryFromElement = fromClause.isSubQuery() && !implied;
189         if ( explicitSubqueryFromElement ) {
190             implied = true;
191         }
192
193         Type elementType = queryableCollection.getElementType();
194         if ( elementType.isEntityType() ) { // A collection of entities...
195
elem = createEntityAssociation( role, roleAlias, joinType );
196         }
197         else if ( elementType.isComponentType() ) { // A collection of components...
198
JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
199             elem = createCollectionJoin( joinSequence, roleAlias );
200         }
201         else { // A collection of scalar elements...
202
JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
203             elem = createCollectionJoin( joinSequence, roleAlias );
204         }
205
206         elem.setRole( role );
207         elem.setQueryableCollection( queryableCollection );
208         // Don't include sub-classes for implied collection joins or subquery joins.
209
if ( implied ) {
210             elem.setIncludeSubclasses( false );
211         }
212
213         if ( explicitSubqueryFromElement ) {
214             elem.setInProjectionList( true ); // Treat explict from elements in sub-queries properly.
215
}
216
217         if ( fetchFlag ) {
218             elem.setFetch( true );
219         }
220         return elem;
221     }
222
223     FromElement createEntityJoin(
224             String JavaDoc entityClass,
225             String JavaDoc tableAlias,
226             JoinSequence joinSequence,
227             boolean fetchFlag,
228             boolean inFrom,
229             EntityType type) throws SemanticException {
230         FromElement elem = createJoin( entityClass, tableAlias, joinSequence, type, false );
231         elem.setFetch( fetchFlag );
232         EntityPersister entityPersister = elem.getEntityPersister();
233         int numberOfTables = entityPersister.getQuerySpaces().length;
234         if ( numberOfTables > 1 && implied && !elem.useFromFragment() ) {
235             if ( log.isDebugEnabled() ) {
236                 log.debug( "createEntityJoin() : Implied multi-table entity join" );
237             }
238             elem.setUseFromFragment( true );
239         }
240
241         // If this is an implied join in a FROM clause, then use ANSI-style joining, and set the
242
// flag on the FromElement that indicates that it was implied in the FROM clause itself.
243
if ( implied && inFrom ) {
244             joinSequence.setUseThetaStyle( false );
245             elem.setUseFromFragment( true );
246             elem.setImpliedInFromClause( true );
247         }
248         if ( elem.getWalker().isSubQuery() ) {
249             // two conditions where we need to transform this to a theta-join syntax:
250
// 1) 'elem' is the "root from-element" in correlated subqueries
251
// 2) The DotNode.useThetaStyleImplicitJoins has been set to true
252
// and 'elem' represents an implicit join
253
if ( elem.getFromClause() != elem.getOrigin().getFromClause() ||
254 // ( implied && DotNode.useThetaStyleImplicitJoins ) ) {
255
DotNode.useThetaStyleImplicitJoins ) {
256                 // the "root from-element" in correlated subqueries do need this piece
257
elem.setType( FROM_FRAGMENT );
258                 joinSequence.setUseThetaStyle( true );
259                 elem.setUseFromFragment( false );
260             }
261         }
262
263         return elem;
264     }
265
266     FromElement createElementJoin(QueryableCollection queryableCollection) throws SemanticException {
267         FromElement elem;
268
269         implied = true; //TODO: always true for now, but not if we later decide to support elements() in the from clause
270
inElementsFunction = true;
271         Type elementType = queryableCollection.getElementType();
272         if ( !elementType.isEntityType() ) {
273             throw new IllegalArgumentException JavaDoc( "Cannot create element join for a collection of non-entities!" );
274         }
275         this.queryableCollection = queryableCollection;
276         SessionFactoryHelper sfh = fromClause.getSessionFactoryHelper();
277         FromElement destination = null;
278         String JavaDoc tableAlias = null;
279         EntityPersister entityPersister = queryableCollection.getElementPersister();
280         tableAlias = fromClause.getAliasGenerator().createName( entityPersister.getEntityName() );
281         String JavaDoc associatedEntityName = entityPersister.getEntityName();
282         EntityPersister targetEntityPersister = sfh.requireClassPersister( associatedEntityName );
283         // Create the FROM element for the target (the elements of the collection).
284
destination = createAndAddFromElement( associatedEntityName,
285                 classAlias,
286                 targetEntityPersister,
287                 ( EntityType ) queryableCollection.getElementType(),
288                 tableAlias
289         );
290         // If the join is implied, then don't include sub-classes on the element.
291
if ( implied ) {
292             destination.setIncludeSubclasses( false );
293         }
294         fromClause.addCollectionJoinFromElementByPath( path, destination );
295 // origin.addDestination(destination);
296
// Add the query spaces.
297
fromClause.getWalker().addQuerySpaces( entityPersister.getQuerySpaces() );
298
299         CollectionType type = queryableCollection.getCollectionType();
300         String JavaDoc role = type.getRole();
301         String JavaDoc roleAlias = origin.getTableAlias();
302
303         String JavaDoc[] targetColumns = sfh.getCollectionElementColumns( role, roleAlias );
304         AssociationType elementAssociationType = sfh.getElementAssociationType( type );
305
306         // Create the join element under the from element.
307
int joinType = JoinFragment.INNER_JOIN;
308         JoinSequence joinSequence = sfh.createJoinSequence( implied, elementAssociationType, tableAlias, joinType, targetColumns );
309         elem = initializeJoin( path, destination, joinSequence, targetColumns, origin, false );
310         elem.setUseFromFragment( true ); // The associated entity is implied, but it must be included in the FROM.
311
elem.setCollectionTableAlias( roleAlias ); // The collection alias is the role.
312
return elem;
313     }
314
315     private FromElement createCollectionJoin(JoinSequence collectionJoinSequence, String JavaDoc tableAlias) throws SemanticException {
316         String JavaDoc text = queryableCollection.getTableName();
317         AST ast = createFromElement( text );
318         FromElement destination = ( FromElement ) ast;
319         Type elementType = queryableCollection.getElementType();
320         if ( elementType.isCollectionType() ) {
321             throw new SemanticException( "Collections of collections are not supported!" );
322         }
323         destination.initializeCollection( fromClause, classAlias, tableAlias );
324         destination.setType( JOIN_FRAGMENT ); // Tag this node as a JOIN.
325
destination.setIncludeSubclasses( false ); // Don't include subclasses in the join.
326
destination.setCollectionJoin( true ); // This is a clollection join.
327
destination.setJoinSequence( collectionJoinSequence );
328         destination.setOrigin( origin, false );
329 // origin.addDestination( destination );
330
// This was the cause of HHH-242
331
// origin.setType( FROM_FRAGMENT ); // Set the parent node type so that the AST is properly formed.
332
origin.setText( "" ); // The destination node will have all the FROM text.
333
origin.setCollectionJoin( true ); // The parent node is a collection join too (voodoo - see JoinProcessor)
334
fromClause.addCollectionJoinFromElementByPath( path, destination );
335         fromClause.getWalker().addQuerySpaces( queryableCollection.getCollectionSpaces() );
336         return destination;
337     }
338
339     private FromElement createEntityAssociation(
340             String JavaDoc role,
341             String JavaDoc roleAlias,
342             int joinType) throws SemanticException {
343         FromElement elem;
344         Queryable entityPersister = ( Queryable ) queryableCollection.getElementPersister();
345         String JavaDoc associatedEntityName = entityPersister.getEntityName();
346         // Get the class name of the associated entity.
347
if ( queryableCollection.isOneToMany() ) {
348             if ( log.isDebugEnabled() ) {
349                 log.debug( "createEntityAssociation() : One to many - path = " + path + " role = " + role + " associatedEntityName = " + associatedEntityName );
350             }
351             JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
352
353             elem = createJoin( associatedEntityName, roleAlias, joinSequence, ( EntityType ) queryableCollection.getElementType(), false );
354         }
355         else {
356             if ( log.isDebugEnabled() ) {
357                 log.debug( "createManyToMany() : path = " + path + " role = " + role + " associatedEntityName = " + associatedEntityName );
358             }
359             elem = createManyToMany( role, associatedEntityName,
360                     roleAlias, entityPersister, ( EntityType ) queryableCollection.getElementType(), joinType );
361             fromClause.getWalker().addQuerySpaces( queryableCollection.getCollectionSpaces() );
362         }
363         elem.setCollectionTableAlias( roleAlias );
364         return elem;
365     }
366
367     private FromElement createJoin(
368             String JavaDoc entityClass,
369             String JavaDoc tableAlias,
370             JoinSequence joinSequence,
371             EntityType type,
372             boolean manyToMany) throws SemanticException {
373         // origin, path, implied, columns, classAlias,
374
EntityPersister entityPersister = fromClause.getSessionFactoryHelper().requireClassPersister( entityClass );
375         FromElement destination = createAndAddFromElement( entityClass,
376                 classAlias,
377                 entityPersister,
378                 type,
379                 tableAlias );
380         return initializeJoin( path, destination, joinSequence, getColumns(), origin, manyToMany );
381     }
382
383     private FromElement createManyToMany(
384             String JavaDoc role,
385             String JavaDoc associatedEntityName,
386             String JavaDoc roleAlias,
387             Queryable entityPersister,
388             EntityType type,
389             int joinType) throws SemanticException {
390         FromElement elem;
391         SessionFactoryHelper sfh = fromClause.getSessionFactoryHelper();
392         if ( inElementsFunction /*implied*/ ) {
393             // For implied many-to-many, just add the end join.
394
JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
395             elem = createJoin( associatedEntityName, roleAlias, joinSequence, type, true );
396         }
397         else {
398             // For an explicit many-to-many relationship, add a second join from the intermediate
399
// (many-to-many) table to the destination table. Also, make sure that the from element's
400
// idea of the destination is the destination table.
401
String JavaDoc tableAlias = fromClause.getAliasGenerator().createName( entityPersister.getEntityName() );
402             String JavaDoc[] secondJoinColumns = sfh.getCollectionElementColumns( role, roleAlias );
403             // Add the second join, the one that ends in the destination table.
404
JoinSequence joinSequence = createJoinSequence( roleAlias, joinType );
405             joinSequence.addJoin( sfh.getElementAssociationType( collectionType ), tableAlias, joinType, secondJoinColumns );
406             elem = createJoin( associatedEntityName, tableAlias, joinSequence, type, false );
407             elem.setUseFromFragment( true );
408         }
409         return elem;
410     }
411
412     private JoinSequence createJoinSequence(String JavaDoc roleAlias, int joinType) {
413         SessionFactoryHelper sessionFactoryHelper = fromClause.getSessionFactoryHelper();
414         String JavaDoc[] joinColumns = getColumns();
415         if ( collectionType == null ) {
416             throw new IllegalStateException JavaDoc( "collectionType is null!" );
417         }
418         return sessionFactoryHelper.createJoinSequence( implied, collectionType, roleAlias, joinType, joinColumns );
419     }
420
421     private FromElement createAndAddFromElement(
422             String JavaDoc className,
423             String JavaDoc classAlias,
424             EntityPersister entityPersister,
425             EntityType type,
426             String JavaDoc tableAlias) {
427         if ( !( entityPersister instanceof Joinable ) ) {
428             throw new IllegalArgumentException JavaDoc( "EntityPersister " + entityPersister + " does not implement Joinable!" );
429         }
430         FromElement element = createFromElement( entityPersister );
431         initializeAndAddFromElement( element, className, classAlias, entityPersister, type, tableAlias );
432         return element;
433     }
434
435     private void initializeAndAddFromElement(
436             FromElement element,
437             String JavaDoc className,
438             String JavaDoc classAlias,
439             EntityPersister entityPersister,
440             EntityType type,
441             String JavaDoc tableAlias) {
442         if ( tableAlias == null ) {
443             AliasGenerator aliasGenerator = fromClause.getAliasGenerator();
444             tableAlias = aliasGenerator.createName( entityPersister.getEntityName() );
445         }
446         element.initializeEntity( fromClause, className, entityPersister, type, classAlias, tableAlias );
447     }
448
449     private FromElement createFromElement(EntityPersister entityPersister) {
450         Joinable joinable = ( Joinable ) entityPersister;
451         String JavaDoc text = joinable.getTableName();
452         AST ast = createFromElement( text );
453         FromElement element = ( FromElement ) ast;
454         return element;
455     }
456
457     private AST createFromElement(String JavaDoc text) {
458         AST ast = ASTUtil.create( fromClause.getASTFactory(),
459                 implied ? IMPLIED_FROM : FROM_FRAGMENT, // This causes the factory to instantiate the desired class.
460
text );
461         // Reset the node type, because the rest of the system is expecting FROM_FRAGMENT, all we wanted was
462
// for the factory to create the right sub-class. This might get reset again later on anyway to make the
463
// SQL generation simpler.
464
ast.setType( FROM_FRAGMENT );
465         return ast;
466     }
467
468     private FromElement initializeJoin(
469             String JavaDoc path,
470             FromElement destination,
471             JoinSequence joinSequence,
472             String JavaDoc[] columns,
473             FromElement origin,
474             boolean manyToMany) {
475         destination.setType( JOIN_FRAGMENT );
476         destination.setJoinSequence( joinSequence );
477         destination.setColumns( columns );
478         destination.setOrigin( origin, manyToMany );
479         fromClause.addJoinByPathMap( path, destination );
480         return destination;
481     }
482
483     private String JavaDoc[] getColumns() {
484         if ( columns == null ) {
485             throw new IllegalStateException JavaDoc( "No foriegn key columns were supplied!" );
486         }
487         return columns;
488     }
489 }
490
Popular Tags