1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
5 * indicated by the @author tags or express copyright attribution
6 * statements applied by the authors. All third-party contributions are
7 * distributed under license by Red Hat Middleware LLC.
8 *
9 * This copyrighted material is made available to anyone wishing to use, modify,
10 * copy, or redistribute it subject to the terms and conditions of the GNU
11 * Lesser General Public License, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this distribution; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301 USA
23 *
24 */
25 package org.hibernate.mapping;
26
27 import java.util.Comparator;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.Properties;
32
33 import org.hibernate.FetchMode;
34 import org.hibernate.MappingException;
35 import org.hibernate.engine.Mapping;
36 import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
37 import org.hibernate.type.CollectionType;
38 import org.hibernate.type.Type;
39 import org.hibernate.type.TypeFactory;
40 import org.hibernate.util.ArrayHelper;
41 import org.hibernate.util.EmptyIterator;
42 import org.hibernate.util.ReflectHelper;
43
44 /**
45 * Mapping for a collection. Subclasses specialize to particular collection styles.
46 *
47 * @author Gavin King
48 */
49 public abstract class Collection implements Fetchable, Value, Filterable {
50
51 public static final String DEFAULT_ELEMENT_COLUMN_NAME = "elt";
52 public static final String DEFAULT_KEY_COLUMN_NAME = "id";
53
54 private KeyValue key;
55 private Value element;
56 private Table collectionTable;
57 private String role;
58 private boolean lazy;
59 private boolean extraLazy;
60 private boolean inverse;
61 private boolean mutable = true;
62 private boolean subselectLoadable;
63 private String cacheConcurrencyStrategy;
64 private String cacheRegionName;
65 private String orderBy;
66 private String where;
67 private String manyToManyWhere;
68 private String manyToManyOrderBy;
69 private PersistentClass owner;
70 private String referencedPropertyName;
71 private String nodeName;
72 private String elementNodeName;
73 private boolean sorted;
74 private Comparator comparator;
75 private String comparatorClassName;
76 private boolean orphanDelete;
77 private int batchSize = -1;
78 private FetchMode fetchMode;
79 private boolean embedded = true;
80 private boolean optimisticLocked = true;
81 private Class collectionPersisterClass;
82 private String typeName;
83 private Properties typeParameters;
84 private final java.util.Map filters = new HashMap();
85 private final java.util.Map manyToManyFilters = new HashMap();
86 private final java.util.Set synchronizedTables = new HashSet();
87
88 private String customSQLInsert;
89 private boolean customInsertCallable;
90 private ExecuteUpdateResultCheckStyle insertCheckStyle;
91 private String customSQLUpdate;
92 private boolean customUpdateCallable;
93 private ExecuteUpdateResultCheckStyle updateCheckStyle;
94 private String customSQLDelete;
95 private boolean customDeleteCallable;
96 private ExecuteUpdateResultCheckStyle deleteCheckStyle;
97 private String customSQLDeleteAll;
98 private boolean customDeleteAllCallable;
99 private ExecuteUpdateResultCheckStyle deleteAllCheckStyle;
100
101 private String loaderName;
102
103 protected Collection(PersistentClass owner) {
104 this.owner = owner;
105 }
106
107 public boolean isSet() {
108 return false;
109 }
110
111 public KeyValue getKey() {
112 return key;
113 }
114
115 public Value getElement() {
116 return element;
117 }
118
119 public boolean isIndexed() {
120 return false;
121 }
122
123 public Table getCollectionTable() {
124 return collectionTable;
125 }
126
127 public void setCollectionTable(Table table) {
128 this.collectionTable = table;
129 }
130
131 public boolean isSorted() {
132 return sorted;
133 }
134
135 public Comparator getComparator() {
136 if ( comparator == null && comparatorClassName != null ) {
137 try {
138 setComparator( (Comparator) ReflectHelper.classForName( comparatorClassName ).newInstance() );
139 }
140 catch ( Exception e ) {
141 throw new MappingException(
142 "Could not instantiate comparator class [" + comparatorClassName
143 + "] for collection " + getRole()
144 );
145 }
146 }
147 return comparator;
148 }
149
150 public boolean isLazy() {
151 return lazy;
152 }
153
154 public void setLazy(boolean lazy) {
155 this.lazy = lazy;
156 }
157
158 public String getRole() {
159 return role;
160 }
161
162 public abstract CollectionType getDefaultCollectionType() throws MappingException;
163
164 public boolean isPrimitiveArray() {
165 return false;
166 }
167
168 public boolean isArray() {
169 return false;
170 }
171
172 public boolean hasFormula() {
173 return false;
174 }
175
176 public boolean isOneToMany() {
177 return element instanceof OneToMany;
178 }
179
180 public boolean isInverse() {
181 return inverse;
182 }
183
184 public String getOwnerEntityName() {
185 return owner.getEntityName();
186 }
187
188 public String getOrderBy() {
189 return orderBy;
190 }
191
192 public void setComparator(Comparator comparator) {
193 this.comparator = comparator;
194 }
195
196 public void setElement(Value element) {
197 this.element = element;
198 }
199
200 public void setKey(KeyValue key) {
201 this.key = key;
202 }
203
204 public void setOrderBy(String orderBy) {
205 this.orderBy = orderBy;
206 }
207
208 public void setRole(String role) {
209 this.role = role==null ? null : role.intern();
210 }
211
212 public void setSorted(boolean sorted) {
213 this.sorted = sorted;
214 }
215
216 public void setInverse(boolean inverse) {
217 this.inverse = inverse;
218 }
219
220 public PersistentClass getOwner() {
221 return owner;
222 }
223
224 public void setOwner(PersistentClass owner) {
225 this.owner = owner;
226 }
227
228 public String getWhere() {
229 return where;
230 }
231
232 public void setWhere(String where) {
233 this.where = where;
234 }
235
236 public String getManyToManyWhere() {
237 return manyToManyWhere;
238 }
239
240 public void setManyToManyWhere(String manyToManyWhere) {
241 this.manyToManyWhere = manyToManyWhere;
242 }
243
244 public String getManyToManyOrdering() {
245 return manyToManyOrderBy;
246 }
247
248 public void setManyToManyOrdering(String orderFragment) {
249 this.manyToManyOrderBy = orderFragment;
250 }
251
252 public boolean isIdentified() {
253 return false;
254 }
255
256 public boolean hasOrphanDelete() {
257 return orphanDelete;
258 }
259
260 public void setOrphanDelete(boolean orphanDelete) {
261 this.orphanDelete = orphanDelete;
262 }
263
264 public int getBatchSize() {
265 return batchSize;
266 }
267
268 public void setBatchSize(int i) {
269 batchSize = i;
270 }
271
272 public FetchMode getFetchMode() {
273 return fetchMode;
274 }
275
276 public void setFetchMode(FetchMode fetchMode) {
277 this.fetchMode = fetchMode;
278 }
279
280 public void setCollectionPersisterClass(Class persister) {
281 this.collectionPersisterClass = persister;
282 }
283
284 public Class getCollectionPersisterClass() {
285 return collectionPersisterClass;
286 }
287
288 public void validate(Mapping mapping) throws MappingException {
289 if ( getKey().isCascadeDeleteEnabled() && ( !isInverse() || !isOneToMany() ) ) {
290 throw new MappingException(
291 "only inverse one-to-many associations may use on-delete=\"cascade\": "
292 + getRole() );
293 }
294 if ( !getKey().isValid( mapping ) ) {
295 throw new MappingException(
296 "collection foreign key mapping has wrong number of columns: "
297 + getRole()
298 + " type: "
299 + getKey().getType().getName() );
300 }
301 if ( !getElement().isValid( mapping ) ) {
302 throw new MappingException(
303 "collection element mapping has wrong number of columns: "
304 + getRole()
305 + " type: "
306 + getElement().getType().getName() );
307 }
308
309 checkColumnDuplication();
310
311 if ( elementNodeName!=null && elementNodeName.startsWith("@") ) {
312 throw new MappingException("element node must not be an attribute: " + elementNodeName );
313 }
314 if ( elementNodeName!=null && elementNodeName.equals(".") ) {
315 throw new MappingException("element node must not be the parent: " + elementNodeName );
316 }
317 if ( nodeName!=null && nodeName.indexOf('@')>-1 ) {
318 throw new MappingException("collection node must not be an attribute: " + elementNodeName );
319 }
320 }
321
322 private void checkColumnDuplication(java.util.Set distinctColumns, Iterator columns)
323 throws MappingException {
324 while ( columns.hasNext() ) {
325 Selectable s = (Selectable) columns.next();
326 if ( !s.isFormula() ) {
327 Column col = (Column) s;
328 if ( !distinctColumns.add( col.getName() ) ) {
329 throw new MappingException( "Repeated column in mapping for collection: "
330 + getRole()
331 + " column: "
332 + col.getName() );
333 }
334 }
335 }
336 }
337
338 private void checkColumnDuplication() throws MappingException {
339 HashSet cols = new HashSet();
340 checkColumnDuplication( cols, getKey().getColumnIterator() );
341 if ( isIndexed() ) {
342 checkColumnDuplication( cols, ( (IndexedCollection) this )
343 .getIndex()
344 .getColumnIterator() );
345 }
346 if ( isIdentified() ) {
347 checkColumnDuplication( cols, ( (IdentifierCollection) this )
348 .getIdentifier()
349 .getColumnIterator() );
350 }
351 if ( !isOneToMany() ) {
352 checkColumnDuplication( cols, getElement().getColumnIterator() );
353 }
354 }
355
356 public Iterator getColumnIterator() {
357 return EmptyIterator.INSTANCE;
358 }
359
360 public int getColumnSpan() {
361 return 0;
362 }
363
364 public Type getType() throws MappingException {
365 return getCollectionType();
366 }
367
368 public CollectionType getCollectionType() {
369 if ( typeName == null ) {
370 return getDefaultCollectionType();
371 }
372 else {
373 return TypeFactory.customCollection( typeName, typeParameters, role, referencedPropertyName, isEmbedded() );
374 }
375 }
376
377 public boolean isNullable() {
378 return true;
379 }
380
381 public boolean isAlternateUniqueKey() {
382 return false;
383 }
384
385 public Table getTable() {
386 return owner.getTable();
387 }
388
389 public void createForeignKey() {
390 }
391
392 public boolean isSimpleValue() {
393 return false;
394 }
395
396 public boolean isValid(Mapping mapping) throws MappingException {
397 return true;
398 }
399
400 private void createForeignKeys() throws MappingException {
401 // if ( !isInverse() ) { // for inverse collections, let the "other end" handle it
402 if ( referencedPropertyName == null ) {
403 getElement().createForeignKey();
404 key.createForeignKeyOfEntity( getOwner().getEntityName() );
405 }
406 // }
407 }
408
409 abstract void createPrimaryKey();
410
411 public void createAllKeys() throws MappingException {
412 createForeignKeys();
413 if ( !isInverse() ) createPrimaryKey();
414 }
415
416 public String getCacheConcurrencyStrategy() {
417 return cacheConcurrencyStrategy;
418 }
419
420 public void setCacheConcurrencyStrategy(String cacheConcurrencyStrategy) {
421 this.cacheConcurrencyStrategy = cacheConcurrencyStrategy;
422 }
423
424 public void setTypeUsingReflection(String className, String propertyName) {
425 }
426
427 public String getCacheRegionName() {
428 return cacheRegionName == null ? role : cacheRegionName;
429 }
430
431 public void setCacheRegionName(String cacheRegionName) {
432 this.cacheRegionName = cacheRegionName;
433 }
434
435
436
437 public void setCustomSQLInsert(String customSQLInsert, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
438 this.customSQLInsert = customSQLInsert;
439 this.customInsertCallable = callable;
440 this.insertCheckStyle = checkStyle;
441 }
442
443 public String getCustomSQLInsert() {
444 return customSQLInsert;
445 }
446
447 public boolean isCustomInsertCallable() {
448 return customInsertCallable;
449 }
450
451 public ExecuteUpdateResultCheckStyle getCustomSQLInsertCheckStyle() {
452 return insertCheckStyle;
453 }
454
455 public void setCustomSQLUpdate(String customSQLUpdate, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
456 this.customSQLUpdate = customSQLUpdate;
457 this.customUpdateCallable = callable;
458 this.updateCheckStyle = checkStyle;
459 }
460
461 public String getCustomSQLUpdate() {
462 return customSQLUpdate;
463 }
464
465 public boolean isCustomUpdateCallable() {
466 return customUpdateCallable;
467 }
468
469 public ExecuteUpdateResultCheckStyle getCustomSQLUpdateCheckStyle() {
470 return updateCheckStyle;
471 }
472
473 public void setCustomSQLDelete(String customSQLDelete, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
474 this.customSQLDelete = customSQLDelete;
475 this.customDeleteCallable = callable;
476 this.deleteCheckStyle = checkStyle;
477 }
478
479 public String getCustomSQLDelete() {
480 return customSQLDelete;
481 }
482
483 public boolean isCustomDeleteCallable() {
484 return customDeleteCallable;
485 }
486
487 public ExecuteUpdateResultCheckStyle getCustomSQLDeleteCheckStyle() {
488 return deleteCheckStyle;
489 }
490
491 public void setCustomSQLDeleteAll(String customSQLDeleteAll, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) {
492 this.customSQLDeleteAll = customSQLDeleteAll;
493 this.customDeleteAllCallable = callable;
494 this.deleteAllCheckStyle = checkStyle;
495 }
496
497 public String getCustomSQLDeleteAll() {
498 return customSQLDeleteAll;
499 }
500
501 public boolean isCustomDeleteAllCallable() {
502 return customDeleteAllCallable;
503 }
504
505 public ExecuteUpdateResultCheckStyle getCustomSQLDeleteAllCheckStyle() {
506 return deleteAllCheckStyle;
507 }
508
509 public void addFilter(String name, String condition) {
510 filters.put( name, condition );
511 }
512
513 public java.util.Map getFilterMap() {
514 return filters;
515 }
516
517 public void addManyToManyFilter(String name, String condition) {
518 manyToManyFilters.put( name, condition );
519 }
520
521 public java.util.Map getManyToManyFilterMap() {
522 return manyToManyFilters;
523 }
524
525 public String toString() {
526 return getClass().getName() + '(' + getRole() + ')';
527 }
528
529 public java.util.Set getSynchronizedTables() {
530 return synchronizedTables;
531 }
532
533 public String getLoaderName() {
534 return loaderName;
535 }
536
537 public void setLoaderName(String name) {
538 this.loaderName = name==null ? null : name.intern();
539 }
540
541 public String getReferencedPropertyName() {
542 return referencedPropertyName;
543 }
544
545 public void setReferencedPropertyName(String propertyRef) {
546 this.referencedPropertyName = propertyRef==null ? null : propertyRef.intern();
547 }
548
549 public boolean isOptimisticLocked() {
550 return optimisticLocked;
551 }
552
553 public void setOptimisticLocked(boolean optimisticLocked) {
554 this.optimisticLocked = optimisticLocked;
555 }
556
557 public boolean isMap() {
558 return false;
559 }
560
561 public String getTypeName() {
562 return typeName;
563 }
564
565 public void setTypeName(String typeName) {
566 this.typeName = typeName;
567 }
568
569 public Properties getTypeParameters() {
570 return typeParameters;
571 }
572
573 public void setTypeParameters(Properties parameterMap) {
574 this.typeParameters = parameterMap;
575 }
576
577 public boolean[] getColumnInsertability() {
578 return ArrayHelper.EMPTY_BOOLEAN_ARRAY;
579 }
580
581 public boolean[] getColumnUpdateability() {
582 return ArrayHelper.EMPTY_BOOLEAN_ARRAY;
583 }
584
585 public String getNodeName() {
586 return nodeName;
587 }
588
589 public void setNodeName(String nodeName) {
590 this.nodeName = nodeName;
591 }
592
593 public String getElementNodeName() {
594 return elementNodeName;
595 }
596
597 public void setElementNodeName(String elementNodeName) {
598 this.elementNodeName = elementNodeName;
599 }
600
601 public boolean isEmbedded() {
602 return embedded;
603 }
604
605 public void setEmbedded(boolean embedded) {
606 this.embedded = embedded;
607 }
608
609 public boolean isSubselectLoadable() {
610 return subselectLoadable;
611 }
612
613
614 public void setSubselectLoadable(boolean subqueryLoadable) {
615 this.subselectLoadable = subqueryLoadable;
616 }
617
618 public boolean isMutable() {
619 return mutable;
620 }
621
622 public void setMutable(boolean mutable) {
623 this.mutable = mutable;
624 }
625
626 public boolean isExtraLazy() {
627 return extraLazy;
628 }
629
630 public void setExtraLazy(boolean extraLazy) {
631 this.extraLazy = extraLazy;
632 }
633
634 public boolean hasOrder() {
635 return orderBy!=null || manyToManyOrderBy!=null;
636 }
637
638 public void setComparatorClassName(String comparatorClassName) {
639 this.comparatorClassName = comparatorClassName;
640 }
641
642 public String getComparatorClassName() {
643 return comparatorClassName;
644 }
645 }