Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/hibernate/loader/collection/CollectionLoader.java


1   //$Id: CollectionLoader.java,v 1.5 2005/04/25 16:47:29 steveebersole Exp $
2   package org.hibernate.loader.collection;
3   
4   import java.io.Serializable;
5   import java.util.List;
6   import java.util.Map;
7   import java.util.Set;
8   import java.util.Iterator;
9   
10  import org.apache.commons.logging.Log;
11  import org.apache.commons.logging.LogFactory;
12  import org.hibernate.FetchMode;
13  import org.hibernate.HibernateException;
14  import org.hibernate.LockMode;
15  import org.hibernate.MappingException;
16  import org.hibernate.engine.SessionFactoryImplementor;
17  import org.hibernate.engine.SessionImplementor;
18  import org.hibernate.loader.OuterJoinLoader;
19  import org.hibernate.loader.OuterJoinableAssociation;
20  import org.hibernate.persister.collection.CollectionPersister;
21  import org.hibernate.persister.collection.QueryableCollection;
22  import org.hibernate.persister.entity.Loadable;
23  import org.hibernate.sql.JoinFragment;
24  import org.hibernate.sql.Select;
25  import org.hibernate.type.AssociationType;
26  import org.hibernate.type.EntityType;
27  import org.hibernate.type.Type;
28  import org.hibernate.util.ArrayHelper;
29  import org.hibernate.util.StringHelper;
30  
31  /**
32   * Loads a collection of values or a many-to-many association.
33   * <br>
34   * The collection persister must implement <tt>QueryableCOllection<tt>. For
35   * other collections, create a customized subclass of <tt>Loader</tt>.
36   *
37   * @see OneToManyLoader
38   * @author Gavin King
39   */
40  public class CollectionLoader extends OuterJoinLoader implements CollectionInitializer {
41  
42    private static final Log log = LogFactory.getLog(CollectionLoader.class);
43  
44    private final QueryableCollection collectionPersister;
45    private final Type keyType;
46    private String[] aliases;
47  
48    public CollectionLoader(QueryableCollection persister, SessionFactoryImplementor session, Map enabledFilters)
49    throws MappingException {
50      this(persister, 1, session, enabledFilters);
51    }
52  
53    public CollectionLoader(QueryableCollection persister, int batchSize, SessionFactoryImplementor factory, Map enabledFilters)
54    throws MappingException {
55      this(persister, batchSize, null, factory, enabledFilters);
56    }
57    
58    public CollectionLoader(QueryableCollection persister, int batchSize, String subquery, SessionFactoryImplementor factory, Map enabledFilters)
59    throws MappingException {
60  
61      super(factory, enabledFilters);
62  
63      this.keyType = persister.getKeyType();
64      this.collectionPersister = persister;
65  
66      String alias = generateRootAlias( persister.getRole() );
67  
68      final List associations = walkCollectionTree(persister, alias);
69      initStatementString(persister, alias, associations, batchSize, subquery);
70      initPersisters(associations);
71  
72      postInstantiate();
73  
74      log.debug( "Static select for collection " + persister.getRole() + ": " + getSQLString() );
75    }
76  
77    private void initPersisters(List associations)
78    throws MappingException {
79  
80      final int joins = associations.size();
81      lockModeArray = ArrayHelper.fillArray(LockMode.NONE, joins);
82  
83      persisters = new Loadable[joins];
84      owners = new int[joins];
85      aliases = new String[joins];
86      ownerAssociationType = new EntityType[joins];
87      for ( int i=0; i<joins; i++ ) {
88        OuterJoinableAssociation oj = (OuterJoinableAssociation) associations.get(i);
89        persisters[i] = (Loadable) oj.getJoinable();
90        //cast is safe b/c one-to-many can't outerjoin to another collection!
91        owners[i] = oj.getOwner(associations);
92        ownerAssociationType[i] = (EntityType) oj.getJoinableType();
93        aliases[i] = oj.getRHSAlias();
94      }
95  
96      if ( ArrayHelper.isAllNegative(owners) ) owners = null;
97  
98  
99    }
100 
101   protected CollectionPersister getCollectionPersister() {
102     return collectionPersister;
103   }
104 
105   public void initialize(Serializable id, SessionImplementor session)
106   throws HibernateException {
107     loadCollection(session, id, keyType);
108   }
109 
110   private void initStatementString(
111     final QueryableCollection persister,
112     final String alias,
113     final List associations,
114     final int batchSize,
115     final String subquery)
116   throws MappingException {
117 
118     final int joins=associations.size();
119     suffixes = generateSuffixes(joins);
120 
121     StringBuffer whereString = whereString(alias, persister.getKeyColumnNames(), batchSize, subquery);
122     String filter = persister.filterFragment( alias, getEnabledFilters() );
123     if ( persister.isManyToMany() ) {
124       // from the collection of associations, locate OJA for the
125       // ManyToOne corresponding to this persister to fully
126       // define the many-to-many; we need that OJA so that we can
127       // use its alias here
128       // TODO : is there a better way here?
129       Iterator itr = associations.iterator();
130       AssociationType associationType = ( AssociationType ) persister.getElementType();
131       while ( itr.hasNext() ) {
132         OuterJoinableAssociation oja = ( OuterJoinableAssociation ) itr.next();
133         if ( oja.getJoinableType() == associationType ) {
134           // we found it
135           filter += persister.getManyToManyFilterFragment( oja.getRHSAlias(), getEnabledFilters() );
136         }
137       }
138     }
139     whereString.insert( 0, StringHelper.moveAndToBeginning( filter ) );
140 
141     JoinFragment ojf = mergeOuterJoins(associations);
142     Select select = new Select( getDialect() )
143       .setSelectClause(
144         persister.selectFragment(alias) +
145         selectString(associations)
146       )
147       .setFromClause( persister.getTableName(), alias )
148       .setWhereClause( whereString.toString()  )
149       .setOuterJoins(
150         ojf.toFromFragmentString(),
151         ojf.toWhereFragmentString()
152       );
153 
154     if ( persister.hasOrdering() ) {
155       select.setOrderByClause( persister.getSQLOrderByString(alias) );
156     }
157 
158     if ( getFactory().getSettings().isCommentsEnabled() ) {
159       select.setComment( "load collection " + persister.getRole() );
160     }
161 
162     sql = select.toStatementString();
163   }
164 
165   /**
166    * We can use an inner join for first many-to-many association
167    */
168   protected int getJoinType(
169       AssociationType type, 
170       FetchMode config, 
171       String path, 
172       Set visitedAssociations,
173       String lhsTable,
174       String[] lhsColumns,
175       boolean nullable,
176       int currentDepth)
177   throws MappingException {
178 
179     int joinType = super.getJoinType(
180         type, 
181         config, 
182         path, 
183         visitedAssociations, 
184         lhsTable, 
185         lhsColumns, 
186         nullable, 
187         currentDepth
188     );
189     //we can use an inner join for the many-to-many
190     if ( joinType==JoinFragment.LEFT_OUTER_JOIN && "".equals(path) ) {
191       joinType=JoinFragment.INNER_JOIN;
192     }
193     return joinType;
194   }
195   
196   protected Type getKeyType() {
197     return keyType;
198   }
199 
200   public String toString() {
201     return getClass().getName() + '(' + collectionPersister.getRole() + ')';
202   }
203 
204   protected String[] getAliases() {
205     return aliases;
206   }
207 
208   protected boolean isSubselectLoadingEnabled() {
209     return hasSubselectLoadableCollections();
210   }
211 
212 }