KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > type > CollectionType


1 // $Id: CollectionType.java,v 1.35 2005/07/20 19:56:46 oneovthafew Exp $
2
package org.hibernate.type;
3
4 import java.io.Serializable JavaDoc;
5 import java.sql.PreparedStatement JavaDoc;
6 import java.sql.ResultSet JavaDoc;
7 import java.sql.SQLException JavaDoc;
8 import java.util.ArrayList JavaDoc;
9 import java.util.Collection JavaDoc;
10 import java.util.Iterator JavaDoc;
11 import java.util.List JavaDoc;
12 import java.util.Map JavaDoc;
13
14 import org.dom4j.Element;
15 import org.dom4j.Node;
16 import org.hibernate.EntityMode;
17 import org.hibernate.Hibernate;
18 import org.hibernate.HibernateException;
19 import org.hibernate.MappingException;
20 import org.hibernate.collection.PersistentCollection;
21 import org.hibernate.engine.CollectionKey;
22 import org.hibernate.engine.EntityEntry;
23 import org.hibernate.engine.Mapping;
24 import org.hibernate.engine.PersistenceContext;
25 import org.hibernate.engine.SessionFactoryImplementor;
26 import org.hibernate.engine.SessionImplementor;
27 import org.hibernate.persister.collection.CollectionPersister;
28 import org.hibernate.persister.collection.QueryableCollection;
29 import org.hibernate.persister.entity.Joinable;
30 import org.hibernate.proxy.HibernateProxy;
31 import org.hibernate.proxy.LazyInitializer;
32 import org.hibernate.util.ArrayHelper;
33 import org.hibernate.util.MarkerObject;
34
35 /**
36  * A type that handles Hibernate <tt>PersistentCollection</tt>s (including arrays).
37  *
38  * @author Gavin King
39  */

