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.persister.collection;
26
27 import java.io.Serializable;
28 import java.sql.PreparedStatement;
29 import java.sql.ResultSet;
30 import java.sql.SQLException;
31 import java.util.Arrays;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.Map;
35
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38 import org.hibernate.AssertionFailure;
39 import org.hibernate.FetchMode;
40 import org.hibernate.HibernateException;
41 import org.hibernate.MappingException;
42 import org.hibernate.QueryException;
43 import org.hibernate.TransientObjectException;
44 import org.hibernate.jdbc.Expectation;
45 import org.hibernate.jdbc.Expectations;
46 import org.hibernate.cache.CacheException;
47 import org.hibernate.cache.access.CollectionRegionAccessStrategy;
48 import org.hibernate.cache.entry.CacheEntryStructure;
49 import org.hibernate.cache.entry.StructuredCollectionCacheEntry;
50 import org.hibernate.cache.entry.StructuredMapCacheEntry;
51 import org.hibernate.cache.entry.UnstructuredCacheEntry;
52 import org.hibernate.cfg.Configuration;
53 import org.hibernate.collection.PersistentCollection;
54 import org.hibernate.dialect.Dialect;
55 import org.hibernate.engine.EntityKey;
56 import org.hibernate.engine.PersistenceContext;
57 import org.hibernate.engine.SessionFactoryImplementor;
58 import org.hibernate.engine.SessionImplementor;
59 import org.hibernate.engine.SubselectFetch;
60 import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
61 import org.hibernate.exception.JDBCExceptionHelper;
62 import org.hibernate.exception.SQLExceptionConverter;
63 import org.hibernate.id.IdentifierGenerator;
64 import org.hibernate.loader.collection.CollectionInitializer;
65 import org.hibernate.mapping.Collection;
66 import org.hibernate.mapping.Column;
67 import org.hibernate.mapping.Formula;
68 import org.hibernate.mapping.IdentifierCollection;
69 import org.hibernate.mapping.IndexedCollection;
70 import org.hibernate.mapping.List;
71 import org.hibernate.mapping.Selectable;
72 import org.hibernate.mapping.Table;
73 import org.hibernate.metadata.CollectionMetadata;
74 import org.hibernate.persister.entity.EntityPersister;
75 import org.hibernate.persister.entity.Loadable;
76 import org.hibernate.persister.entity.PropertyMapping;
77 import org.hibernate.pretty.MessageHelper;
78 import org.hibernate.sql.Alias;
79 import org.hibernate.sql.SelectFragment;
80 import org.hibernate.sql.SimpleSelect;
81 import org.hibernate.sql.Template;
82 import org.hibernate.type.AbstractComponentType;
83 import org.hibernate.type.CollectionType;
84 import org.hibernate.type.EntityType;
85 import org.hibernate.type.Type;
86 import org.hibernate.util.ArrayHelper;
87 import org.hibernate.util.CollectionHelper;
88 import org.hibernate.util.FilterHelper;
89 import org.hibernate.util.StringHelper;
90
91
92 /**
93 * Base implementation of the <tt>QueryableCollection</tt> interface.
94 *
95 * @author Gavin King
96 * @see BasicCollectionPersister
97 * @see OneToManyPersister
98 */
99 public abstract class AbstractCollectionPersister
100 implements CollectionMetadata, SQLLoadableCollection {
101 // TODO: encapsulate the protected instance variables!
102
103 private final String role;
104
105 //SQL statements
106 private final String sqlDeleteString;
107 private final String sqlInsertRowString;
108 private final String sqlUpdateRowString;
109 private final String sqlDeleteRowString;
110 private final String sqlSelectSizeString;
111 private final String sqlSelectRowByIndexString;
112 private final String sqlDetectRowByIndexString;
113 private final String sqlDetectRowByElementString;
114
115 private final String sqlOrderByString;
116 protected final String sqlWhereString;
117 private final String sqlOrderByStringTemplate;
118 private final String sqlWhereStringTemplate;
119 private final boolean hasOrder;
120 protected final boolean hasWhere;
121 private final int baseIndex;
122
123 private final String nodeName;
124 private final String elementNodeName;
125 private final String indexNodeName;
126
127 protected final boolean indexContainsFormula;
128 protected final boolean elementIsPureFormula;
129
130 //types
131 private final Type keyType;
132 private final Type indexType;
133 protected final Type elementType;
134 private final Type identifierType;
135
136 //columns
137 protected final String[] keyColumnNames;
138 protected final String[] indexColumnNames;
139 protected final String[] indexFormulaTemplates;
140 protected final String[] indexFormulas;
141 protected final boolean[] indexColumnIsSettable;
142 protected final String[] elementColumnNames;
143 protected final String[] elementFormulaTemplates;
144 protected final String[] elementFormulas;
145 protected final boolean[] elementColumnIsSettable;
146 protected final boolean[] elementColumnIsInPrimaryKey;
147 protected final String[] indexColumnAliases;
148 protected final String[] elementColumnAliases;
149 protected final String[] keyColumnAliases;
150
151 protected final String identifierColumnName;
152 private final String identifierColumnAlias;
153 //private final String unquotedIdentifierColumnName;
154
155 protected final String qualifiedTableName;
156
157 private final String queryLoaderName;
158
159 private final boolean isPrimitiveArray;
160 private final boolean isArray;
161 protected final boolean hasIndex;
162 protected final boolean hasIdentifier;
163 private final boolean isLazy;
164 private final boolean isExtraLazy;
165 private final boolean isInverse;
166 private final boolean isMutable;
167 private final boolean isVersioned;
168 protected final int batchSize;
169 private final FetchMode fetchMode;
170 private final boolean hasOrphanDelete;
171 private final boolean subselectLoadable;
172
173 //extra information about the element type
174 private final Class elementClass;
175 private final String entityName;
176
177 private final Dialect dialect;
178 private final SQLExceptionConverter sqlExceptionConverter;
179 private final SessionFactoryImplementor factory;
180 private final EntityPersister ownerPersister;
181 private final IdentifierGenerator identifierGenerator;
182 private final PropertyMapping elementPropertyMapping;
183 private final EntityPersister elementPersister;
184 private final CollectionRegionAccessStrategy cacheAccessStrategy;
185 private final CollectionType collectionType;
186 private CollectionInitializer initializer;
187
188 private final CacheEntryStructure cacheEntryStructure;
189
190 // dynamic filters for the collection
191 private final FilterHelper filterHelper;
192
193 // dynamic filters specifically for many-to-many inside the collection
194 private final FilterHelper manyToManyFilterHelper;
195
196 private final String manyToManyWhereString;
197 private final String manyToManyWhereTemplate;
198
199 private final String manyToManyOrderByString;
200 private final String manyToManyOrderByTemplate;
201
202 // custom sql
203 private final boolean insertCallable;
204 private final boolean updateCallable;
205 private final boolean deleteCallable;
206 private final boolean deleteAllCallable;
207 private ExecuteUpdateResultCheckStyle insertCheckStyle;
208 private ExecuteUpdateResultCheckStyle updateCheckStyle;
209 private ExecuteUpdateResultCheckStyle deleteCheckStyle;
210 private ExecuteUpdateResultCheckStyle deleteAllCheckStyle;
211
212 private final Serializable[] spaces;
213
214 private Map collectionPropertyColumnAliases = new HashMap();
215 private Map collectionPropertyColumnNames = new HashMap();
216
217 private static final Logger log = LoggerFactory.getLogger( AbstractCollectionPersister.class );
218
219 public AbstractCollectionPersister(
220 final Collection collection,
221 final CollectionRegionAccessStrategy cacheAccessStrategy,
222 final Configuration cfg,
223 final SessionFactoryImplementor factory) throws MappingException, CacheException {
224
225 this.factory = factory;
226 this.cacheAccessStrategy = cacheAccessStrategy;
227 if ( factory.getSettings().isStructuredCacheEntriesEnabled() ) {
228 cacheEntryStructure = collection.isMap() ?
229 ( CacheEntryStructure ) new StructuredMapCacheEntry() :
230 ( CacheEntryStructure ) new StructuredCollectionCacheEntry();
231 }
232 else {
233 cacheEntryStructure = new UnstructuredCacheEntry();
234 }
235
236 dialect = factory.getDialect();
237 sqlExceptionConverter = factory.getSQLExceptionConverter();
238 collectionType = collection.getCollectionType();
239 role = collection.getRole();
240 entityName = collection.getOwnerEntityName();
241 ownerPersister = factory.getEntityPersister(entityName);
242 queryLoaderName = collection.getLoaderName();
243 nodeName = collection.getNodeName();
244 isMutable = collection.isMutable();
245
246 Table table = collection.getCollectionTable();
247 fetchMode = collection.getElement().getFetchMode();
248 elementType = collection.getElement().getType();
249 //isSet = collection.isSet();
250 //isSorted = collection.isSorted();
251 isPrimitiveArray = collection.isPrimitiveArray();
252 isArray = collection.isArray();
253 subselectLoadable = collection.isSubselectLoadable();
254
255 qualifiedTableName = table.getQualifiedName(
256 dialect,
257 factory.getSettings().getDefaultCatalogName(),
258 factory.getSettings().getDefaultSchemaName()
259 );
260
261 int spacesSize = 1 + collection.getSynchronizedTables().size();
262 spaces = new String[spacesSize];
263 spaces[0] = qualifiedTableName;
264 Iterator iter = collection.getSynchronizedTables().iterator();
265 for ( int i = 1; i < spacesSize; i++ ) {
266 spaces[i] = (String) iter.next();
267 }
268
269 sqlOrderByString = collection.getOrderBy();
270 hasOrder = sqlOrderByString != null;
271 sqlOrderByStringTemplate = hasOrder ?
272 Template.renderOrderByStringTemplate(sqlOrderByString, dialect, factory.getSqlFunctionRegistry()) :
273 null;
274 sqlWhereString = StringHelper.isNotEmpty( collection.getWhere() ) ? "( " + collection.getWhere() + ") " : null;
275 hasWhere = sqlWhereString != null;
276 sqlWhereStringTemplate = hasWhere ?
277 Template.renderWhereStringTemplate(sqlWhereString, dialect, factory.getSqlFunctionRegistry()) :
278 null;
279
280 hasOrphanDelete = collection.hasOrphanDelete();
281
282 int batch = collection.getBatchSize();
283 if ( batch == -1 ) {
284 batch = factory.getSettings().getDefaultBatchFetchSize();
285 }
286 batchSize = batch;
287
288 isVersioned = collection.isOptimisticLocked();
289
290 // KEY
291
292 keyType = collection.getKey().getType();
293 iter = collection.getKey().getColumnIterator();
294 int keySpan = collection.getKey().getColumnSpan();
295 keyColumnNames = new String[keySpan];
296 keyColumnAliases = new String[keySpan];
297 int k = 0;
298 while ( iter.hasNext() ) {
299 // NativeSQL: collect key column and auto-aliases
300 Column col = ( (Column) iter.next() );
301 keyColumnNames[k] = col.getQuotedName(dialect);
302 keyColumnAliases[k] = col.getAlias(dialect);
303 k++;
304 }
305
306 //unquotedKeyColumnNames = StringHelper.unQuote(keyColumnAliases);
307
308 //ELEMENT
309
310 String elemNode = collection.getElementNodeName();
311 if ( elementType.isEntityType() ) {
312 String entityName = ( (EntityType) elementType ).getAssociatedEntityName();
313 elementPersister = factory.getEntityPersister(entityName);
314 if ( elemNode==null ) {
315 elemNode = cfg.getClassMapping(entityName).getNodeName();
316 }
317 // NativeSQL: collect element column and auto-aliases
318
319 }
320 else {
321 elementPersister = null;
322 }
323 elementNodeName = elemNode;
324
325 int elementSpan = collection.getElement().getColumnSpan();
326 elementColumnAliases = new String[elementSpan];
327 elementColumnNames = new String[elementSpan];
328 elementFormulaTemplates = new String[elementSpan];
329 elementFormulas = new String[elementSpan];
330 elementColumnIsSettable = new boolean[elementSpan];
331 elementColumnIsInPrimaryKey = new boolean[elementSpan];
332 boolean isPureFormula = true;
333 boolean hasNotNullableColumns = false;
334 int j = 0;
335 iter = collection.getElement().getColumnIterator();
336 while ( iter.hasNext() ) {
337 Selectable selectable = (Selectable) iter.next();
338 elementColumnAliases[j] = selectable.getAlias(dialect);
339 if ( selectable.isFormula() ) {
340 Formula form = (Formula) selectable;
341 elementFormulaTemplates[j] = form.getTemplate(dialect, factory.getSqlFunctionRegistry());
342 elementFormulas[j] = form.getFormula();
343 }
344 else {
345 Column col = (Column) selectable;
346 elementColumnNames[j] = col.getQuotedName(dialect);
347 elementColumnIsSettable[j] = true;
348 elementColumnIsInPrimaryKey[j] = !col.isNullable();
349 if ( !col.isNullable() ) {
350 hasNotNullableColumns = true;
351 }
352 isPureFormula = false;
353 }
354 j++;
355 }
356 elementIsPureFormula = isPureFormula;
357
358 //workaround, for backward compatibility of sets with no
359 //not-null columns, assume all columns are used in the
360 //row locator SQL
361 if ( !hasNotNullableColumns ) {
362 Arrays.fill( elementColumnIsInPrimaryKey, true );
363 }
364
365
366 // INDEX AND ROW SELECT
367
368 hasIndex = collection.isIndexed();
369 if (hasIndex) {
370 // NativeSQL: collect index column and auto-aliases
371 IndexedCollection indexedCollection = (IndexedCollection) collection;
372 indexType = indexedCollection.getIndex().getType();
373 int indexSpan = indexedCollection.getIndex().getColumnSpan();
374 iter = indexedCollection.getIndex().getColumnIterator();
375 indexColumnNames = new String[indexSpan];
376 indexFormulaTemplates = new String[indexSpan];
377 indexFormulas = new String[indexSpan];
378 indexColumnIsSettable = new boolean[indexSpan];
379 indexColumnAliases = new String[indexSpan];
380 int i = 0;
381 boolean hasFormula = false;
382 while ( iter.hasNext() ) {
383 Selectable s = (Selectable) iter.next();
384 indexColumnAliases[i] = s.getAlias(dialect);
385 if ( s.isFormula() ) {
386 Formula indexForm = (Formula) s;
387 indexFormulaTemplates[i] = indexForm.getTemplate(dialect, factory.getSqlFunctionRegistry());
388 indexFormulas[i] = indexForm.getFormula();
389 hasFormula = true;
390 }
391 else {
392 Column indexCol = (Column) s;
393 indexColumnNames[i] = indexCol.getQuotedName(dialect);
394 indexColumnIsSettable[i] = true;
395 }
396 i++;
397 }
398 indexContainsFormula = hasFormula;
399 baseIndex = indexedCollection.isList() ?
400 ( (List) indexedCollection ).getBaseIndex() : 0;
401
402 indexNodeName = indexedCollection.getIndexNodeName();
403
404 }
405 else {
406 indexContainsFormula = false;
407 indexColumnIsSettable = null;
408 indexFormulaTemplates = null;
409 indexFormulas = null;
410 indexType = null;
411 indexColumnNames = null;
412 indexColumnAliases = null;
413 baseIndex = 0;
414 indexNodeName = null;
415 }
416
417 hasIdentifier = collection.isIdentified();
418 if (hasIdentifier) {
419 if ( collection.isOneToMany() ) {
420 throw new MappingException( "one-to-many collections with identifiers are not supported" );
421 }
422 IdentifierCollection idColl = (IdentifierCollection) collection;
423 identifierType = idColl.getIdentifier().getType();
424 iter = idColl.getIdentifier().getColumnIterator();
425 Column col = ( Column ) iter.next();
426 identifierColumnName = col.getQuotedName(dialect);
427 identifierColumnAlias = col.getAlias(dialect);
428 //unquotedIdentifierColumnName = identifierColumnAlias;
429 identifierGenerator = idColl.getIdentifier().createIdentifierGenerator(
430 factory.getDialect(),
431 factory.getSettings().getDefaultCatalogName(),
432 factory.getSettings().getDefaultSchemaName(),
433 null
434 );
435 }
436 else {
437 identifierType = null;
438 identifierColumnName = null;
439 identifierColumnAlias = null;
440 //unquotedIdentifierColumnName = null;
441 identifierGenerator = null;
442 }
443
444 //GENERATE THE SQL:
445
446 //sqlSelectString = sqlSelectString();
447 //sqlSelectRowString = sqlSelectRowString();
448
449 if ( collection.getCustomSQLInsert() == null ) {
450 sqlInsertRowString = generateInsertRowString();
451 insertCallable = false;
452 insertCheckStyle = ExecuteUpdateResultCheckStyle.COUNT;
453 }
454 else {
455 sqlInsertRowString = collection.getCustomSQLInsert();
456 insertCallable = collection.isCustomInsertCallable();
457 insertCheckStyle = collection.getCustomSQLInsertCheckStyle() == null
458 ? ExecuteUpdateResultCheckStyle.determineDefault( collection.getCustomSQLInsert(), insertCallable )
459 : collection.getCustomSQLInsertCheckStyle();
460 }
461
462 if ( collection.getCustomSQLUpdate() == null ) {
463 sqlUpdateRowString = generateUpdateRowString();
464 updateCallable = false;
465 updateCheckStyle = ExecuteUpdateResultCheckStyle.COUNT;
466 }
467 else {
468 sqlUpdateRowString = collection.getCustomSQLUpdate();
469 updateCallable = collection.isCustomUpdateCallable();
470 updateCheckStyle = collection.getCustomSQLUpdateCheckStyle() == null
471 ? ExecuteUpdateResultCheckStyle.determineDefault( collection.getCustomSQLUpdate(), insertCallable )
472 : collection.getCustomSQLUpdateCheckStyle();
473 }
474
475 if ( collection.getCustomSQLDelete() == null ) {
476 sqlDeleteRowString = generateDeleteRowString();
477 deleteCallable = false;
478 deleteCheckStyle = ExecuteUpdateResultCheckStyle.NONE;
479 }
480 else {
481 sqlDeleteRowString = collection.getCustomSQLDelete();
482 deleteCallable = collection.isCustomDeleteCallable();
483 deleteCheckStyle = ExecuteUpdateResultCheckStyle.NONE;
484 }
485
486 if ( collection.getCustomSQLDeleteAll() == null ) {
487 sqlDeleteString = generateDeleteString();
488 deleteAllCallable = false;
489 deleteAllCheckStyle = ExecuteUpdateResultCheckStyle.NONE;
490 }
491 else {
492 sqlDeleteString = collection.getCustomSQLDeleteAll();
493 deleteAllCallable = collection.isCustomDeleteAllCallable();
494 deleteAllCheckStyle = ExecuteUpdateResultCheckStyle.NONE;
495 }
496
497 sqlSelectSizeString = generateSelectSizeString( collection.isIndexed() && !collection.isMap() );
498 sqlDetectRowByIndexString = generateDetectRowByIndexString();
499 sqlDetectRowByElementString = generateDetectRowByElementString();
500 sqlSelectRowByIndexString = generateSelectRowByIndexString();
501
502 logStaticSQL();
503
504 isLazy = collection.isLazy();
505 isExtraLazy = collection.isExtraLazy();
506
507 isInverse = collection.isInverse();
508
509 if ( collection.isArray() ) {
510 elementClass = ( (org.hibernate.mapping.Array) collection ).getElementClass();
511 }
512 else {
513 // for non-arrays, we don't need to know the element class
514 elementClass = null; //elementType.returnedClass();
515 }
516
517 if ( elementType.isComponentType() ) {
518 elementPropertyMapping = new CompositeElementPropertyMapping(
519 elementColumnNames,
520 elementFormulaTemplates,
521 (AbstractComponentType) elementType,
522 factory
523 );
524 }
525 else if ( !elementType.isEntityType() ) {
526 elementPropertyMapping = new ElementPropertyMapping(
527 elementColumnNames,
528 elementType
529 );
530 }
531 else {
532 if ( elementPersister instanceof PropertyMapping ) { //not all classpersisters implement PropertyMapping!
533 elementPropertyMapping = (PropertyMapping) elementPersister;
534 }
535 else {
536 elementPropertyMapping = new ElementPropertyMapping(
537 elementColumnNames,
538 elementType
539 );
540 }
541 }
542
543 // Handle any filters applied to this collection
544 filterHelper = new FilterHelper( collection.getFilterMap(), dialect, factory.getSqlFunctionRegistry() );
545
546 // Handle any filters applied to this collection for many-to-many
547 manyToManyFilterHelper = new FilterHelper( collection.getManyToManyFilterMap(), dialect, factory.getSqlFunctionRegistry() );
548 manyToManyWhereString = StringHelper.isNotEmpty( collection.getManyToManyWhere() ) ?
549 "( " + collection.getManyToManyWhere() + ")" :
550 null;
551 manyToManyWhereTemplate = manyToManyWhereString == null ?
552 null :
553 Template.renderWhereStringTemplate( manyToManyWhereString, factory.getDialect(), factory.getSqlFunctionRegistry() );
554 manyToManyOrderByString = collection.getManyToManyOrdering();
555 manyToManyOrderByTemplate = manyToManyOrderByString == null
556 ? null
557 : Template.renderOrderByStringTemplate( manyToManyOrderByString, factory.getDialect(), factory.getSqlFunctionRegistry() );
558
559 initCollectionPropertyMap();
560 }
561
562 public void postInstantiate() throws MappingException {
563 initializer = queryLoaderName == null ?
564 createCollectionInitializer( CollectionHelper.EMPTY_MAP ) :
565 new NamedQueryCollectionInitializer( queryLoaderName, this );
566 }
567
568 protected void logStaticSQL() {
569 if ( log.isDebugEnabled() ) {
570 log.debug( "Static SQL for collection: " + getRole() );
571 if ( getSQLInsertRowString() != null ) {
572 log.debug( " Row insert: " + getSQLInsertRowString() );
573 }
574 if ( getSQLUpdateRowString() != null ) {
575 log.debug( " Row update: " + getSQLUpdateRowString() );
576 }
577 if ( getSQLDeleteRowString() != null ) {
578 log.debug( " Row delete: " + getSQLDeleteRowString() );
579 }
580 if ( getSQLDeleteString() != null ) {
581 log.debug( " One-shot delete: " + getSQLDeleteString() );
582 }
583 }
584 }
585
586 public void initialize(Serializable key, SessionImplementor session) throws HibernateException {
587 getAppropriateInitializer( key, session ).initialize( key, session );
588 }
589
590 protected CollectionInitializer getAppropriateInitializer(Serializable key, SessionImplementor session) {
591 if ( queryLoaderName != null ) {
592 //if there is a user-specified loader, return that
593 //TODO: filters!?
594 return initializer;
595 }
596 CollectionInitializer subselectInitializer = getSubselectInitializer( key, session );
597 if ( subselectInitializer != null ) {
598 return subselectInitializer;
599 }
600 else if ( session.getEnabledFilters().isEmpty() ) {
601 return initializer;
602 }
603 else {
604 return createCollectionInitializer( session.getEnabledFilters() );
605 }
606 }
607
608 private CollectionInitializer getSubselectInitializer(Serializable key, SessionImplementor session) {
609
610 if ( !isSubselectLoadable() ) {
611 return null;
612 }
613
614 final PersistenceContext persistenceContext = session.getPersistenceContext();
615
616 SubselectFetch subselect = persistenceContext.getBatchFetchQueue()
617 .getSubselect( new EntityKey( key, getOwnerEntityPersister(), session.getEntityMode() ) );
618
619 if (subselect == null) {
620 return null;
621 }
622 else {
623
624 // Take care of any entities that might have
625 // been evicted!
626 Iterator iter = subselect.getResult().iterator();
627 while ( iter.hasNext() ) {
628 if ( !persistenceContext.containsEntity( (EntityKey) iter.next() ) ) {
629 iter.remove();
630 }
631 }
632
633 // Run a subquery loader
634 return createSubselectInitializer( subselect, session );
635 }
636 }
637
638 protected abstract CollectionInitializer createSubselectInitializer(SubselectFetch subselect, SessionImplementor session);
639
640 protected abstract CollectionInitializer createCollectionInitializer(Map enabledFilters)
641 throws MappingException;
642
643 public CollectionRegionAccessStrategy getCacheAccessStrategy() {
644 return cacheAccessStrategy;
645 }
646
647 public boolean hasCache() {
648 return cacheAccessStrategy != null;
649 }
650
651 public CollectionType getCollectionType() {
652 return collectionType;
653 }
654
655 protected String getSQLWhereString(String alias) {
656 return StringHelper.replace( sqlWhereStringTemplate, Template.TEMPLATE, alias );
657 }
658
659 public String getSQLOrderByString(String alias) {
660 return hasOrdering() ?
661 StringHelper.replace( sqlOrderByStringTemplate, Template.TEMPLATE, alias ) : "";
662 }
663
664 public String getManyToManyOrderByString(String alias) {
665 if ( isManyToMany() && manyToManyOrderByString != null ) {
666 return StringHelper.replace( manyToManyOrderByTemplate, Template.TEMPLATE, alias );
667 }
668 else {
669 return "";
670 }
671 }
672 public FetchMode getFetchMode() {
673 return fetchMode;
674 }
675
676 public boolean hasOrdering() {
677 return hasOrder;
678 }
679
680 public boolean hasManyToManyOrdering() {
681 return isManyToMany() && manyToManyOrderByTemplate != null;
682 }
683
684 public boolean hasWhere() {
685 return hasWhere;
686 }
687
688 protected String getSQLDeleteString() {
689 return sqlDeleteString;
690 }
691
692 protected String getSQLInsertRowString() {
693 return sqlInsertRowString;
694 }
695
696 protected String getSQLUpdateRowString() {
697 return sqlUpdateRowString;
698 }
699
700 protected String getSQLDeleteRowString() {
701 return sqlDeleteRowString;
702 }
703
704 public Type getKeyType() {
705 return keyType;
706 }
707
708 public Type getIndexType() {
709 return indexType;
710 }
711
712 public Type getElementType() {
713 return elementType;
714 }
715
716 /**
717 * Return the element class of an array, or null otherwise
718 */
719 public Class getElementClass() { //needed by arrays
720 return elementClass;
721 }
722
723 public Object readElement(ResultSet rs, Object owner, String[] aliases, SessionImplementor session)
724 throws HibernateException, SQLException {
725 return getElementType().nullSafeGet( rs, aliases, session, owner );
726 }
727
728 public Object readIndex(ResultSet rs, String[] aliases, SessionImplementor session)
729 throws HibernateException, SQLException {
730 Object index = getIndexType().nullSafeGet( rs, aliases, session, null );
731 if ( index == null ) {
732 throw new HibernateException( "null index column for collection: " + role );
733 }
734 index = decrementIndexByBase( index );
735 return index;
736 }
737
738 protected Object decrementIndexByBase(Object index) {
739 if (baseIndex!=0) {
740 index = new Integer( ( (Integer) index ).intValue() - baseIndex );
741 }
742 return index;
743 }
744
745 public Object readIdentifier(ResultSet rs, String alias, SessionImplementor session)
746 throws HibernateException, SQLException {
747 Object id = getIdentifierType().nullSafeGet( rs, alias, session, null );
748 if ( id == null ) {
749 throw new HibernateException( "null identifier column for collection: " + role );
750 }
751 return id;
752 }
753
754 public Object readKey(ResultSet rs, String[] aliases, SessionImplementor session)
755 throws HibernateException, SQLException {
756 return getKeyType().nullSafeGet( rs, aliases, session, null );
757 }
758
759 /**
760 * Write the key to a JDBC <tt>PreparedStatement</tt>
761 */
762 protected int writeKey(PreparedStatement st, Serializable key, int i, SessionImplementor session)
763 throws HibernateException, SQLException {
764
765 if ( key == null ) {
766 throw new NullPointerException( "null key for collection: " + role ); //an assertion
767 }
768 getKeyType().nullSafeSet( st, key, i, session );
769 return i + keyColumnAliases.length;
770 }
771
772 /**
773 * Write the element to a JDBC <tt>PreparedStatement</tt>
774 */
775 protected int writeElement(PreparedStatement st, Object elt, int i, SessionImplementor session)
776 throws HibernateException, SQLException {
777 getElementType().nullSafeSet(st, elt, i, elementColumnIsSettable, session);
778 return i + ArrayHelper.countTrue(elementColumnIsSettable);
779
780 }
781
782 /**
783 * Write the index to a JDBC <tt>PreparedStatement</tt>
784 */
785 protected int writeIndex(PreparedStatement st, Object index, int i, SessionImplementor session)
786 throws HibernateException, SQLException {
787 getIndexType().nullSafeSet( st, incrementIndexByBase(index), i, indexColumnIsSettable, session );
788 return i + ArrayHelper.countTrue(indexColumnIsSettable);
789 }
790
791 protected Object incrementIndexByBase(Object index) {
792 if (baseIndex!=0) {
793 index = new Integer( ( (Integer) index ).intValue() + baseIndex );
794 }
795 return index;
796 }
797
798 /**
799 * Write the element to a JDBC <tt>PreparedStatement</tt>
800 */
801 protected int writeElementToWhere(PreparedStatement st, Object elt, int i, SessionImplementor session)
802 throws HibernateException, SQLException {
803 if (elementIsPureFormula) {
804 throw new AssertionFailure("cannot use a formula-based element in the where condition");
805 }
806 getElementType().nullSafeSet(st, elt, i, elementColumnIsInPrimaryKey, session);
807 return i + elementColumnAliases.length;
808
809 }
810
811 /**
812 * Write the index to a JDBC <tt>PreparedStatement</tt>
813 */
814 protected int writeIndexToWhere(PreparedStatement st, Object index, int i, SessionImplementor session)
815 throws HibernateException, SQLException {
816 if (indexContainsFormula) {
817 throw new AssertionFailure("cannot use a formula-based index in the where condition");
818 }
819 getIndexType().nullSafeSet( st, incrementIndexByBase(index), i, session );
820 return i + indexColumnAliases.length;
821 }
822
823 /**
824 * Write the identifier to a JDBC <tt>PreparedStatement</tt>
825 */
826 public int writeIdentifier(PreparedStatement st, Object id, int i, SessionImplementor session)
827 throws HibernateException, SQLException {
828
829 getIdentifierType().nullSafeSet( st, id, i, session );
830 return i + 1;
831 }
832
833 public boolean isPrimitiveArray() {
834 return isPrimitiveArray;
835 }
836
837 public boolean isArray() {
838 return isArray;
839 }
840
841 public String[] getKeyColumnAliases(String suffix) {
842 return new Alias( suffix ).toAliasStrings( keyColumnAliases );
843 }
844
845 public String[] getElementColumnAliases(String suffix) {
846 return new Alias( suffix ).toAliasStrings( elementColumnAliases );
847 }
848
849 public String[] getIndexColumnAliases(String suffix) {
850 if ( hasIndex ) {
851 return new Alias( suffix ).toAliasStrings( indexColumnAliases );
852 }
853 else {
854 return null;
855 }
856 }
857
858 public String getIdentifierColumnAlias(String suffix) {
859 if ( hasIdentifier ) {
860 return new Alias( suffix ).toAliasString( identifierColumnAlias );
861 }
862 else {
863 return null;
864 }
865 }
866
867 public String getIdentifierColumnName() {
868 if ( hasIdentifier ) {
869 return identifierColumnName;
870 } else {
871 return null;
872 }
873 }
874
875 /**
876 * Generate a list of collection index, key and element columns
877 */
878 public String selectFragment(String alias, String columnSuffix) {
879 SelectFragment frag = generateSelectFragment( alias, columnSuffix );
880 appendElementColumns( frag, alias );
881 appendIndexColumns( frag, alias );
882 appendIdentifierColumns( frag, alias );
883
884 return frag.toFragmentString()
885 .substring( 2 ); //strip leading ','
886 }
887
888 protected String generateSelectSizeString(boolean isIntegerIndexed) {
889 String selectValue = isIntegerIndexed ?
890 "max(" + getIndexColumnNames()[0] + ") + 1": //lists, arrays
891 "count(" + getElementColumnNames()[0] + ")"; //sets, maps, bags
892 return new SimpleSelect(dialect)
893 .setTableName( getTableName() )
894 .addCondition( getKeyColumnNames(), "=?" )
895 .addColumn(selectValue)
896 .toStatementString();
897 }
898
899 protected String generateDetectRowByIndexString() {
900 if ( !hasIndex() ) {
901 return null;
902 }
903 return new SimpleSelect(dialect)
904 .setTableName( getTableName() )
905 .addCondition( getKeyColumnNames(), "=?" )
906 .addCondition( getIndexColumnNames(), "=?" )
907 .addCondition( indexFormulas, "=?" )
908 .addColumn("1")
909 .toStatementString();
910 }
911
912 protected String generateSelectRowByIndexString() {
913 if ( !hasIndex() ) {
914 return null;
915 }
916 return new SimpleSelect(dialect)
917 .setTableName( getTableName() )
918 .addCondition( getKeyColumnNames(), "=?" )
919 .addCondition( getIndexColumnNames(), "=?" )
920 .addCondition( indexFormulas, "=?" )
921 .addColumns( getElementColumnNames(), elementColumnAliases )
922 .addColumns( indexFormulas, indexColumnAliases )
923 .toStatementString();
924 }
925
926 protected String generateDetectRowByElementString() {
927 return new SimpleSelect(dialect)
928 .setTableName( getTableName() )
929 .addCondition( getKeyColumnNames(), "=?" )
930 .addCondition( getElementColumnNames(), "=?" )
931 .addCondition( elementFormulas, "=?" )
932 .addColumn("1")
933 .toStatementString();
934 }
935
936 protected SelectFragment generateSelectFragment(String alias, String columnSuffix) {
937 return new SelectFragment()
938 .setSuffix( columnSuffix )
939 .addColumns( alias, keyColumnNames, keyColumnAliases );
940 }
941
942 protected void appendElementColumns(SelectFragment frag, String elemAlias) {
943 for ( int i=0; i<elementColumnIsSettable.length; i++ ) {
944 if ( elementColumnIsSettable[i] ) {
945 frag.addColumn( elemAlias, elementColumnNames[i], elementColumnAliases[i] );
946 }
947 else {
948 frag.addFormula( elemAlias, elementFormulaTemplates[i], elementColumnAliases[i] );
949 }
950 }
951 }
952
953 protected void appendIndexColumns(SelectFragment frag, String alias) {
954 if ( hasIndex ) {
955 for ( int i=0; i<indexColumnIsSettable.length; i++ ) {
956 if ( indexColumnIsSettable[i] ) {
957 frag.addColumn( alias, indexColumnNames[i], indexColumnAliases[i] );
958 }
959 else {
960 frag.addFormula( alias, indexFormulaTemplates[i], indexColumnAliases[i] );
961 }
962 }
963 }
964 }
965
966 protected void appendIdentifierColumns(SelectFragment frag, String alias) {
967 if ( hasIdentifier ) {
968 frag.addColumn( alias, identifierColumnName, identifierColumnAlias );
969 }
970 }
971
972 public String[] getIndexColumnNames() {
973 return indexColumnNames;
974 }
975
976 public String[] getIndexFormulas() {
977 return indexFormulas;
978 }
979
980 public String[] getIndexColumnNames(String alias) {
981 return qualify(alias, indexColumnNames, indexFormulaTemplates);
982
983 }
984
985 public String[] getElementColumnNames(String alias) {
986 return qualify(alias, elementColumnNames, elementFormulaTemplates);
987 }
988
989 private static String[] qualify(String alias, String[] columnNames, String[] formulaTemplates) {
990 int span = columnNames.length;
991 String[] result = new String[span];
992 for (int i=0; i<span; i++) {
993 if ( columnNames[i]==null ) {
994 result[i] = StringHelper.replace( formulaTemplates[i], Template.TEMPLATE, alias );
995 }
996 else {
997 result[i] = StringHelper.qualify( alias, columnNames[i] );
998 }
999 }
1000 return result;
1001 }
1002
1003 public String[] getElementColumnNames() {
1004 return elementColumnNames; //TODO: something with formulas...
1005 }
1006
1007 public String[] getKeyColumnNames() {
1008 return keyColumnNames;
1009 }
1010
1011 public boolean hasIndex() {
1012 return hasIndex;
1013 }
1014
1015 public boolean isLazy() {
1016 return isLazy;
1017 }
1018
1019 public boolean isInverse() {
1020 return isInverse;
1021 }
1022
1023 public String getTableName() {
1024 return qualifiedTableName;
1025 }
1026
1027 public void remove(Serializable id, SessionImplementor session) throws HibernateException {
1028
1029 if ( !isInverse && isRowDeleteEnabled() ) {
1030
1031 if ( log.isDebugEnabled() ) {
1032 log.debug(
1033 "Deleting collection: " +
1034 MessageHelper.collectionInfoString( this, id, getFactory() )
1035 );
1036 }
1037
1038 // Remove all the old entries
1039
1040 try {
1041 int offset = 1;
1042 PreparedStatement st = null;
1043 Expectation expectation = Expectations.appropriateExpectation( getDeleteAllCheckStyle() );
1044 boolean callable = isDeleteAllCallable();
1045 boolean useBatch = expectation.canBeBatched();
1046 String sql = getSQLDeleteString();
1047 if ( useBatch ) {
1048 if ( callable ) {
1049 st = session.getBatcher().prepareBatchCallableStatement( sql );
1050 }
1051 else {
1052 st = session.getBatcher().prepareBatchStatement( sql );
1053 }
1054 }
1055 else {
1056 if ( callable ) {
1057 st = session.getBatcher().prepareCallableStatement( sql );
1058 }
1059 else {
1060 st = session.getBatcher().prepareStatement( sql );
1061 }
1062 }
1063
1064
1065 try {
1066 offset+= expectation.prepare( st );
1067
1068 writeKey( st, id, offset, session );
1069 if ( useBatch ) {
1070 session.getBatcher().addToBatch( expectation );
1071 }
1072 else {
1073 expectation.verifyOutcome( st.executeUpdate(), st, -1 );
1074 }
1075 }
1076 catch ( SQLException sqle ) {
1077 if ( useBatch ) {
1078 session.getBatcher().abortBatch( sqle );
1079 }
1080 throw sqle;
1081 }
1082 finally {
1083 if ( !useBatch ) {
1084 session.getBatcher().closeStatement( st );
1085 }
1086 }
1087
1088 if ( log.isDebugEnabled() ) {
1089 log.debug( "done deleting collection" );
1090 }
1091 }
1092 catch ( SQLException sqle ) {
1093 throw JDBCExceptionHelper.convert(
1094 sqlExceptionConverter,
1095 sqle,
1096 "could not delete collection: " +
1097 MessageHelper.collectionInfoString( this, id, getFactory() ),
1098 getSQLDeleteString()
1099 );
1100 }
1101
1102 }
1103
1104 }
1105
1106 public void recreate(PersistentCollection collection, Serializable id, SessionImplementor session)
1107 throws HibernateException {
1108
1109 if ( !isInverse && isRowInsertEnabled() ) {
1110
1111 if ( log.isDebugEnabled() ) {
1112 log.debug(
1113 "Inserting collection: " +
1114 MessageHelper.collectionInfoString( this, id, getFactory() )
1115 );
1116 }
1117
1118 try {
1119 //create all the new entries
1120 Iterator entries = collection.entries(this);
1121 if ( entries.hasNext() ) {
1122 collection.preInsert( this );
1123 int i = 0;
1124 int count = 0;
1125 while ( entries.hasNext() ) {
1126
1127 final Object entry = entries.next();
1128 if ( collection.entryExists( entry, i ) ) {
1129 int offset = 1;
1130 PreparedStatement st = null;
1131 Expectation expectation = Expectations.appropriateExpectation( getInsertCheckStyle() );
1132 boolean callable = isInsertCallable();
1133 boolean useBatch = expectation.canBeBatched();
1134 String sql = getSQLInsertRowString();
1135
1136 if ( useBatch ) {
1137 if ( callable ) {
1138 st = session.getBatcher().prepareBatchCallableStatement( sql );
1139 }
1140 else {
1141 st = session.getBatcher().prepareBatchStatement( sql );
1142 }
1143 }
1144 else {
1145 if ( callable ) {
1146 st = session.getBatcher().prepareCallableStatement( sql );
1147 }
1148 else {
1149 st = session.getBatcher().prepareStatement( sql );
1150 }
1151 }
1152
1153
1154 try {
1155 offset+= expectation.prepare( st );
1156
1157 //TODO: copy/paste from insertRows()
1158 int loc = writeKey( st, id, offset, session );
1159 if ( hasIdentifier ) {
1160 loc = writeIdentifier( st, collection.getIdentifier(entry, i), loc, session );
1161 }
1162 if ( hasIndex /*&& !indexIsFormula*/ ) {
1163 loc = writeIndex( st, collection.getIndex(entry, i, this), loc, session );
1164 }
1165 loc = writeElement(st, collection.getElement(entry), loc, session );
1166
1167 if ( useBatch ) {
1168 session.getBatcher().addToBatch( expectation );
1169 }
1170 else {
1171 expectation.verifyOutcome( st.executeUpdate(), st, -1 );
1172 }
1173
1174 collection.afterRowInsert( this, entry, i );
1175 count++;
1176 }
1177 catch ( SQLException sqle ) {
1178 if ( useBatch ) {
1179 session.getBatcher().abortBatch( sqle );
1180 }
1181 throw sqle;
1182 }
1183 finally {
1184 if ( !useBatch ) {
1185 session.getBatcher().closeStatement( st );
1186 }
1187 }
1188
1189 }
1190 i++;
1191 }
1192
1193 if ( log.isDebugEnabled() ) {
1194 log.debug( "done inserting collection: " + count + " rows inserted" );
1195 }
1196
1197 }
1198 else {
1199 if ( log.isDebugEnabled() ) {
1200 log.debug( "collection was empty" );
1201 }
1202 }
1203 }
1204 catch ( SQLException sqle ) {
1205 throw JDBCExceptionHelper.convert(
1206 sqlExceptionConverter,
1207 sqle,
1208 "could not insert collection: " +
1209 MessageHelper.collectionInfoString( this, id, getFactory() ),
1210 getSQLInsertRowString()
1211 );
1212 }
1213 }
1214 }
1215
1216 protected boolean isRowDeleteEnabled() {
1217 return true;
1218 }
1219
1220 public void deleteRows(PersistentCollection collection, Serializable id, SessionImplementor session)
1221 throws HibernateException {
1222
1223 if ( !isInverse && isRowDeleteEnabled() ) {
1224
1225 if ( log.isDebugEnabled() ) {
1226 log.debug(
1227 "Deleting rows of collection: " +
1228 MessageHelper.collectionInfoString( this, id, getFactory() )
1229 );
1230 }
1231
1232 boolean deleteByIndex = !isOneToMany() && hasIndex && !indexContainsFormula;
1233
1234 try {
1235 //delete all the deleted entries
1236 Iterator deletes = collection.getDeletes( this, !deleteByIndex );
1237 if ( deletes.hasNext() ) {
1238 int offset = 1;
1239 int count = 0;
1240 while ( deletes.hasNext() ) {
1241 PreparedStatement st = null;
1242 Expectation expectation = Expectations.appropriateExpectation( getDeleteCheckStyle() );
1243 boolean callable = isDeleteCallable();
1244 boolean useBatch = expectation.canBeBatched();
1245 String sql = getSQLDeleteRowString();
1246
1247 if ( useBatch ) {
1248 if ( callable ) {
1249 st = session.getBatcher().prepareBatchCallableStatement( sql );
1250 }
1251 else {
1252 st = session.getBatcher().prepareBatchStatement( sql );
1253 }
1254 }
1255 else {
1256 if ( callable ) {
1257 st = session.getBatcher().prepareCallableStatement( sql );
1258 }
1259 else {
1260 st = session.getBatcher().prepareStatement( sql );
1261 }
1262 }
1263
1264 try {
1265 expectation.prepare( st );
1266
1267 Object entry = deletes.next();
1268 int loc = offset;
1269 if ( hasIdentifier ) {
1270 writeIdentifier( st, entry, loc, session );
1271 }
1272 else {
1273 loc = writeKey( st, id, loc, session );
1274 if ( deleteByIndex ) {
1275 writeIndexToWhere( st, entry, loc, session );
1276 }
1277 else {
1278 writeElementToWhere( st, entry, loc, session );
1279 }
1280 }
1281
1282 if ( useBatch ) {
1283 session.getBatcher().addToBatch( expectation );
1284 }
1285 else {
1286 expectation.verifyOutcome( st.executeUpdate(), st, -1 );
1287 }
1288 count++;
1289 }
1290 catch ( SQLException sqle ) {
1291 if ( useBatch ) {
1292 session.getBatcher().abortBatch( sqle );
1293 }
1294 throw sqle;
1295 }
1296 finally {
1297 if ( !useBatch ) {
1298 session.getBatcher().closeStatement( st );
1299 }
1300 }
1301
1302 if ( log.isDebugEnabled() ) {
1303 log.debug( "done deleting collection rows: " + count + " deleted" );
1304 }
1305 }
1306 }
1307 else {
1308 if ( log.isDebugEnabled() ) {
1309 log.debug( "no rows to delete" );
1310 }
1311 }
1312 }
1313 catch ( SQLException sqle ) {
1314 throw JDBCExceptionHelper.convert(
1315 sqlExceptionConverter,
1316 sqle,
1317 "could not delete collection rows: " +
1318 MessageHelper.collectionInfoString( this, id, getFactory() ),
1319 getSQLDeleteRowString()
1320 );
1321 }
1322 }
1323 }
1324
1325 protected boolean isRowInsertEnabled() {
1326 return true;
1327 }
1328
1329 public void insertRows(PersistentCollection collection, Serializable id, SessionImplementor session)
1330 throws HibernateException {
1331
1332 if ( !isInverse && isRowInsertEnabled() ) {
1333
1334 if ( log.isDebugEnabled() ) {
1335 log.debug(
1336 "Inserting rows of collection: " +
1337 MessageHelper.collectionInfoString( this, id, getFactory() )
1338 );
1339 }
1340
1341 try {
1342 //insert all the new entries
1343 collection.preInsert( this );
1344 Iterator entries = collection.entries( this );
1345 Expectation expectation = Expectations.appropriateExpectation( getInsertCheckStyle() );
1346 boolean callable = isInsertCallable();
1347 boolean useBatch = expectation.canBeBatched();
1348 String sql = getSQLInsertRowString();
1349 int i = 0;
1350 int count = 0;
1351 while ( entries.hasNext() ) {
1352 int offset = 1;
1353 Object entry = entries.next();
1354 PreparedStatement st = null;
1355 if ( collection.needsInserting( entry, i, elementType ) ) {
1356
1357 if ( useBatch ) {
1358 if ( st == null ) {
1359 if ( callable ) {
1360 st = session.getBatcher().prepareBatchCallableStatement( sql );
1361 }
1362 else {
1363 st = session.getBatcher().prepareBatchStatement( sql );
1364 }
1365 }
1366 }
1367 else {
1368 if ( callable ) {
1369 st = session.getBatcher().prepareCallableStatement( sql );
1370 }
1371 else {
1372 st = session.getBatcher().prepareStatement( sql );
1373 }
1374 }
1375
1376 try {
1377 offset += expectation.prepare( st );
1378 //TODO: copy/paste from recreate()
1379 offset = writeKey( st, id, offset, session );
1380 if ( hasIdentifier ) {
1381 offset = writeIdentifier( st, collection.getIdentifier(entry, i), offset, session );
1382 }
1383 if ( hasIndex /*&& !indexIsFormula*/ ) {
1384 offset = writeIndex( st, collection.getIndex(entry, i, this), offset, session );
1385 }
1386 writeElement(st, collection.getElement(entry), offset, session );
1387
1388 if ( useBatch ) {
1389 session.getBatcher().addToBatch( expectation );
1390 }
1391 else {
1392 expectation.verifyOutcome( st.executeUpdate(), st, -1 );
1393 }
1394 collection.afterRowInsert( this, entry, i );
1395 count++;
1396 }
1397 catch ( SQLException sqle ) {
1398 if ( useBatch ) {
1399 session.getBatcher().abortBatch( sqle );
1400 }
1401 throw sqle;
1402 }
1403 finally {
1404 if ( !useBatch ) {
1405 session.getBatcher().closeStatement( st );
1406 }
1407 }
1408 }
1409 i++;
1410 }
1411 if ( log.isDebugEnabled() ) {
1412 log.debug( "done inserting rows: " + count + " inserted" );
1413 }
1414 }
1415 catch ( SQLException sqle ) {
1416 throw JDBCExceptionHelper.convert(
1417 sqlExceptionConverter,
1418 sqle,
1419 "could not insert collection rows: " +
1420 MessageHelper.collectionInfoString( this, id, getFactory() ),
1421 getSQLInsertRowString()
1422 );
1423 }
1424
1425 }
1426 }
1427
1428
1429 public String getRole() {
1430 return role;
1431 }
1432
1433 public String getOwnerEntityName() {
1434 return entityName;
1435 }
1436
1437 public EntityPersister getOwnerEntityPersister() {
1438 return ownerPersister;
1439 }
1440
1441 public IdentifierGenerator getIdentifierGenerator() {
1442 return identifierGenerator;
1443 }
1444
1445 public Type getIdentifierType() {
1446 return identifierType;
1447 }
1448
1449 public boolean hasOrphanDelete() {
1450 return hasOrphanDelete;
1451 }
1452
1453 public Type toType(String propertyName) throws QueryException {
1454 if ( "index".equals( propertyName ) ) {
1455 return indexType;
1456 }
1457 return elementPropertyMapping.toType( propertyName );
1458 }
1459
1460 public abstract boolean isManyToMany();
1461
1462 public String getManyToManyFilterFragment(String alias, Map enabledFilters) {
1463 StringBuffer buffer = new StringBuffer();
1464 manyToManyFilterHelper.render( buffer, alias, enabledFilters );
1465
1466 if ( manyToManyWhereString != null ) {
1467 buffer.append( " and " )
1468 .append( StringHelper.replace( manyToManyWhereTemplate, Template.TEMPLATE, alias ) );
1469 }
1470
1471 return buffer.toString();
1472 }
1473
1474 public String[] toColumns(String alias, String propertyName)
1475 throws QueryException {
1476
1477 if ( "index".equals( propertyName ) ) {
1478 if ( isManyToMany() ) {
1479 throw new QueryException( "index() function not supported for many-to-many association" );
1480 }
1481 return StringHelper.qualify( alias, indexColumnNames );
1482 }
1483
1484 return elementPropertyMapping.toColumns( alias, propertyName );
1485 }
1486
1487 public String[] toColumns(String propertyName)
1488 throws QueryException {
1489
1490 if ( "index".equals( propertyName ) ) {
1491 if ( isManyToMany() ) {
1492 throw new QueryException( "index() function not supported for many-to-many association" );
1493 }
1494 return indexColumnNames;
1495 }
1496
1497 return elementPropertyMapping.toColumns( propertyName );
1498 }
1499
1500 public Type getType() {
1501 return elementPropertyMapping.getType(); //==elementType ??
1502 }
1503
1504 public String getName() {
1505 return getRole();
1506 }
1507
1508 public EntityPersister getElementPersister() {
1509 if ( elementPersister == null ) {
1510 throw new AssertionFailure( "not an association" );
1511 }
1512 return ( Loadable ) elementPersister;
1513 }
1514
1515 public boolean isCollection() {
1516 return true;
1517 }
1518
1519 public Serializable[] getCollectionSpaces() {
1520 return spaces;
1521 }
1522
1523 protected abstract String generateDeleteString();
1524
1525 protected abstract String generateDeleteRowString();
1526
1527 protected abstract String generateUpdateRowString();
1528
1529 protected abstract String generateInsertRowString();
1530
1531 public void updateRows(PersistentCollection collection, Serializable id, SessionImplementor session)
1532 throws HibernateException {
1533
1534 if ( !isInverse && collection.isRowUpdatePossible() ) {
1535
1536 if ( log.isDebugEnabled() ) {
1537 log.debug( "Updating rows of collection: " + role + "#" + id );
1538 }
1539
1540 //update all the modified entries
1541 int count = doUpdateRows( id, collection, session );
1542
1543 if ( log.isDebugEnabled() ) {
1544 log.debug( "done updating rows: " + count + " updated" );
1545 }
1546 }
1547 }
1548
1549 protected abstract int doUpdateRows(Serializable key, PersistentCollection collection, SessionImplementor session)
1550 throws HibernateException;
1551
1552 public CollectionMetadata getCollectionMetadata() {
1553 return this;
1554 }
1555
1556 public SessionFactoryImplementor getFactory() {
1557 return factory;
1558 }
1559
1560 protected String filterFragment(String alias) throws MappingException {
1561 return hasWhere() ? " and " + getSQLWhereString( alias ) : "";
1562 }
1563
1564 public String filterFragment(String alias, Map enabledFilters) throws MappingException {
1565
1566 StringBuffer sessionFilterFragment = new StringBuffer();
1567 filterHelper.render( sessionFilterFragment, alias, enabledFilters );
1568
1569 return sessionFilterFragment.append( filterFragment( alias ) ).toString();
1570 }
1571
1572 public String oneToManyFilterFragment(String alias) throws MappingException {
1573 return "";
1574 }
1575
1576 protected boolean isInsertCallable() {
1577 return insertCallable;
1578 }
1579
1580 protected ExecuteUpdateResultCheckStyle getInsertCheckStyle() {
1581 return insertCheckStyle;
1582 }
1583
1584 protected boolean isUpdateCallable() {
1585 return updateCallable;
1586 }
1587
1588 protected ExecuteUpdateResultCheckStyle getUpdateCheckStyle() {
1589 return updateCheckStyle;
1590 }
1591
1592 protected boolean isDeleteCallable() {
1593 return deleteCallable;
1594 }
1595
1596 protected ExecuteUpdateResultCheckStyle getDeleteCheckStyle() {
1597 return deleteCheckStyle;
1598 }
1599
1600 protected boolean isDeleteAllCallable() {
1601 return deleteAllCallable;
1602 }
1603
1604 protected ExecuteUpdateResultCheckStyle getDeleteAllCheckStyle() {
1605 return deleteAllCheckStyle;
1606 }
1607
1608 public String toString() {
1609 return StringHelper.unqualify( getClass().getName() ) + '(' + role + ')';
1610 }
1611
1612 public boolean isVersioned() {
1613 return isVersioned && getOwnerEntityPersister().isVersioned();
1614 }
1615
1616 public String getNodeName() {
1617 return nodeName;
1618 }
1619
1620 public String getElementNodeName() {
1621 return elementNodeName;
1622 }
1623
1624 public String getIndexNodeName() {
1625 return indexNodeName;
1626 }
1627
1628 protected SQLExceptionConverter getSQLExceptionConverter() {
1629 return sqlExceptionConverter;
1630 }
1631
1632 public CacheEntryStructure getCacheEntryStructure() {
1633 return cacheEntryStructure;
1634 }
1635
1636 public boolean isAffectedByEnabledFilters(SessionImplementor session) {
1637 return filterHelper.isAffectedBy( session.getEnabledFilters() ) ||
1638 ( isManyToMany() && manyToManyFilterHelper.isAffectedBy( session.getEnabledFilters() ) );
1639 }
1640
1641 public boolean isSubselectLoadable() {
1642 return subselectLoadable;
1643 }
1644
1645 public boolean isMutable() {
1646 return isMutable;
1647 }
1648
1649 public String[] getCollectionPropertyColumnAliases(String propertyName, String suffix) {
1650 String rawAliases[] = (String[]) collectionPropertyColumnAliases.get(propertyName);
1651
1652 if ( rawAliases == null ) {
1653 return null;
1654 }
1655
1656 String result[] = new String[rawAliases.length];
1657 for ( int i=0; i<rawAliases.length; i++ ) {
1658 result[i] = new Alias(suffix).toUnquotedAliasString( rawAliases[i] );
1659 }
1660 return result;
1661 }
1662
1663 //TODO: formulas ?
1664 public void initCollectionPropertyMap() {
1665
1666 initCollectionPropertyMap( "key", keyType, keyColumnAliases, keyColumnNames );
1667 initCollectionPropertyMap( "element", elementType, elementColumnAliases, elementColumnNames );
1668 if (hasIndex) {
1669 initCollectionPropertyMap( "index", indexType, indexColumnAliases, indexColumnNames );
1670 }
1671 if (hasIdentifier) {
1672 initCollectionPropertyMap(
1673 "id",
1674 identifierType,
1675 new String[] { identifierColumnAlias },
1676 new String[] { identifierColumnName }
1677 );
1678 }
1679 }
1680
1681 private void initCollectionPropertyMap(String aliasName, Type type, String[] columnAliases, String[] columnNames) {
1682
1683 collectionPropertyColumnAliases.put(aliasName, columnAliases);
1684 collectionPropertyColumnNames.put(aliasName, columnNames);
1685
1686 if( type.isComponentType() ) {
1687 AbstractComponentType ct = (AbstractComponentType) type;
1688 String[] propertyNames = ct.getPropertyNames();
1689 for (int i = 0; i < propertyNames.length; i++) {
1690 String name = propertyNames[i];
1691 collectionPropertyColumnAliases.put( aliasName + "." + name, columnAliases[i] );
1692 collectionPropertyColumnNames.put( aliasName + "." + name, columnNames[i] );
1693 }
1694 }
1695
1696 }
1697
1698 public int getSize(Serializable key, SessionImplementor session) {
1699 try {
1700 PreparedStatement st = session.getBatcher().prepareSelectStatement(sqlSelectSizeString);
1701 try {
1702 getKeyType().nullSafeSet(st, key, 1, session);
1703 ResultSet rs = st.executeQuery();
1704 try {
1705 return rs.next() ? rs.getInt(1) - baseIndex : 0;
1706 }
1707 finally {
1708 rs.close();
1709 }
1710 }
1711 finally {
1712 session.getBatcher().closeStatement( st );
1713 }
1714 }
1715 catch (SQLException sqle) {
1716 throw JDBCExceptionHelper.convert(
1717 getFactory().getSQLExceptionConverter(),
1718 sqle,
1719 "could not retrieve collection size: " +
1720 MessageHelper.collectionInfoString( this, key, getFactory() ),
1721 sqlSelectSizeString
1722 );
1723 }
1724 }
1725
1726 public boolean indexExists(Serializable key, Object index, SessionImplementor session) {
1727 return exists(key, incrementIndexByBase(index), getIndexType(), sqlDetectRowByIndexString, session);
1728 }
1729
1730 public boolean elementExists(Serializable key, Object element, SessionImplementor session) {
1731 return exists(key, element, getElementType(), sqlDetectRowByElementString, session);
1732 }
1733
1734 private boolean exists(Serializable key, Object indexOrElement, Type indexOrElementType, String sql, SessionImplementor session) {
1735 try {
1736 PreparedStatement st = session.getBatcher().prepareSelectStatement(sql);
1737 try {
1738 getKeyType().nullSafeSet(st, key, 1, session);
1739 indexOrElementType.nullSafeSet( st, indexOrElement, keyColumnNames.length + 1, session );
1740 ResultSet rs = st.executeQuery();
1741 try {
1742 return rs.next();
1743 }
1744 finally {
1745 rs.close();
1746 }
1747 }
1748 catch( TransientObjectException e ) {
1749 return false;
1750 }
1751 finally {
1752 session.getBatcher().closeStatement( st );
1753 }
1754 }
1755 catch (SQLException sqle) {
1756 throw JDBCExceptionHelper.convert(
1757 getFactory().getSQLExceptionConverter(),
1758 sqle,
1759 "could not check row existence: " +
1760 MessageHelper.collectionInfoString( this, key, getFactory() ),
1761 sqlSelectSizeString
1762 );
1763 }
1764 }
1765
1766 public Object getElementByIndex(Serializable key, Object index, SessionImplementor session, Object owner) {
1767 try {
1768 PreparedStatement st = session.getBatcher().prepareSelectStatement(sqlSelectRowByIndexString);
1769 try {
1770 getKeyType().nullSafeSet(st, key, 1, session);
1771 getIndexType().nullSafeSet( st, incrementIndexByBase(index), keyColumnNames.length + 1, session );
1772 ResultSet rs = st.executeQuery();
1773 try {
1774 if ( rs.next() ) {
1775 return getElementType().nullSafeGet(rs, elementColumnAliases, session, owner);
1776 }
1777 else {
1778 return null;
1779 }
1780 }
1781 finally {
1782 rs.close();
1783 }
1784 }
1785 finally {
1786 session.getBatcher().closeStatement( st );
1787 }
1788 }
1789 catch (SQLException sqle) {
1790 throw JDBCExceptionHelper.convert(
1791 getFactory().getSQLExceptionConverter(),
1792 sqle,
1793 "could not read row: " +
1794 MessageHelper.collectionInfoString( this, key, getFactory() ),
1795 sqlSelectSizeString
1796 );
1797 }
1798 }
1799
1800 public boolean isExtraLazy() {
1801 return isExtraLazy;
1802 }
1803
1804 protected Dialect getDialect() {
1805 return dialect;
1806 }
1807 }