| 1 package org.hibernate.persister.collection; 3 4 import java.io.Serializable ; 5 import java.sql.CallableStatement ; 6 import java.sql.PreparedStatement ; 7 import java.sql.ResultSet ; 8 import java.sql.SQLException ; 9 import java.sql.Types ; 10 import java.util.Arrays ; 11 import java.util.HashMap ; 12 import java.util.Iterator ; 13 import java.util.Map ; 14 15 import org.apache.commons.logging.Log; 16 import org.apache.commons.logging.LogFactory; 17 import org.hibernate.AssertionFailure; 18 import org.hibernate.FetchMode; 19 import org.hibernate.HibernateException; 20 import org.hibernate.MappingException; 21 import org.hibernate.QueryException; 22 import org.hibernate.cache.CacheConcurrencyStrategy; 23 import org.hibernate.cache.CacheException; 24 import org.hibernate.cache.entry.CacheEntryStructure; 25 import org.hibernate.cache.entry.StructuredCollectionCacheEntry; 26 import org.hibernate.cache.entry.StructuredMapCacheEntry; 27 import org.hibernate.cache.entry.UnstructuredCacheEntry; 28 import org.hibernate.cfg.Configuration; 29 import org.hibernate.collection.PersistentCollection; 30 import org.hibernate.dialect.Dialect; 31 import org.hibernate.engine.EntityKey; 32 import org.hibernate.engine.PersistenceContext; 33 import org.hibernate.engine.SessionFactoryImplementor; 34 import org.hibernate.engine.SessionImplementor; 35 import org.hibernate.engine.SubselectFetch; 36 import org.hibernate.exception.JDBCExceptionHelper; 37 import org.hibernate.exception.SQLExceptionConverter; 38 import org.hibernate.id.IdentifierGenerator; 39 import org.hibernate.loader.collection.CollectionInitializer; 40 import org.hibernate.mapping.Collection; 41 import org.hibernate.mapping.Column; 42 import org.hibernate.mapping.Formula; 43 import org.hibernate.mapping.IdentifierCollection; 44 import org.hibernate.mapping.IndexedCollection; 45 import org.hibernate.mapping.List; 46 import org.hibernate.mapping.Selectable; 47 import org.hibernate.mapping.Table; 48 import org.hibernate.metadata.CollectionMetadata; 49 import org.hibernate.persister.entity.EntityPersister; 50 import org.hibernate.persister.entity.Loadable; 51 import org.hibernate.persister.entity.PropertyMapping; 52 import org.hibernate.pretty.MessageHelper; 53 import org.hibernate.sql.Alias; 54 import org.hibernate.sql.SelectFragment; 55 import org.hibernate.sql.Template; 56 import org.hibernate.type.AbstractComponentType; 57 import org.hibernate.type.CollectionType; 58 import org.hibernate.type.EntityType; 59 import org.hibernate.type.Type; 60 import org.hibernate.util.ArrayHelper; 61 import org.hibernate.util.CollectionHelper; 62 import org.hibernate.util.FilterHelper; 63 import org.hibernate.util.StringHelper; 64 65 66 73 public abstract class AbstractCollectionPersister 74 implements CollectionMetadata, SQLLoadableCollection { 75 77 private final String role; 78 79 private final String sqlDeleteString; 81 private final String sqlInsertRowString; 82 private final String sqlUpdateRowString; 83 private final String sqlDeleteRowString; 84 85 private final String sqlOrderByString; 86 protected final String sqlWhereString; 87 private final String sqlOrderByStringTemplate; 88 private final String sqlWhereStringTemplate; 89 private final boolean hasOrder; 90 protected final boolean hasWhere; 91 private final int baseIndex; 92 93 private final String nodeName; 94 private final String elementNodeName; 95 private final String indexNodeName; 96 97 protected final boolean indexContainsFormula; 98 protected final boolean elementIsPureFormula; 99 100 private final Type keyType; 102 private final Type indexType; 103 protected final Type elementType; 104 private final Type identifierType; 105 106 protected final String [] keyColumnNames; 108 protected final String [] indexColumnNames; 109 protected final String [] indexFormulaTemplates; 110 protected final boolean[] indexColumnIsSettable; 111 protected final String [] elementColumnNames; 112 protected final String [] elementFormulaTemplates; 113 protected final boolean[] elementColumnIsSettable; 114 protected final boolean[] elementColumnIsInPrimaryKey; 115 protected final String [] indexColumnAliases; 116 protected final String [] elementColumnAliases; 117 protected final String [] keyColumnAliases; 118 119 protected final String identifierColumnName; 120 private final String identifierColumnAlias; 121 123 protected final String qualifiedTableName; 124 125 private final String queryLoaderName; 126 127 private final boolean isPrimitiveArray; 128 private final boolean isArray; 129 protected final boolean hasIndex; 130 protected final boolean hasIdentifier; 131 private final boolean isLazy; 132 private final boolean isInverse; 133 private final boolean isMutable; 134 private final boolean isVersioned; 135 protected final int batchSize; 136 private final FetchMode fetchMode; 137 private final boolean hasOrphanDelete; 138 private final boolean subselectLoadable; 139 140 private final Class elementClass; 142 private final String entityName; 143 144 private final Dialect dialect; 145 private final SQLExceptionConverter sqlExceptionConverter; 146 private final SessionFactoryImplementor factory; 147 private final EntityPersister ownerPersister; 148 private final IdentifierGenerator identifierGenerator; 149 private final PropertyMapping elementPropertyMapping; 150 private final EntityPersister elementPersister; 151 private final CacheConcurrencyStrategy cache; 152 private final CollectionType collectionType; 153 private CollectionInitializer initializer; 154 155 private final CacheEntryStructure cacheEntryStructure; 156 157 private final FilterHelper filterHelper; 159 160 private final FilterHelper manyToManyFilterHelper; 162 163 private final String manyToManyWhereString; 164 private final String manyToManyWhereTemplate; 165 166 private final boolean insertCallable; 168 private final boolean updateCallable; 169 private final boolean deleteCallable; 170 private final boolean deleteAllCallable; 171 172 private final Serializable [] spaces; 173 174 private Map collectionPropertyColumnAliases = new HashMap (); 175 private Map collectionPropertyColumnNames = new HashMap (); 176 177 private static final Log log = LogFactory.getLog( AbstractCollectionPersister.class ); 178 179 public AbstractCollectionPersister( 180 final Collection collection, 181 final CacheConcurrencyStrategy cache, 182 final Configuration cfg, 183 final SessionFactoryImplementor factory) 184 throws MappingException, CacheException { 185 186 this.factory = factory; 187 this.cache = cache; 188 if ( factory.getSettings().isStructuredCacheEntriesEnabled() ) { 189 cacheEntryStructure = collection.isMap() ? 190 (CacheEntryStructure) new StructuredMapCacheEntry() : 191 (CacheEntryStructure) new StructuredCollectionCacheEntry(); 192 } 193 else { 194 cacheEntryStructure = new UnstructuredCacheEntry(); 195 } 196 197 dialect = factory.getDialect(); 198 sqlExceptionConverter = factory.getSQLExceptionConverter(); 199 collectionType = collection.getCollectionType(); 200 role = collection.getRole(); 201 entityName = collection.getOwnerEntityName(); 202 ownerPersister = factory.getEntityPersister(entityName); 203 queryLoaderName = collection.getLoaderName(); 204 nodeName = collection.getNodeName(); 205 isMutable = collection.isMutable(); 206 207 Table table = collection.getCollectionTable(); 208 fetchMode = collection.getElement().getFetchMode(); 209 elementType = collection.getElement().getType(); 210 isPrimitiveArray = collection.isPrimitiveArray(); 213 isArray = collection.isArray(); 214 subselectLoadable = collection.isSubselectLoadable(); 215 216 qualifiedTableName = table.getQualifiedName( 217 dialect, 218 factory.getSettings().getDefaultCatalogName(), 219 factory.getSettings().getDefaultSchemaName() 220 ); 221 222 int spacesSize = 1 + collection.getSynchronizedTables().size(); 223 spaces = new String [spacesSize]; 224 spaces[0] = qualifiedTableName; 225 Iterator iter = collection.getSynchronizedTables().iterator(); 226 for ( int i = 1; i < spacesSize; i++ ) { 227 spaces[i] = (String ) iter.next(); 228 } 229 230 sqlOrderByString = collection.getOrderBy(); 231 hasOrder = sqlOrderByString != null; 232 sqlOrderByStringTemplate = hasOrder ? 233 Template.renderOrderByStringTemplate(sqlOrderByString, dialect) : 234 null; 235 sqlWhereString = collection.getWhere(); 236 hasWhere = sqlWhereString != null; 237 sqlWhereStringTemplate = hasWhere ? 238 Template.renderWhereStringTemplate(sqlWhereString, dialect) : 239 null; 240 241 hasOrphanDelete = collection.hasOrphanDelete(); 242 243 int batch = collection.getBatchSize(); 244 if (batch==-1) batch = factory.getSettings().getDefaultBatchFetchSize(); 245 batchSize = batch; 246 247 isVersioned = collection.isOptimisticLocked(); 248 249 251 keyType = collection.getKey().getType(); 252 iter = collection.getKey().getColumnIterator(); 253 int keySpan = collection.getKey().getColumnSpan(); 254 keyColumnNames = new String [keySpan]; 255 keyColumnAliases = new String [keySpan]; 256 int k = 0; 257 while ( iter.hasNext() ) { 258 Column col = ( (Column) iter.next() ); 260 keyColumnNames[k] = col.getQuotedName(dialect); 261 keyColumnAliases[k] = col.getAlias(dialect); 262 k++; 263 } 264 265 267 269 String elemNode = collection.getElementNodeName(); 270 if ( elementType.isEntityType() ) { 271 String entityName = ( (EntityType) elementType ).getAssociatedEntityName(); 272 elementPersister = factory.getEntityPersister(entityName); 273 if ( elemNode==null ) { 274 elemNode = cfg.getClassMapping(entityName).getNodeName(); 275 } 276 278 } 279 else { 280 elementPersister = null; 281 } 282 elementNodeName = elemNode; 283 284 int elementSpan = collection.getElement().getColumnSpan(); 285 elementColumnAliases = new String [elementSpan]; 286 elementColumnNames = new String [elementSpan]; 287 elementFormulaTemplates = new String [elementSpan]; 288 elementColumnIsSettable = new boolean[elementSpan]; 289 elementColumnIsInPrimaryKey = new boolean[elementSpan]; 290 boolean isPureFormula = true; 291 boolean hasNotNullableColumns = false; 292 int j = 0; 293 iter = collection.getElement().getColumnIterator(); 294 while ( iter.hasNext() ) { 295 Selectable selectable = (Selectable) iter.next(); 296 elementColumnAliases[j] = selectable.getAlias(dialect); 297 if ( selectable.isFormula() ) { 298 Formula form = (Formula) selectable; 299 elementFormulaTemplates[j] = form.getTemplate(dialect); 300 } 301 else { 302 Column col = (Column) selectable; 303 elementColumnNames[j] = col.getQuotedName(dialect); 304 elementColumnIsSettable[j] = true; 305 elementColumnIsInPrimaryKey[j] = !col.isNullable(); 306 if ( !col.isNullable() ) hasNotNullableColumns = true; 307 isPureFormula = false; 308 } 309 j++; 310 } 311 elementIsPureFormula = isPureFormula; 312 313 if ( !hasNotNullableColumns ) Arrays.fill(elementColumnIsInPrimaryKey, true); 317 318 319 321 hasIndex = collection.isIndexed(); 322 if (hasIndex) { 323 IndexedCollection indexedCollection = (IndexedCollection) collection; 325 indexType = indexedCollection.getIndex().getType(); 326 int indexSpan = indexedCollection.getIndex().getColumnSpan(); 327 iter = indexedCollection.getIndex().getColumnIterator(); 328 indexColumnNames = new String [indexSpan]; 329 indexFormulaTemplates = new String [indexSpan]; 330 indexColumnIsSettable = new boolean[indexSpan]; 331 indexColumnAliases = new String [indexSpan]; 332 int i = 0; 333 boolean hasFormula = false; 334 while ( iter.hasNext() ) { 335 Selectable s = (Selectable) iter.next(); 336 indexColumnAliases[i] = s.getAlias(dialect); 337 if ( s.isFormula() ) { 338 Formula indexForm = (Formula) s; 339 indexFormulaTemplates[i] = indexForm.getTemplate(dialect); 340 hasFormula = true; 341 } 342 else { 343 Column indexCol = (Column) s; 344 indexColumnNames[i] = indexCol.getQuotedName(dialect); 345 indexColumnIsSettable[i] = true; 346 } 347 i++; 348 } 349 indexContainsFormula = hasFormula; 350 baseIndex = indexedCollection.isList() ? 351 ( (List) indexedCollection ).getBaseIndex() : 0; 352 353 indexNodeName = indexedCollection.getIndexNodeName(); 354 355 } 356 else { 357 indexContainsFormula = false; 358 indexColumnIsSettable = null; 359 indexFormulaTemplates = null; 360 indexType = null; 361 indexColumnNames = null; 362 indexColumnAliases = null; 363 baseIndex = 0; 364 indexNodeName = null; 365 } 366 367 hasIdentifier = collection.isIdentified(); 368 if (hasIdentifier) { 369 if ( collection.isOneToMany() ) { 370 throw new MappingException( "one-to-many collections with identifiers are not supported" ); 371 } 372 IdentifierCollection idColl = (IdentifierCollection) collection; 373 identifierType = idColl.getIdentifier().getType(); 374 iter = idColl.getIdentifier().getColumnIterator(); 375 Column col = ( Column ) iter.next(); 376 identifierColumnName = col.getQuotedName(dialect); 377 identifierColumnAlias = col.getAlias(dialect); 378 identifierGenerator = idColl.getIdentifier().createIdentifierGenerator( 380 factory.getDialect(), 381 factory.getSettings().getDefaultCatalogName(), 382 factory.getSettings().getDefaultSchemaName(), 383 null 384 ); 385 } 386 else { 387 identifierType = null; 388 identifierColumnName = null; 389 identifierColumnAlias = null; 390 identifierGenerator = null; 392 } 393 394 396 if ( collection.getCustomSQLDeleteAll() == null ) { 398 sqlDeleteString = generateDeleteString(); 399 deleteAllCallable = false; 400 } 401 else { 402 sqlDeleteString = collection.getCustomSQLDeleteAll(); 403 deleteAllCallable = collection.isCustomDeleteAllCallable(); 404 } 405 if ( collection.getCustomSQLInsert() == null ) { 407 sqlInsertRowString = generateInsertRowString(); 408 insertCallable = false; 409 } 410 else { 411 sqlInsertRowString = collection.getCustomSQLInsert(); 412 insertCallable = collection.isCustomInsertCallable(); 413 } 414 415 if ( collection.getCustomSQLUpdate() == null ) { 416 sqlUpdateRowString = generateUpdateRowString(); 417 updateCallable = false; 418 } 419 else { 420 sqlUpdateRowString = collection.getCustomSQLUpdate(); 421 updateCallable = collection.isCustomUpdateCallable(); 422 } 423 if ( collection.getCustomSQLDelete() == null ) { 424 sqlDeleteRowString = generateDeleteRowString(); 425 deleteCallable = false; 426 } 427 else { 428 sqlDeleteRowString = collection.getCustomSQLDelete(); 429 deleteCallable = collection.isCustomDeleteCallable(); 430 } 431 logStaticSQL(); 432 isLazy = collection.isLazy(); 433 434 isInverse = collection.isInverse(); 435 436 if ( collection.isArray() ) { 437 elementClass = ( (org.hibernate.mapping.Array) collection ).getElementClass(); 438 } 439 else { 440 elementClass = null; } 443 444 if ( elementType.isComponentType() ) { 445 elementPropertyMapping = new CompositeElementPropertyMapping( 446 elementColumnNames, 447 elementFormulaTemplates, 448 (AbstractComponentType) elementType, 449 factory 450 ); 451 } 452 else if ( !elementType.isEntityType() ) { 453 elementPropertyMapping = new ElementPropertyMapping( 454 elementColumnNames, 455 elementType 456 ); 457 } 458 else { 459 if ( elementPersister instanceof PropertyMapping ) { elementPropertyMapping = (PropertyMapping) elementPersister; 461 } 462 else { 463 elementPropertyMapping = new ElementPropertyMapping( 464 elementColumnNames, 465 elementType 466 ); 467 } 468 } 469 470 filterHelper = new FilterHelper( collection.getFilterMap(), dialect ); 472 473 manyToManyFilterHelper = new FilterHelper( collection.getManyToManyFilterMap(), dialect ); 475 manyToManyWhereString = collection.getManyToManyWhere(); 476 manyToManyWhereTemplate = manyToManyWhereString == null ? 477 null : 478 Template.renderWhereStringTemplate( manyToManyWhereString, factory.getDialect() ); 479 480 initCollectionPropertyMap(); 481 } 482 483 public void postInstantiate() throws MappingException { 484 initializer = queryLoaderName == null ? 485 createCollectionInitializer( CollectionHelper.EMPTY_MAP ) : 486 new NamedQueryCollectionInitializer( queryLoaderName, this ); 487 } 488 489 protected void logStaticSQL() { 490 if ( log.isDebugEnabled() ) { 491 log.debug( "Static SQL for collection: " + getRole() ); 492 if ( getSQLInsertRowString() != null ) log.debug( " Row insert: " + getSQLInsertRowString() ); 493 if ( getSQLUpdateRowString() != null ) log.debug( " Row update: " + getSQLUpdateRowString() ); 494 if ( getSQLDeleteRowString() != null ) log.debug( " Row delete: " + getSQLDeleteRowString() ); 495 if ( getSQLDeleteString() != null ) log.debug( " One-shot delete: " + getSQLDeleteString() ); 496 } 497 } 498 499 public void initialize(Serializable key, SessionImplementor session) throws HibernateException { 500 getAppropriateInitializer( key, session ).initialize( key, session ); 501 } 502 503 protected CollectionInitializer getAppropriateInitializer(Serializable key, SessionImplementor session) { 504 if ( queryLoaderName != null ) { 505 return initializer; 508 } 509 CollectionInitializer subselectInitializer = getSubselectInitializer( key, session ); 510 if ( subselectInitializer != null ) { 511 return subselectInitializer; 512 } 513 else if ( session.getEnabledFilters().isEmpty() ) { 514 return initializer; 515 } 516 else { 517 return createCollectionInitializer( session.getEnabledFilters() ); 518 } 519 } 520 521 private CollectionInitializer getSubselectInitializer(Serializable key, SessionImplementor session) { 522 523 if ( !isSubselectLoadable() ) return null; 524 525 final PersistenceContext persistenceContext = session.getPersistenceContext(); 526 527 SubselectFetch subselect = persistenceContext.getBatchFetchQueue() 528 .getSubselect( new EntityKey( key, getOwnerEntityPersister(), session.getEntityMode() ) ); 529 530 if (subselect == null) { 531 return null; 532 } 533 else { 534 535 Iterator iter = subselect.getResult().iterator(); 538 while ( iter.hasNext() ) { 539 if ( !persistenceContext.containsEntity( (EntityKey) iter.next() ) ) { 540 iter.remove(); 541 } 542 } 543 544 return createSubselectInitializer( subselect, session ); 546 } 547 } 548 549 protected abstract CollectionInitializer createSubselectInitializer(SubselectFetch subselect, SessionImplementor session); 550 551 protected abstract CollectionInitializer createCollectionInitializer(Map enabledFilters) 552 throws MappingException; 553 554 public CacheConcurrencyStrategy getCache() { 555 return cache; 556 } 557 558 public boolean hasCache() { 559 return cache != null; 560 } 561 562 public CollectionType getCollectionType() { 563 return collectionType; 564 } 565 566 protected String getSQLWhereString(String alias) { 567 return StringHelper.replace( sqlWhereStringTemplate, Template.TEMPLATE, alias ); 568 } 569 570 public String getSQLOrderByString(String alias) { 571 return hasOrdering() ? 572 StringHelper.replace( sqlOrderByStringTemplate, Template.TEMPLATE, alias ) : ""; 573 } 574 575 public FetchMode getFetchMode() { 576 return fetchMode; 577 } 578 579 public boolean hasOrdering() { 580 return hasOrder; 581 } 582 583 public boolean hasWhere() { 584 return hasWhere; 585 } 586 587 protected String getSQLDeleteString() { 588 return sqlDeleteString; 589 } 590 591 protected String getSQLInsertRowString() { 592 return sqlInsertRowString; 593 } 594 595 protected String getSQLUpdateRowString() { 596 return sqlUpdateRowString; 597 } 598 599 protected String getSQLDeleteRowString() { 600 return sqlDeleteRowString; 601 } 602 603 public Type getKeyType() { 604 return keyType; 605 } 606 607 public Type getIndexType() { 608 return indexType; 609 } 610 611 public Type getElementType() { 612 return elementType; 613 } 614 615 618 public Class getElementClass() { return elementClass; 620 } 621 622 public Object readElement(ResultSet rs, Object owner, String [] aliases, SessionImplementor session) throws HibernateException, SQLException { 623 Object element = getElementType().nullSafeGet( rs, aliases, session, owner ); 624 return element; 625 } 626 627 public Object readIndex(ResultSet rs, String [] aliases, SessionImplementor session) throws HibernateException, SQLException { 628 Object index = getIndexType().nullSafeGet( rs, aliases, session, null ); 629 if ( index == null ) { 630 throw new HibernateException( "null index column for collection: " + role ); 631 } 632 if (baseIndex!=0) { 633 index = new Integer ( ( (Integer ) index ).intValue() - baseIndex ); 634 } 635 return index; 636 } 637 638 public Object readIdentifier(ResultSet rs, String alias, SessionImplementor session) throws HibernateException, SQLException { 639 Object id = getIdentifierType().nullSafeGet( rs, alias, session, null ); 640 if ( id == null ) throw new HibernateException( "null identifier column for collection: " + role ); 641 return id; 642 } 643 644 public Object readKey(ResultSet rs, String [] aliases, SessionImplementor session) throws HibernateException, SQLException { 645 return getKeyType().nullSafeGet( rs, aliases, session, null ); 646 } 647 648 651 protected int writeKey(PreparedStatement st, Serializable key, int i, SessionImplementor session) 652 throws HibernateException, SQLException { 653 654 if ( key == null ) throw new NullPointerException ( "null key for collection: " + role ); getKeyType().nullSafeSet( st, key, i, session ); 656 return i + keyColumnAliases.length; 657 } 658 659 662 protected int writeElement(PreparedStatement st, Object elt, int i, SessionImplementor session) 663 throws HibernateException, SQLException { 664 getElementType().nullSafeSet(st, elt, i, elementColumnIsSettable, session); 665 return i + ArrayHelper.countTrue(elementColumnIsSettable); 666 667 } 668 669 672 protected int writeIndex(PreparedStatement st, Object index, int i, SessionImplementor session) 673 throws HibernateException, SQLException { 674 if (baseIndex!=0) { 675 index = new Integer ( ( (Integer ) index ).intValue() + baseIndex ); 676 } 677 getIndexType().nullSafeSet( st, index, i, indexColumnIsSettable, session ); 678 return i + ArrayHelper.countTrue(indexColumnIsSettable); 679 } 680 681 684 protected int writeElementToWhere(PreparedStatement st, Object elt, int i, SessionImplementor session) 685 throws HibernateException, SQLException { 686 if (elementIsPureFormula) throw new AssertionFailure("cannot use a formula-based element in the where condition"); 687 getElementType().nullSafeSet(st, elt, i, elementColumnIsInPrimaryKey, session); 688 return i + elementColumnAliases.length; 689 690 } 691 692 695 protected int writeIndexToWhere(PreparedStatement st, Object index, int i, SessionImplementor session) 696 throws HibernateException, SQLException { 697 if (indexContainsFormula) throw new AssertionFailure("cannot use a formula-based index in the where condition"); 698 if (baseIndex!=0) { 699 index = new Integer ( ( (Integer ) index ).intValue() + baseIndex ); 700 } 701 getIndexType().nullSafeSet( st, index, i, session ); 702 return i + indexColumnAliases.length; 703 } 704 705 708 public int writeIdentifier(PreparedStatement st, Object  |