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 }