40 public abstract class CollectionType extends AbstractType implements AssociationType {
41
42     private static final Object JavaDoc NOT_NULL_COLLECTION = new MarkerObject( "NOT NULL COLLECTION" );
43     public static final Object JavaDoc UNFETCHED_COLLECTION = new MarkerObject( "UNFETCHED COLLECTION" );
44
45     private final String JavaDoc role;
46     private final String JavaDoc foreignKeyPropertyName;
47     private final boolean isEmbeddedInXML;
48
49     public CollectionType(String JavaDoc role, String JavaDoc foreignKeyPropertyName, boolean isEmbeddedInXML) {
50         this.role = role;
51         this.foreignKeyPropertyName = foreignKeyPropertyName;
52         this.isEmbeddedInXML = isEmbeddedInXML;
53     }
54     
55     public boolean isEmbeddedInXML() {
56         return isEmbeddedInXML;
57     }
58
59     public String JavaDoc getRole() {
60         return role;
61     }
62
63     public Object JavaDoc indexOf(Object JavaDoc collection, Object JavaDoc element) {
64         throw new UnsupportedOperationException JavaDoc( "generic collections don't have indexes" );
65     }
66
67     public boolean contains(Object JavaDoc collection, Object JavaDoc childObject, CollectionPersister persister,
68             SessionImplementor session) {
69         // we do not have to worry about queued additions to uninitialized
70
// collections, since they can only occur for inverse collections!
71
Iterator JavaDoc elems = getElementsIterator( collection, session );
72         while ( elems.hasNext() ) {
73             Object JavaDoc element = elems.next();
74             // worrying about proxies is perhaps a little bit of overkill here...
75
if ( element instanceof HibernateProxy ) {
76                 LazyInitializer li = ( (HibernateProxy) element ).getHibernateLazyInitializer();
77                 if ( !li.isUninitialized() ) element = li.getImplementation();
78             }
79             if ( element == childObject ) return true;
80         }
81         return false;
82     }
83
84     public boolean isCollectionType() {
85         return true;
86     }
87
88     public final boolean isEqual(Object JavaDoc x, Object JavaDoc y, EntityMode entityMode) {
89         return x == y
90             || ( x instanceof PersistentCollection && ( (PersistentCollection) x ).isWrapper( y ) )
91             || ( y instanceof PersistentCollection && ( (PersistentCollection) y ).isWrapper( x ) );
92     }
93
94     public int compare(Object JavaDoc x, Object JavaDoc y, EntityMode entityMode) {
95         return 0; // collections cannot be compared
96
}
97
98     public int getHashCode(Object JavaDoc x, EntityMode entityMode) {
99         throw new UnsupportedOperationException JavaDoc( "cannot perform lookups on collections" );
100     }
101
102     /**
103      * Instantiate an uninitialized collection wrapper or holder. Callers MUST add the holder to the
104      * persistence context!
105      */

106     public abstract PersistentCollection instantiate(SessionImplementor session,
107             CollectionPersister persister, Serializable JavaDoc key) throws HibernateException;
108
109     public Object JavaDoc nullSafeGet(ResultSet JavaDoc rs, String JavaDoc name, SessionImplementor session, Object JavaDoc owner)
110             throws HibernateException, SQLException JavaDoc {
111         return nullSafeGet( rs, new String JavaDoc[] { name }, session, owner );
112     }
113
114     public Object JavaDoc nullSafeGet(ResultSet JavaDoc rs, String JavaDoc[] name, SessionImplementor session, Object JavaDoc owner)
115             throws HibernateException, SQLException JavaDoc {
116         return resolve( null, session, owner );
117     }
118
119     public final void nullSafeSet(PreparedStatement JavaDoc st, Object JavaDoc value, int index, boolean[] settable,
120             SessionImplementor session) throws HibernateException, SQLException JavaDoc {
121         //NOOP
122
}
123
124     public void nullSafeSet(PreparedStatement JavaDoc st, Object JavaDoc value, int index,
125             SessionImplementor session) throws HibernateException, SQLException JavaDoc {
126     }
127
128     public int[] sqlTypes(Mapping session) throws MappingException {
129         return ArrayHelper.EMPTY_INT_ARRAY;
130     }
131
132     public int getColumnSpan(Mapping session) throws MappingException {
133         return 0;
134     }
135
136     public String JavaDoc toLoggableString(Object JavaDoc value, SessionFactoryImplementor factory)
137             throws HibernateException {
138
139         if ( value == null ) return "null";
140         
141         if ( Hibernate.isInitialized( value ) ) {
142             if ( getReturnedClass().isInstance(value) ) {
143                 List JavaDoc list = new ArrayList JavaDoc();
144                 Type elemType = getElementType( factory );
145                 Iterator JavaDoc iter = getElementsIterator( value );
146                 while ( iter.hasNext() ) {
147                     list.add( elemType.toLoggableString( iter.next(), factory ) );
148                 }
149                 return list.toString();
150             }
151             else {
152                 // for DOM4J "collections" only
153
return ( (Element) value ).asXML(); //TODO: it would be better if this was done at the higher level by Printer
154
}
155         }
156         else {
157             return "<uninitialized>";
158         }
159         
160     }
161
162     public Object JavaDoc deepCopy(Object JavaDoc value, EntityMode entityMode, SessionFactoryImplementor factory)
163             throws HibernateException {
164         return value;
165     }
166
167     public String JavaDoc getName() {
168         return getReturnedClass().getName() + '(' + getRole() + ')';
169     }
170
171     /**
172      * Get an iterator over the element set of the collection, which may not yet be wrapped
173      */

174     public Iterator JavaDoc getElementsIterator(Object JavaDoc collection, SessionImplementor session) {
175         if ( session.getEntityMode()==EntityMode.DOM4J ) {
176             final SessionFactoryImplementor factory = session.getFactory();
177             final CollectionPersister persister = factory.getCollectionPersister( getRole() );
178             final Type elementType = persister.getElementType();
179             
180             List JavaDoc elements = ( (Element) collection ).elements( persister.getElementNodeName() );
181             ArrayList JavaDoc results = new ArrayList JavaDoc();
182             for ( int i=0; i<elements.size(); i++ ) {
183                 Element value = (Element) elements.get(i);
184                 results.add( elementType.fromXMLNode( value, factory ) );
185             }
186             return results.iterator();
187         }
188         else {
189             return getElementsIterator(collection);
190         }
191     }
192
193     /**
194      * Get an iterator over the element set of the collection in POJO mode
195      */

196     protected Iterator JavaDoc getElementsIterator(Object JavaDoc collection) {
197         return ( (Collection JavaDoc) collection ).iterator();
198     }
199
200     public boolean isMutable() {
201         return false;
202     }
203
204     public Serializable JavaDoc disassemble(Object JavaDoc value, SessionImplementor session, Object JavaDoc owner)
205             throws HibernateException {
206         //remember the uk value
207

208         //This solution would allow us to eliminate the owner arg to disassemble(), but
209
//what if the collection was null, and then later had elements added? seems unsafe
210
//session.getPersistenceContext().getCollectionEntry( (PersistentCollection) value ).getKey();
211

212         final Serializable JavaDoc key = getKeyOfOwner(owner, session);
213         if (key==null) {
214             return null;
215         }
216         else {
217             return getPersister(session)
218                     .getKeyType()
219                     .disassemble( key, session, owner );
220         }
221     }
222
223     public Object JavaDoc assemble(Serializable JavaDoc cached, SessionImplementor session, Object JavaDoc owner)
224             throws HibernateException {
225         //we must use the "remembered" uk value, since it is
226
//not available from the EntityEntry during assembly
227
if (cached==null) {
228             return null;
229         }
230         else {
231             final Serializable JavaDoc key = (Serializable JavaDoc) getPersister(session)
232                     .getKeyType()
233                     .assemble( cached, session, owner);
234             return resolveKey( key, session, owner );
235         }
236     }
237
238     /**
239      * Is the owning entity versioned?
240      */

241     private boolean isOwnerVersioned(SessionImplementor session) throws MappingException {
242         return getPersister( session )
243                 .getOwnerEntityPersister()
244                 .isVersioned();
245     }
246
247     private CollectionPersister getPersister(SessionImplementor session) {
248         return session.getFactory()
249                 .getCollectionPersister( role );
250     }
251
252     public boolean isDirty(Object JavaDoc old, Object JavaDoc current, SessionImplementor session)
253             throws HibernateException {
254
255         // collections don't dirty an unversioned parent entity
256

257         // TODO: I don't really like this implementation; it would be better if
258
// this was handled by searchForDirtyCollections()
259
return isOwnerVersioned( session ) && super.isDirty( old, current, session );
260         // return false;
261

262     }
263
264     /**
265      * Wrap the naked collection instance in a wrapper, or instantiate a holder. Callers MUST add
266      * the holder to the persistence context!
267      */

268     public abstract PersistentCollection wrap(SessionImplementor session, Object JavaDoc collection);
269
270     /**
271      * Note: return true because this type is castable to <tt>AssociationType</tt>. Not because
272      * all collections are associations.
273      */

274     public boolean isAssociationType() {
275         return true;
276     }
277
278     public ForeignKeyDirection getForeignKeyDirection() {
279         return ForeignKeyDirection.FOREIGN_KEY_TO_PARENT;
280     }
281
282     /**
283      * Get the key value from the owning entity instance, usually the identifier, but might be some
284      * other unique key, in the case of property-ref
285      */

286     public Serializable JavaDoc getKeyOfOwner(Object JavaDoc owner, SessionImplementor session) {
287         
288         EntityEntry e = session.getPersistenceContext().getEntry( owner );
289         if ( e == null ) return null; // This just handles a particular case of component
290
// projection, perhaps get rid of it and throw an exception
291

292         if ( foreignKeyPropertyName == null ) {
293             return e.getId();
294         }
295         else {
296             // TODO: at the point where we are resolving collection references, we don't
297
// know if the uk value has been resolved (depends if it was earlier or
298
// later in the mapping document) - now, we could try and use e.getStatus()
299
// to decide to semiResolve(), trouble is that initializeEntity() reuses
300
// the same array for resolved and hydrated values
301
Object JavaDoc id = e.getLoadedValue( foreignKeyPropertyName );
302
303             // NOTE VERY HACKISH WORKAROUND!!
304
Type keyType = getPersister( session ).getKeyType();
305             if ( !keyType.getReturnedClass().isInstance( id ) ) {
306                 id = (Serializable JavaDoc) keyType.semiResolve(
307                         e.getLoadedValue( foreignKeyPropertyName ),
308                         session,
309                         owner
310                     );
311             }
312
313             return (Serializable JavaDoc) id;
314         }
315     }
316
317     public Object JavaDoc hydrate(ResultSet JavaDoc rs, String JavaDoc[] name, SessionImplementor session, Object JavaDoc owner) {
318         // can't just return null here, since that would
319
// cause an owning component to become null
320
return NOT_NULL_COLLECTION;
321     }
322
323     public Object JavaDoc resolve(Object JavaDoc value, SessionImplementor session, Object JavaDoc owner)
324             throws HibernateException {
325         
326         return resolveKey( getKeyOfOwner( owner, session ), session, owner );
327     }
328     
329     private Object JavaDoc resolveKey(Serializable JavaDoc key, SessionImplementor session, Object JavaDoc owner) {
330         // if (key==null) throw new AssertionFailure("owner identifier unknown when re-assembling
331
// collection reference");
332
return key == null ? null : // TODO: can this case really occur??
333
getCollection( key, session, owner );
334     }
335
336     public Object JavaDoc semiResolve(Object JavaDoc value, SessionImplementor session, Object JavaDoc owner)
337             throws HibernateException {
338         throw new UnsupportedOperationException JavaDoc(
339             "collection mappings may not form part of a property-ref" );
340     }
341
342     public boolean isArrayType() {
343         return false;
344     }
345
346     public boolean useLHSPrimaryKey() {
347         return foreignKeyPropertyName == null;
348     }
349
350     public String JavaDoc getRHSUniqueKeyPropertyName() {
351         return null;
352     }
353
354     public Joinable getAssociatedJoinable(SessionFactoryImplementor factory)
355             throws MappingException {
356         return (Joinable) factory.getCollectionPersister( role );
357     }
358
359     public boolean isModified(Object JavaDoc old, Object JavaDoc current, SessionImplementor session) throws HibernateException {
360         return false;
361     }
362
363     public String JavaDoc getAssociatedEntityName(SessionFactoryImplementor factory)
364             throws MappingException {
365         try {
366             
367             QueryableCollection collectionPersister = (QueryableCollection) factory
368                     .getCollectionPersister( role );
369             
370             if ( !collectionPersister.getElementType().isEntityType() ) {
371                 throw new MappingException(
372                         "collection was not an association: " +
373                         collectionPersister.getRole()
374                     );
375             }
376             
377             return collectionPersister.getElementPersister().getEntityName();
378             
379         }
380         catch (ClassCastException JavaDoc cce) {
381             throw new MappingException( "collection role is not queryable " + role );
382         }
383     }
384
385     /**
386      * Replace the elements of a collection with the elements of another collection
387      */

388     public Object JavaDoc replaceElements(Object JavaDoc original, Object JavaDoc target, Object JavaDoc owner, Map JavaDoc copyCache,
389             SessionImplementor session) throws HibernateException {
390
391         // TODO: does not work for EntityMode.DOM4J yet!
392

393         java.util.Collection JavaDoc result = (java.util.Collection JavaDoc) target;
394
395         result.clear();
396
397         // copy elements into newly empty target collection
398
Type elemType = getElementType( session.getFactory() );
399         Iterator JavaDoc iter = ( (java.util.Collection JavaDoc) original ).iterator();
400         while ( iter.hasNext() ) {
401             result.add( elemType.replace( iter.next(), null, session, owner, copyCache ) );
402         }
403         
404         return result;
405
406     }
407
408     /**
409      * Instantiate an empty instance of the "underlying" collection (not a wrapper)
410      */

411     public abstract Object JavaDoc instantiate(Object JavaDoc original);
412
413     public Object JavaDoc replace(final Object JavaDoc original, final Object JavaDoc target,
414             final SessionImplementor session, final Object JavaDoc owner, final Map JavaDoc copyCache)
415             throws HibernateException {
416
417         if ( original == null ) return null;
418         if ( !Hibernate.isInitialized( original ) ) return target;
419         //if ( original == target ) return target; // can't do this, since need to merge element references
420

421         Object JavaDoc result = target==null || target==original ? //instead, put the merged elements in a new collection
422
instantiate( original ) : target;
423         
424         //for arrays, replaceElements() may return a different reference, since
425
//the array length might not match
426
result = replaceElements( original, result, owner, copyCache, session );
427         
428         if (original==target) {
429             //get the elements back into the target
430
//TODO: this is a little inefficient, don't need to do a whole
431
// deep replaceElements() call
432
replaceElements( result, target, owner, copyCache, session );
433             result = target;
434         }
435                 
436         return result;
437
438     }
439
440     /**
441      * Get the Hibernate type of the collection elements
442      */

443     public final Type getElementType(SessionFactoryImplementor factory) throws MappingException {
444         return factory.getCollectionPersister( getRole() ).getElementType();
445     }
446
447     public String JavaDoc toString() {
448         return getClass().getName() + '(' + getRole() + ')';
449     }
450
451     public String JavaDoc getOnCondition(String JavaDoc alias, SessionFactoryImplementor factory, Map JavaDoc enabledFilters)
452             throws MappingException {
453         return getAssociatedJoinable( factory ).filterFragment( alias, enabledFilters );
454     }
455
456     /**
457      * instantiate a collection wrapper (called when loading an object)
458      */

459     public Object JavaDoc getCollection(Serializable JavaDoc key, SessionImplementor session, Object JavaDoc owner)
460             throws HibernateException {
461
462         CollectionPersister persister = getPersister( session );
463         final PersistenceContext persistenceContext = session.getPersistenceContext();
464         final EntityMode entityMode = session.getEntityMode();
465
466         if (entityMode==EntityMode.DOM4J && !isEmbeddedInXML) {
467             return UNFETCHED_COLLECTION;
468         }
469         
470         // check if collection is currently being loaded
471
PersistentCollection collection = persistenceContext
472                 .getCollectionLoadContext()
473                 .getLoadingCollection( persister, key, entityMode );
474         
475         if ( collection == null ) {
476             
477             // check if it is already completely loaded, but unowned
478
collection = persistenceContext.useUnownedCollection( new CollectionKey(persister, key, entityMode) );
479             
480             if (collection==null) {
481
482                 // create a new collection wrapper, to be initialized later
483
collection = instantiate( session, persister, key );
484                 collection.setOwner(owner);
485     
486                 persistenceContext.addUninitializedCollection( persister, collection, key );
487     
488                 // some collections are not lazy:
489
if ( initializeImmediately( entityMode ) ) {
490                     session.initializeCollection( collection, false );
491                 }
492                 else if ( !persister.isLazy() ) {
493                     persistenceContext.addNonLazyCollection( collection );
494                 }
495     
496                 if ( hasHolder( entityMode ) ) {
497                     session.getPersistenceContext().addCollectionHolder( collection );
498                 }
499                 
500             }
501             
502         }
503         
504         collection.setOwner(owner);
505
506         return collection.getValue();
507     }
508
509     public boolean hasHolder(EntityMode entityMode) {
510         return entityMode == EntityMode.DOM4J;
511     }
512
513     protected boolean initializeImmediately(EntityMode entityMode) {
514         return entityMode == EntityMode.DOM4J;
515     }
516
517     public String JavaDoc getLHSPropertyName() {
518         return foreignKeyPropertyName;
519     }
520
521     public boolean isXMLElement() {
522         return true;
523     }
524
525     public Object JavaDoc fromXMLNode(Node xml, Mapping factory) throws HibernateException {
526         return xml;
527     }
528
529     public void setToXMLNode(Node node, Object JavaDoc value, SessionFactoryImplementor factory)
530     throws HibernateException {
531         if ( !isEmbeddedInXML ) {
532             node.detach();
533         }
534         else {
535             replaceNode( node, (Element) value );
536         }
537     }
538     
539     public boolean isAlwaysDirtyChecked() {
540         return true;
541         // because we sometimes need to incremement version number of owner
542
// and also because of how assemble/disassemble is implemented for uks
543
}
544
545     public boolean[] toColumnNullness(Object JavaDoc value, Mapping mapping) {
546         return ArrayHelper.EMPTY_BOOLEAN_ARRAY;
547     }
548 }
549
Popular Tags