001: // $Id: Collection.java 11495 2007-05-09 03:52:56Z steve.ebersole@jboss.com $
002: package org.hibernate.mapping;
003:
004: import java.util.Comparator;
005: import java.util.HashMap;
006: import java.util.HashSet;
007: import java.util.Iterator;
008: import java.util.Properties;
009:
010: import org.hibernate.FetchMode;
011: import org.hibernate.MappingException;
012: import org.hibernate.engine.Mapping;
013: import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
014: import org.hibernate.type.CollectionType;
015: import org.hibernate.type.Type;
016: import org.hibernate.type.TypeFactory;
017: import org.hibernate.util.ArrayHelper;
018: import org.hibernate.util.EmptyIterator;
019: import org.hibernate.util.ReflectHelper;
020:
021: /**
022: * Mapping for a collection. Subclasses specialize to particular collection styles.
023: *
024: * @author Gavin King
025: */
026: public abstract class Collection implements Fetchable, Value,
027: Filterable {
028:
029: public static final String DEFAULT_ELEMENT_COLUMN_NAME = "elt";
030: public static final String DEFAULT_KEY_COLUMN_NAME = "id";
031:
032: private KeyValue key;
033: private Value element;
034: private Table collectionTable;
035: private String role;
036: private boolean lazy;
037: private boolean extraLazy;
038: private boolean inverse;
039: private boolean mutable = true;
040: private boolean subselectLoadable;
041: private String cacheConcurrencyStrategy;
042: private String cacheRegionName;
043: private String orderBy;
044: private String where;
045: private String manyToManyWhere;
046: private String manyToManyOrderBy;
047: private PersistentClass owner;
048: private String referencedPropertyName;
049: private String nodeName;
050: private String elementNodeName;
051: private boolean sorted;
052: private Comparator comparator;
053: private String comparatorClassName;
054: private boolean orphanDelete;
055: private int batchSize = -1;
056: private FetchMode fetchMode;
057: private boolean embedded = true;
058: private boolean optimisticLocked = true;
059: private Class collectionPersisterClass;
060: private String typeName;
061: private Properties typeParameters;
062: private final java.util.Map filters = new HashMap();
063: private final java.util.Map manyToManyFilters = new HashMap();
064: private final java.util.Set synchronizedTables = new HashSet();
065:
066: private String customSQLInsert;
067: private boolean customInsertCallable;
068: private ExecuteUpdateResultCheckStyle insertCheckStyle;
069: private String customSQLUpdate;
070: private boolean customUpdateCallable;
071: private ExecuteUpdateResultCheckStyle updateCheckStyle;
072: private String customSQLDelete;
073: private boolean customDeleteCallable;
074: private ExecuteUpdateResultCheckStyle deleteCheckStyle;
075: private String customSQLDeleteAll;
076: private boolean customDeleteAllCallable;
077: private ExecuteUpdateResultCheckStyle deleteAllCheckStyle;
078:
079: private String loaderName;
080:
081: protected Collection(PersistentClass owner) {
082: this .owner = owner;
083: }
084:
085: public boolean isSet() {
086: return false;
087: }
088:
089: public KeyValue getKey() {
090: return key;
091: }
092:
093: public Value getElement() {
094: return element;
095: }
096:
097: public boolean isIndexed() {
098: return false;
099: }
100:
101: public Table getCollectionTable() {
102: return collectionTable;
103: }
104:
105: public void setCollectionTable(Table table) {
106: this .collectionTable = table;
107: }
108:
109: public boolean isSorted() {
110: return sorted;
111: }
112:
113: public Comparator getComparator() {
114: if (comparator == null && comparatorClassName != null) {
115: try {
116: setComparator((Comparator) ReflectHelper.classForName(
117: comparatorClassName).newInstance());
118: } catch (Exception e) {
119: throw new MappingException(
120: "Could not instantiate comparator class ["
121: + comparatorClassName
122: + "] for collection " + getRole());
123: }
124: }
125: return comparator;
126: }
127:
128: public boolean isLazy() {
129: return lazy;
130: }
131:
132: public void setLazy(boolean lazy) {
133: this .lazy = lazy;
134: }
135:
136: public String getRole() {
137: return role;
138: }
139:
140: public abstract CollectionType getDefaultCollectionType()
141: throws MappingException;
142:
143: public boolean isPrimitiveArray() {
144: return false;
145: }
146:
147: public boolean isArray() {
148: return false;
149: }
150:
151: public boolean hasFormula() {
152: return false;
153: }
154:
155: public boolean isOneToMany() {
156: return element instanceof OneToMany;
157: }
158:
159: public boolean isInverse() {
160: return inverse;
161: }
162:
163: public String getOwnerEntityName() {
164: return owner.getEntityName();
165: }
166:
167: public String getOrderBy() {
168: return orderBy;
169: }
170:
171: public void setComparator(Comparator comparator) {
172: this .comparator = comparator;
173: }
174:
175: public void setElement(Value element) {
176: this .element = element;
177: }
178:
179: public void setKey(KeyValue key) {
180: this .key = key;
181: }
182:
183: public void setOrderBy(String orderBy) {
184: this .orderBy = orderBy;
185: }
186:
187: public void setRole(String role) {
188: this .role = role == null ? null : role.intern();
189: }
190:
191: public void setSorted(boolean sorted) {
192: this .sorted = sorted;
193: }
194:
195: public void setInverse(boolean inverse) {
196: this .inverse = inverse;
197: }
198:
199: public PersistentClass getOwner() {
200: return owner;
201: }
202:
203: public void setOwner(PersistentClass owner) {
204: this .owner = owner;
205: }
206:
207: public String getWhere() {
208: return where;
209: }
210:
211: public void setWhere(String where) {
212: this .where = where;
213: }
214:
215: public String getManyToManyWhere() {
216: return manyToManyWhere;
217: }
218:
219: public void setManyToManyWhere(String manyToManyWhere) {
220: this .manyToManyWhere = manyToManyWhere;
221: }
222:
223: public String getManyToManyOrdering() {
224: return manyToManyOrderBy;
225: }
226:
227: public void setManyToManyOrdering(String orderFragment) {
228: this .manyToManyOrderBy = orderFragment;
229: }
230:
231: public boolean isIdentified() {
232: return false;
233: }
234:
235: public boolean hasOrphanDelete() {
236: return orphanDelete;
237: }
238:
239: public void setOrphanDelete(boolean orphanDelete) {
240: this .orphanDelete = orphanDelete;
241: }
242:
243: public int getBatchSize() {
244: return batchSize;
245: }
246:
247: public void setBatchSize(int i) {
248: batchSize = i;
249: }
250:
251: public FetchMode getFetchMode() {
252: return fetchMode;
253: }
254:
255: public void setFetchMode(FetchMode fetchMode) {
256: this .fetchMode = fetchMode;
257: }
258:
259: public void setCollectionPersisterClass(Class persister) {
260: this .collectionPersisterClass = persister;
261: }
262:
263: public Class getCollectionPersisterClass() {
264: return collectionPersisterClass;
265: }
266:
267: public void validate(Mapping mapping) throws MappingException {
268: if (getKey().isCascadeDeleteEnabled()
269: && (!isInverse() || !isOneToMany())) {
270: throw new MappingException(
271: "only inverse one-to-many associations may use on-delete=\"cascade\": "
272: + getRole());
273: }
274: if (!getKey().isValid(mapping)) {
275: throw new MappingException(
276: "collection foreign key mapping has wrong number of columns: "
277: + getRole() + " type: "
278: + getKey().getType().getName());
279: }
280: if (!getElement().isValid(mapping)) {
281: throw new MappingException(
282: "collection element mapping has wrong number of columns: "
283: + getRole() + " type: "
284: + getElement().getType().getName());
285: }
286:
287: checkColumnDuplication();
288:
289: if (elementNodeName != null && elementNodeName.startsWith("@")) {
290: throw new MappingException(
291: "element node must not be an attribute: "
292: + elementNodeName);
293: }
294: if (elementNodeName != null && elementNodeName.equals(".")) {
295: throw new MappingException(
296: "element node must not be the parent: "
297: + elementNodeName);
298: }
299: if (nodeName != null && nodeName.indexOf('@') > -1) {
300: throw new MappingException(
301: "collection node must not be an attribute: "
302: + elementNodeName);
303: }
304: }
305:
306: private void checkColumnDuplication(java.util.Set distinctColumns,
307: Iterator columns) throws MappingException {
308: while (columns.hasNext()) {
309: Selectable s = (Selectable) columns.next();
310: if (!s.isFormula()) {
311: Column col = (Column) s;
312: if (!distinctColumns.add(col.getName())) {
313: throw new MappingException(
314: "Repeated column in mapping for collection: "
315: + getRole() + " column: "
316: + col.getName());
317: }
318: }
319: }
320: }
321:
322: private void checkColumnDuplication() throws MappingException {
323: HashSet cols = new HashSet();
324: checkColumnDuplication(cols, getKey().getColumnIterator());
325: if (isIndexed()) {
326: checkColumnDuplication(cols, ((IndexedCollection) this )
327: .getIndex().getColumnIterator());
328: }
329: if (isIdentified()) {
330: checkColumnDuplication(cols, ((IdentifierCollection) this )
331: .getIdentifier().getColumnIterator());
332: }
333: if (!isOneToMany()) {
334: checkColumnDuplication(cols, getElement()
335: .getColumnIterator());
336: }
337: }
338:
339: public Iterator getColumnIterator() {
340: return EmptyIterator.INSTANCE;
341: }
342:
343: public int getColumnSpan() {
344: return 0;
345: }
346:
347: public Type getType() throws MappingException {
348: return getCollectionType();
349: }
350:
351: public CollectionType getCollectionType() {
352: if (typeName == null) {
353: return getDefaultCollectionType();
354: } else {
355: return TypeFactory.customCollection(typeName,
356: typeParameters, role, referencedPropertyName,
357: isEmbedded());
358: }
359: }
360:
361: public boolean isNullable() {
362: return true;
363: }
364:
365: public boolean isAlternateUniqueKey() {
366: return false;
367: }
368:
369: public Table getTable() {
370: return owner.getTable();
371: }
372:
373: public void createForeignKey() {
374: }
375:
376: public boolean isSimpleValue() {
377: return false;
378: }
379:
380: public boolean isValid(Mapping mapping) throws MappingException {
381: return true;
382: }
383:
384: private void createForeignKeys() throws MappingException {
385: // if ( !isInverse() ) { // for inverse collections, let the "other end" handle it
386: if (referencedPropertyName == null) {
387: getElement().createForeignKey();
388: key.createForeignKeyOfEntity(getOwner().getEntityName());
389: }
390: // }
391: }
392:
393: abstract void createPrimaryKey();
394:
395: public void createAllKeys() throws MappingException {
396: createForeignKeys();
397: if (!isInverse())
398: createPrimaryKey();
399: }
400:
401: public String getCacheConcurrencyStrategy() {
402: return cacheConcurrencyStrategy;
403: }
404:
405: public void setCacheConcurrencyStrategy(
406: String cacheConcurrencyStrategy) {
407: this .cacheConcurrencyStrategy = cacheConcurrencyStrategy;
408: }
409:
410: public void setTypeUsingReflection(String className,
411: String propertyName) {
412: }
413:
414: public String getCacheRegionName() {
415: return cacheRegionName == null ? role : cacheRegionName;
416: }
417:
418: public void setCacheRegionName(String cacheRegionName) {
419: this .cacheRegionName = cacheRegionName;
420: }
421:
422: public void setCustomSQLInsert(String customSQLInsert,
423: boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
424: this .customSQLInsert = customSQLInsert;
425: this .customInsertCallable = callable;
426: this .insertCheckStyle = checkStyle;
427: }
428:
429: public String getCustomSQLInsert() {
430: return customSQLInsert;
431: }
432:
433: public boolean isCustomInsertCallable() {
434: return customInsertCallable;
435: }
436:
437: public ExecuteUpdateResultCheckStyle getCustomSQLInsertCheckStyle() {
438: return insertCheckStyle;
439: }
440:
441: public void setCustomSQLUpdate(String customSQLUpdate,
442: boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
443: this .customSQLUpdate = customSQLUpdate;
444: this .customUpdateCallable = callable;
445: this .updateCheckStyle = checkStyle;
446: }
447:
448: public String getCustomSQLUpdate() {
449: return customSQLUpdate;
450: }
451:
452: public boolean isCustomUpdateCallable() {
453: return customUpdateCallable;
454: }
455:
456: public ExecuteUpdateResultCheckStyle getCustomSQLUpdateCheckStyle() {
457: return updateCheckStyle;
458: }
459:
460: public void setCustomSQLDelete(String customSQLDelete,
461: boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
462: this .customSQLDelete = customSQLDelete;
463: this .customDeleteCallable = callable;
464: this .deleteCheckStyle = checkStyle;
465: }
466:
467: public String getCustomSQLDelete() {
468: return customSQLDelete;
469: }
470:
471: public boolean isCustomDeleteCallable() {
472: return customDeleteCallable;
473: }
474:
475: public ExecuteUpdateResultCheckStyle getCustomSQLDeleteCheckStyle() {
476: return deleteCheckStyle;
477: }
478:
479: public void setCustomSQLDeleteAll(String customSQLDeleteAll,
480: boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
481: this .customSQLDeleteAll = customSQLDeleteAll;
482: this .customDeleteAllCallable = callable;
483: this .deleteAllCheckStyle = checkStyle;
484: }
485:
486: public String getCustomSQLDeleteAll() {
487: return customSQLDeleteAll;
488: }
489:
490: public boolean isCustomDeleteAllCallable() {
491: return customDeleteAllCallable;
492: }
493:
494: public ExecuteUpdateResultCheckStyle getCustomSQLDeleteAllCheckStyle() {
495: return deleteAllCheckStyle;
496: }
497:
498: public void addFilter(String name, String condition) {
499: filters.put(name, condition);
500: }
501:
502: public java.util.Map getFilterMap() {
503: return filters;
504: }
505:
506: public void addManyToManyFilter(String name, String condition) {
507: manyToManyFilters.put(name, condition);
508: }
509:
510: public java.util.Map getManyToManyFilterMap() {
511: return manyToManyFilters;
512: }
513:
514: public String toString() {
515: return getClass().getName() + '(' + getRole() + ')';
516: }
517:
518: public java.util.Set getSynchronizedTables() {
519: return synchronizedTables;
520: }
521:
522: public String getLoaderName() {
523: return loaderName;
524: }
525:
526: public void setLoaderName(String name) {
527: this .loaderName = name == null ? null : name.intern();
528: }
529:
530: public String getReferencedPropertyName() {
531: return referencedPropertyName;
532: }
533:
534: public void setReferencedPropertyName(String propertyRef) {
535: this .referencedPropertyName = propertyRef == null ? null
536: : propertyRef.intern();
537: }
538:
539: public boolean isOptimisticLocked() {
540: return optimisticLocked;
541: }
542:
543: public void setOptimisticLocked(boolean optimisticLocked) {
544: this .optimisticLocked = optimisticLocked;
545: }
546:
547: public boolean isMap() {
548: return false;
549: }
550:
551: public String getTypeName() {
552: return typeName;
553: }
554:
555: public void setTypeName(String typeName) {
556: this .typeName = typeName;
557: }
558:
559: public Properties getTypeParameters() {
560: return typeParameters;
561: }
562:
563: public void setTypeParameters(Properties parameterMap) {
564: this .typeParameters = parameterMap;
565: }
566:
567: public boolean[] getColumnInsertability() {
568: return ArrayHelper.EMPTY_BOOLEAN_ARRAY;
569: }
570:
571: public boolean[] getColumnUpdateability() {
572: return ArrayHelper.EMPTY_BOOLEAN_ARRAY;
573: }
574:
575: public String getNodeName() {
576: return nodeName;
577: }
578:
579: public void setNodeName(String nodeName) {
580: this .nodeName = nodeName;
581: }
582:
583: public String getElementNodeName() {
584: return elementNodeName;
585: }
586:
587: public void setElementNodeName(String elementNodeName) {
588: this .elementNodeName = elementNodeName;
589: }
590:
591: public boolean isEmbedded() {
592: return embedded;
593: }
594:
595: public void setEmbedded(boolean embedded) {
596: this .embedded = embedded;
597: }
598:
599: public boolean isSubselectLoadable() {
600: return subselectLoadable;
601: }
602:
603: public void setSubselectLoadable(boolean subqueryLoadable) {
604: this .subselectLoadable = subqueryLoadable;
605: }
606:
607: public boolean isMutable() {
608: return mutable;
609: }
610:
611: public void setMutable(boolean mutable) {
612: this .mutable = mutable;
613: }
614:
615: public boolean isExtraLazy() {
616: return extraLazy;
617: }
618:
619: public void setExtraLazy(boolean extraLazy) {
620: this .extraLazy = extraLazy;
621: }
622:
623: public boolean hasOrder() {
624: return orderBy != null || manyToManyOrderBy != null;
625: }
626:
627: public void setComparatorClassName(String comparatorClassName) {
628: this .comparatorClassName = comparatorClassName;
629: }
630:
631: public String getComparatorClassName() {
632: return comparatorClassName;
633: }
634: }
|