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 id, int i, SessionImplementor session) 709 throws HibernateException, SQLException { 710 711 getIdentifierType().nullSafeSet( st, id, i, session ); 712 return i + 1; 713 } 714 715 public boolean isPrimitiveArray() { 716 return isPrimitiveArray; 717 } 718 719 public boolean isArray() { 720 return isArray; 721 } 722 723 public String [] getKeyColumnAliases(String suffix) { 724 return new Alias( suffix ).toAliasStrings( keyColumnAliases ); 725 } 726 727 public String [] getElementColumnAliases(String suffix) { 728 return new Alias( suffix ).toAliasStrings( elementColumnAliases ); 729 } 730 731 public String [] getIndexColumnAliases(String suffix) { 732 if ( hasIndex ) { 733 return new Alias( suffix ).toAliasStrings( indexColumnAliases ); 734 } 735 else { 736 return null; 737 } 738 } 739 740 public String getIdentifierColumnAlias(String suffix) { 741 if ( hasIdentifier ) { 742 return new Alias( suffix ).toAliasString( identifierColumnAlias ); 743 } 744 else { 745 return null; 746 } 747 } 748 749 752 public String selectFragment(String alias, String columnSuffix) { 753 SelectFragment frag = generateSelectFragment( alias, columnSuffix ); 754 appendElementColumns( frag, alias ); 755 appendIndexColumns( frag, alias ); 756 appendIdentifierColumns( frag, alias ); 757 758 return frag.toFragmentString() 759 .substring( 2 ); } 761 762 protected SelectFragment generateSelectFragment(String alias, String columnSuffix) { 763 SelectFragment frag = new SelectFragment() 764 .setSuffix( columnSuffix ) 765 .addColumns( alias, keyColumnNames, keyColumnAliases ); 766 return frag; 767 } 768 769 protected void appendElementColumns(SelectFragment frag, String elemAlias) { 770 for ( int i=0; i<elementColumnIsSettable.length; i++ ) { 771 if ( elementColumnIsSettable[i] ) { 772 frag.addColumn( elemAlias, elementColumnNames[i], elementColumnAliases[i] ); 773 } 774 else { 775 frag.addFormula( elemAlias, elementFormulaTemplates[i], elementColumnAliases[i] ); 776 } 777 } 778 } 779 780 protected void appendIndexColumns(SelectFragment frag, String alias) { 781 if ( hasIndex ) { 782 for ( int i=0; i<indexColumnIsSettable.length; i++ ) { 783 if ( indexColumnIsSettable[i] ) { 784 frag.addColumn( alias, indexColumnNames[i], indexColumnAliases[i] ); 785 } 786 else { 787 frag.addFormula( alias, indexFormulaTemplates[i], indexColumnAliases[i] ); 788 } 789 } 790 } 791 } 792 793 protected void appendIdentifierColumns(SelectFragment frag, String alias) { 794 if ( hasIdentifier ) frag.addColumn( alias, identifierColumnName, identifierColumnAlias ); 795 } 796 797 public String [] getIndexColumnNames() { 798 return indexColumnNames; 799 } 800 801 public String [] getIndexColumnNames(String alias) { 802 return qualify(alias, indexColumnNames, indexFormulaTemplates); 803 804 } 805 806 public String [] getElementColumnNames(String alias) { 807 return qualify(alias, elementColumnNames, elementFormulaTemplates); 808 } 809 810 private static final String [] qualify(String alias, String [] columnNames, String [] formulaTemplates) { 811 int span = columnNames.length; 812 String [] result = new String [span]; 813 for (int i=0; i<span; i++) { 814 if ( columnNames[i]==null ) { 815 result[i] = StringHelper.replace( formulaTemplates[i], Template.TEMPLATE, alias ); 816 } 817 else { 818 result[i] = StringHelper.qualify( alias, columnNames[i] ); 819 } 820 } 821 return result; 822 } 823 824 public String [] getElementColumnNames() { 825 return elementColumnNames; } 827 828 public String [] getKeyColumnNames() { 829 return keyColumnNames; 830 } 831 832 public boolean hasIndex() { 833 return hasIndex; 834 } 835 836 public boolean isLazy() { 837 return isLazy; 838 } 839 840 public boolean isInverse() { 841 return isInverse; 842 } 843 844 public String getTableName() { 845 return qualifiedTableName; 846 } 847 848 public void remove(Serializable id, SessionImplementor session) throws HibernateException { 849 850 if ( !isInverse && isRowDeleteEnabled() ) { 851 852 if ( log.isDebugEnabled() ) { 853 log.debug( "Deleting collection: " + MessageHelper.collectionInfoString( this, id, getFactory() ) ); 854 } 855 856 858 try { 859 int offset = 1; 860 PreparedStatement st = null; 861 if ( isDeleteCallable() ) { 862 CallableStatement callstatement = session.getBatcher() 863 .prepareBatchCallableStatement( getSQLDeleteString() ); 864 callstatement.registerOutParameter( offset++, Types.NUMERIC ); st = callstatement; 866 } 867 else { 868 st = session.getBatcher().prepareBatchStatement( getSQLDeleteString() ); 869 } 870 871 try { 872 writeKey( st, id, offset, session ); 873 session.getBatcher().addToBatch( -1 ); 874 } 875 catch ( SQLException sqle ) { 876 session.getBatcher().abortBatch( sqle ); 877 throw sqle; 878 } 879 880 if ( log.isDebugEnabled() ) log.debug( "done deleting collection" ); 881 } 882 catch ( SQLException sqle ) { 883 throw JDBCExceptionHelper.convert( 884 sqlExceptionConverter, 885 sqle, 886 "could not delete collection: " + 887 MessageHelper.collectionInfoString( this, id, getFactory() ), 888 getSQLDeleteString() 889 ); 890 } 891 892 } 893 894 } 895 896 public void recreate(PersistentCollection collection, Serializable id, SessionImplementor session) 897 throws HibernateException { 898 899 if ( !isInverse && isRowInsertEnabled() ) { 900 901 if ( log.isDebugEnabled() ) { 902 log.debug( "Inserting collection: " + MessageHelper.collectionInfoString( this, id, getFactory() ) ); 903 } 904 905 try { 906 Iterator entries = collection.entries(this); 908 if ( entries.hasNext() ) { 909 try { 910 collection.preInsert( this ); 911 int i = 0; 912 int count = 0; 913 while ( entries.hasNext() ) { 914 915 final Object entry = entries.next(); 916 if ( collection.entryExists( entry, i ) ) { 917 int offset = 1; 918 PreparedStatement st = null; 919 if ( isInsertCallable() ) { 920 CallableStatement callstatement = session.getBatcher() 921 .prepareBatchCallableStatement( getSQLInsertRowString() ); 922 callstatement.registerOutParameter( offset++, Types.NUMERIC ); st = callstatement; 924 } 925 else { 926 st = session.getBatcher().prepareBatchStatement( getSQLInsertRowString() ); 927 } 928 int loc = writeKey( st, id, offset, session ); 930 if ( hasIdentifier ) { 931 loc = writeIdentifier( st, collection.getIdentifier(entry, i), loc, session ); 932 } 933 if ( hasIndex ) { 934 loc = writeIndex( st, collection.getIndex(entry, i, this), loc, session ); 935 } 936 loc = writeElement(st, collection.getElement(entry), loc, session ); 938 session.getBatcher().addToBatch( 1 ); 940 collection.afterRowInsert( this, entry, i ); 941 count++; 942 } 943 i++; 944 } 945 if ( log.isDebugEnabled() ) log.debug( "done inserting collection: " + count + " rows inserted" ); 946 } 947 catch ( SQLException sqle ) { 948 session.getBatcher().abortBatch( sqle ); 949 throw sqle; 950 } 951 952 } 953 else { 954 if ( log.isDebugEnabled() ) log.debug( "collection was empty" ); 955 } 956 } 957 catch ( SQLException sqle ) { 958 throw JDBCExceptionHelper.convert( 959 sqlExceptionConverter, 960 sqle, 961 "could not insert collection: " + 962 MessageHelper.collectionInfoString( this, id, getFactory() ), 963 getSQLInsertRowString() 964 ); 965 } 966 } 967 } 968 969 protected boolean isRowDeleteEnabled() { 970 return true; 971 } 972 973 public void deleteRows(PersistentCollection collection, Serializable id, SessionImplementor session) 974 throws HibernateException { 975 976 if ( !isInverse && isRowDeleteEnabled() ) { 977 978 if ( log.isDebugEnabled() ) { 979 log.debug( "Deleting rows of collection: " + MessageHelper.collectionInfoString( this, id, getFactory() ) ); 980 } 981 982 boolean deleteByIndex = !isOneToMany() && hasIndex && !indexContainsFormula; 983 984 try { 985 Iterator deletes = collection.getDeletes( this, !deleteByIndex ); 987 if ( deletes.hasNext() ) { 988 int offset = 1; 989 int count = 0; 990 PreparedStatement st = null; 991 if ( isDeleteCallable() ) { 992 CallableStatement callstatement = session.getBatcher() 993 .prepareBatchCallableStatement( getSQLDeleteRowString() ); 994 callstatement.registerOutParameter( offset++, Types.NUMERIC ); st = callstatement; 996 } 997 else { 998 st = session.getBatcher().prepareBatchStatement( getSQLDeleteRowString() ); 999 } 1000 1001 try { 1002 int i=0; 1003 while ( deletes.hasNext() ) { 1004 Object entry = deletes.next(); 1005 int loc = offset; 1006 if ( hasIdentifier ) { 1007 loc = writeIdentifier( st, entry, loc, session ); 1008 } 1009 else { 1010 loc = writeKey( st, id, loc, session ); 1012 if (deleteByIndex) { 1014 loc = writeIndexToWhere( st, entry, loc, session ); 1015 } 1016 else { 1017 loc = writeElementToWhere( st, entry, loc, session ); 1018 } 1019 } 1020 session.getBatcher().addToBatch( -1 ); 1021 count++; 1022 i++; 1023 } 1024 } 1025 catch ( SQLException sqle ) { 1026 session.getBatcher().abortBatch( sqle ); 1027 throw sqle; 1028 } 1029 1030 if ( log.isDebugEnabled() ) log.debug( "done deleting collection rows: " + count + " deleted" ); 1031 } 1032 else { 1033 if ( log.isDebugEnabled() ) log.debug( "no rows to delete" ); 1034 } 1035 } 1036 catch ( SQLException sqle ) { 1037 throw JDBCExceptionHelper.convert( 1038 sqlExceptionConverter, 1039 sqle, 1040 "could not delete collection rows: " + 1041 MessageHelper.collectionInfoString( this, id, getFactory() ), 1042 getSQLDeleteRowString() 1043 ); 1044 } 1045 } 1046 } 1047 1048 protected boolean isRowInsertEnabled() { 1049 return true; 1050 } 1051 1052 public void insertRows(PersistentCollection collection, Serializable id, SessionImplementor session) 1053 throws HibernateException { 1054 1055 if ( !isInverse && isRowInsertEnabled() ) { 1056 1057 if ( log.isDebugEnabled() ) { 1058 log.debug( "Inserting rows of collection: " + MessageHelper.collectionInfoString( this, id, getFactory() ) ); 1059 } 1060 1061 try { 1062 Iterator entries = collection.entries(this); 1064 boolean callable = isInsertCallable(); 1065 try { 1066 collection.preInsert( this ); 1067 int i = 0; 1068 int count = 0; 1069 int offset = 1; 1070 while ( entries.hasNext() ) { 1071 Object entry = entries.next(); 1072 PreparedStatement st = null; 1073 if ( collection.needsInserting( entry, i, elementType ) ) { 1074 if ( st == null ) { 1075 if ( callable ) { 1076 CallableStatement callstatement = session.getBatcher() 1077 .prepareBatchCallableStatement( getSQLInsertRowString() ); 1078 callstatement.registerOutParameter( offset++, Types.NUMERIC ); st = callstatement; 1080 } 1081 else { 1082 st = session.getBatcher().prepareBatchStatement( getSQLInsertRowString() ); 1083 } 1084 } 1085 int loc = writeKey( st, id, offset, session ); 1087 if ( hasIdentifier ) { 1088 loc = writeIdentifier( st, collection.getIdentifier(entry, i), loc, session ); 1089 } 1090 if ( hasIndex ) { 1091 loc = writeIndex( st, collection.getIndex(entry, i, this), loc, session ); 1092 } 1093 loc = writeElement(st, collection.getElement(entry), loc, session ); 1095 session.getBatcher().addToBatch( 1 ); 1097 collection.afterRowInsert( this, entry, i ); 1098 count++; 1099 } 1100 i++; 1101 } 1102 if ( log.isDebugEnabled() ) log.debug( "done inserting rows: " + count + " inserted" ); 1103 } 1104 catch ( SQLException sqle ) { 1105 session.getBatcher().abortBatch( sqle ); 1106 throw sqle; 1107 } 1108 } 1109 catch ( SQLException sqle ) { 1110 throw JDBCExceptionHelper.convert( 1111 sqlExceptionConverter, 1112 sqle, 1113 "could not insert collection rows: " + 1114 MessageHelper.collectionInfoString( this, id, getFactory() ), 1115 getSQLInsertRowString() 1116 ); 1117 } 1118 1119 } 1120 } 1121 1122 1123 public String getRole() { 1124 return role; 1125 } 1126 1127 public String getOwnerEntityName() { 1128 return entityName; 1129 } 1130 1131 public EntityPersister getOwnerEntityPersister() { 1132 return ownerPersister; 1133 } 1134 1135 public IdentifierGenerator getIdentifierGenerator() { 1136 return identifierGenerator; 1137 } 1138 1139 public Type getIdentifierType() { 1140 return identifierType; 1141 } 1142 1143 public boolean hasOrphanDelete() { 1144 return hasOrphanDelete; 1145 } 1146 1147 public Type toType(String propertyName) throws QueryException { 1148 if ( "index".equals( propertyName ) ) return indexType; 1149 return elementPropertyMapping.toType( propertyName ); 1150 } 1151 1152 public abstract boolean isManyToMany(); 1153 1154 public String getManyToManyFilterFragment(String alias, Map enabledFilters) { 1155 StringBuffer buffer = new StringBuffer (); 1156 manyToManyFilterHelper.render( buffer, alias, enabledFilters ); 1157 1158 if ( manyToManyWhereString != null ) { 1159 buffer.append( " and " ) 1160 .append( StringHelper.replace( manyToManyWhereTemplate, Template.TEMPLATE, alias ) ); 1161 } 1162 1163 return buffer.toString(); 1164 } 1165 1166 public String [] toColumns(String alias, String propertyName) 1167 throws QueryException { 1168 1169 if ( "index".equals( propertyName ) ) { 1170 if ( isManyToMany() ) { 1171 throw new QueryException( "index() function not supported for many-to-many association" ); 1172 } 1173 return StringHelper.qualify( alias, indexColumnNames ); 1174 } 1175 1176 return elementPropertyMapping.toColumns( alias, propertyName ); 1177 } 1178 1179 public String [] toColumns(String propertyName) 1180 throws QueryException { 1181 1182 if ( "index".equals( propertyName ) ) { 1183 if ( isManyToMany() ) { 1184 throw new QueryException( "index() function not supported for many-to-many association" ); 1185 } 1186 return indexColumnNames; 1187 } 1188 1189 return elementPropertyMapping.toColumns( propertyName ); 1190 } 1191 1192 public Type getType() { 1193 return elementPropertyMapping.getType(); } 1195 1196 public String getName() { 1197 return getRole(); 1198 } 1199 1200 public EntityPersister getElementPersister() { 1201 if ( elementPersister == null ) throw new AssertionFailure( "not an association" ); 1202 return ( Loadable ) elementPersister; 1203 } 1204 1205 public boolean isCollection() { 1206 return true; 1207 } 1208 1209 public Serializable [] getCollectionSpaces() { 1210 return spaces; 1211 } 1212 1213 protected abstract String generateDeleteString(); 1214 1215 protected abstract String generateDeleteRowString(); 1216 1217 protected abstract String generateUpdateRowString(); 1218 1219 protected abstract String generateInsertRowString(); 1220 1221 public void updateRows(PersistentCollection collection, Serializable id, SessionImplementor session) 1222 throws HibernateException { 1223 1224 if ( !isInverse && collection.isRowUpdatePossible() ) { 1225 1226 if ( log.isDebugEnabled() ) log.debug( "Updating rows of collection: " + role + "#" + id ); 1227 1228 int count = doUpdateRows( id, collection, session ); 1230 1231 if ( log.isDebugEnabled() ) log.debug( "done updating rows: " + count + " updated" ); 1232 } 1233 } 1234 1235 protected abstract int doUpdateRows(Serializable key, PersistentCollection collection, SessionImplementor session) 1236 throws HibernateException; 1237 1238 public CollectionMetadata getCollectionMetadata() { 1239 return this; 1240 } 1241 1242 public SessionFactoryImplementor getFactory() { 1243 return factory; 1244 } 1245 1246 protected String filterFragment(String alias) throws MappingException { 1247 return hasWhere() ? " and " + getSQLWhereString( alias ) : ""; 1248 } 1249 1250 public String filterFragment(String alias, Map enabledFilters) throws MappingException { 1251 1252 StringBuffer sessionFilterFragment = new StringBuffer (); 1253 filterHelper.render( sessionFilterFragment, alias, enabledFilters ); 1254 1255 return sessionFilterFragment.append( filterFragment( alias ) ).toString(); 1256 } 1257 1258 public String oneToManyFilterFragment(String alias) throws MappingException { 1259 return ""; 1260 } 1261 1262 protected boolean isInsertCallable() { 1263 return insertCallable; 1264 } 1265 1266 protected boolean isUpdateCallable() { 1267 return updateCallable; 1268 } 1269 1270 protected boolean isDeleteCallable() { 1271 return deleteCallable; 1272 } 1273 1274 protected boolean isDeleteAllCallable() { 1275 return deleteAllCallable; 1276 } 1277 1278 public String toString() { 1279 return StringHelper.unqualify( getClass().getName() ) + '(' + role + ')'; 1280 } 1281 1282 public boolean isVersioned() { 1283 return isVersioned && getOwnerEntityPersister().isVersioned(); 1284 } 1285 1286 public String getNodeName() { 1287 return nodeName; 1288 } 1289 1290 public String getElementNodeName() { 1291 return elementNodeName; 1292 } 1293 1294 public String getIndexNodeName() { 1295 return indexNodeName; 1296 } 1297 1298 protected SQLExceptionConverter getSQLExceptionConverter() { 1299 return sqlExceptionConverter; 1300 } 1301 1302 public CacheEntryStructure getCacheEntryStructure() { 1303 return cacheEntryStructure; 1304 } 1305 1306 public boolean isAffectedByEnabledFilters(SessionImplementor session) { 1307 return filterHelper.isAffectedBy( session.getEnabledFilters() ) || 1308 ( isManyToMany() && manyToManyFilterHelper.isAffectedBy( session.getEnabledFilters() ) ); 1309 } 1310 1311 public boolean isSubselectLoadable() { 1312 return subselectLoadable; 1313 } 1314 1315 public boolean isMutable() { 1316 return isMutable; 1317 } 1318 1319 public String [] getCollectionPropertyColumnAliases(String propertyName, String suffix) { 1320 String rawAliases[] = (String []) collectionPropertyColumnAliases.get(propertyName); 1321 1322 if(rawAliases==null) return null; 1323 1324 String result[] = new String [rawAliases.length]; 1325 for ( int i=0; i<rawAliases.length; i++ ) { 1326 result[i] = new Alias(suffix).toUnquotedAliasString( rawAliases[i] ); 1327 } 1328 return result; 1329 } 1330 1331 public void initCollectionPropertyMap() { 1333 1334 initCollectionPropertyMap( "key", keyType, keyColumnAliases, keyColumnNames ); 1335 initCollectionPropertyMap( "element", elementType, elementColumnAliases, elementColumnNames ); 1336 if(hasIndex) { 1337 initCollectionPropertyMap( "index", indexType, indexColumnAliases, indexColumnNames ); 1338 } 1339 if(hasIdentifier) { 1340 initCollectionPropertyMap( "id", identifierType, new String [] { identifierColumnAlias }, new String [] { identifierColumnName } ); 1341 } 1342 } 1343 1344 private void initCollectionPropertyMap(String aliasName, Type type, String [] columnAliases, String [] columnNames) { 1345 1346 collectionPropertyColumnAliases.put(aliasName, columnAliases); 1347 collectionPropertyColumnNames.put(aliasName, columnNames); 1348 1349 if( type.isComponentType() ) { 1350 AbstractComponentType ct = (AbstractComponentType) type; 1351 String [] propertyNames = ct.getPropertyNames(); 1352 for (int i = 0; i < propertyNames.length; i++) { 1353 String name = propertyNames[i]; 1354 collectionPropertyColumnAliases.put(aliasName + "." + name, columnAliases[i]); 1355 collectionPropertyColumnAliases.put(aliasName + "." + name, columnNames[i]); 1356 } 1357 } 1358 1359 } 1360} 1361 | Popular Tags |