1 package org.hibernate.persister.entity; 3 4 import java.io.Serializable ; 5 import java.util.ArrayList ; 6 import java.util.HashMap ; 7 import java.util.Iterator ; 8 import java.util.Map ; 9 10 import org.hibernate.AssertionFailure; 11 import org.hibernate.Hibernate; 12 import org.hibernate.HibernateException; 13 import org.hibernate.MappingException; 14 import org.hibernate.QueryException; 15 import org.hibernate.cache.CacheConcurrencyStrategy; 16 import org.hibernate.engine.Mapping; 17 import org.hibernate.engine.SessionFactoryImplementor; 18 import org.hibernate.engine.Versioning; 19 import org.hibernate.mapping.Column; 20 import org.hibernate.mapping.KeyValue; 21 import org.hibernate.mapping.PersistentClass; 22 import org.hibernate.mapping.Property; 23 import org.hibernate.mapping.Selectable; 24 import org.hibernate.mapping.Subclass; 25 import org.hibernate.mapping.Table; 26 import org.hibernate.sql.CaseFragment; 27 import org.hibernate.sql.SelectFragment; 28 import org.hibernate.type.Type; 29 import org.hibernate.util.ArrayHelper; 30 31 37 public class JoinedSubclassEntityPersister extends BasicEntityPersister { 38 39 private final int tableSpan; 41 private final String [] tableNames; 42 private final String [] naturalOrderTableNames; 43 private final String [][] tableKeyColumns; 44 private final String [][] naturalOrderTableKeyColumns; 45 private final boolean[] naturalOrderCascadeDeleteEnabled; 46 47 private final String [] spaces; 48 49 private final String [] subclassClosure; 50 51 private final String [] subclassTableNameClosure; 52 private final String [][] subclassTableKeyColumnClosure; 53 private final boolean[] isClassOrSuperclassTable; 54 55 private final int[] naturalOrderPropertyTableNumbers; 57 private final int[] propertyTableNumbers; 58 59 private final int[] subclassPropertyTableNumberClosure; 62 63 private final int[] subclassColumnTableNumberClosure; 66 private final int[] subclassFormulaTableNumberClosure; 67 68 private final Map subclassesByDiscriminatorValue = new HashMap (); 72 private final String [] discriminatorValues; 73 private final String [] notNullColumnNames; 74 private final int[] notNullColumnTableNumbers; 75 76 private final String [] constraintOrderedTableNames; 77 private final String [][] constraintOrderedKeyColumnNames; 78 79 private final String discriminatorSQLString; 80 81 83 public JoinedSubclassEntityPersister( 84 final PersistentClass persistentClass, 85 final CacheConcurrencyStrategy cache, 86 final SessionFactoryImplementor factory, 87 final Mapping mapping) 88 throws HibernateException { 89 90 super(persistentClass, cache, factory); 91 92 94 final Object discriminatorValue; 95 if ( persistentClass.isPolymorphic() ) { 96 try { 97 discriminatorValue = new Integer ( persistentClass.getSubclassId() ); 98 discriminatorSQLString = discriminatorValue.toString(); 99 } 100 catch (Exception e) { 101 throw new MappingException("Could not format discriminator value to SQL string", e ); 102 } 103 } 104 else { 105 discriminatorValue = null; 106 discriminatorSQLString = null; 107 } 108 109 if ( optimisticLockMode()!=Versioning.OPTIMISTIC_LOCK_VERSION ) { 110 throw new MappingException( 111 "optimistic-lock attribute not supported for joined-subclass mappings: " + 112 getEntityName() 113 ); 114 } 115 116 118 final int idColumnSpan = getIdentifierColumnSpan(); 119 120 ArrayList tables = new ArrayList (); 121 ArrayList keyColumns = new ArrayList (); 122 ArrayList cascadeDeletes = new ArrayList (); 123 Iterator titer = persistentClass.getTableClosureIterator(); 124 Iterator kiter = persistentClass.getKeyClosureIterator(); 125 while ( titer.hasNext() ) { 126 Table tab = (Table) titer.next(); 127 KeyValue key = (KeyValue) kiter.next(); 128 String tabname = tab.getQualifiedName( 129 factory.getDialect(), 130 factory.getSettings().getDefaultCatalogName(), 131 factory.getSettings().getDefaultSchemaName() 132 ); 133 tables.add(tabname); 134 String [] keyCols = new String [idColumnSpan]; 135 Iterator citer = key.getColumnIterator(); 136 for ( int k=0; k<idColumnSpan; k++ ) { 137 keyCols[k] = ( (Column) citer.next() ).getQuotedName( factory.getDialect() ); 138 } 139 keyColumns.add(keyCols); 140 cascadeDeletes.add( new Boolean ( key.isCascadeDeleteEnabled() && factory.getDialect().supportsCascadeDelete() ) ); 141 } 142 naturalOrderTableNames = ArrayHelper.toStringArray(tables); 143 naturalOrderTableKeyColumns = ArrayHelper.to2DStringArray(keyColumns); 144 naturalOrderCascadeDeleteEnabled = ArrayHelper.toBooleanArray(cascadeDeletes); 145 146 ArrayList subtables = new ArrayList (); 147 ArrayList isConcretes = new ArrayList (); 148 keyColumns = new ArrayList (); 149 titer = persistentClass.getSubclassTableClosureIterator(); 150 while ( titer.hasNext() ) { 151 Table tab = (Table) titer.next(); 152 isConcretes.add( new Boolean ( persistentClass.isClassOrSuperclassTable(tab) ) ); 153 String tabname = tab.getQualifiedName( 154 factory.getDialect(), 155 factory.getSettings().getDefaultCatalogName(), 156 factory.getSettings().getDefaultSchemaName() 157 ); 158 subtables.add(tabname); 159 String [] key = new String [idColumnSpan]; 160 Iterator citer = tab.getPrimaryKey().getColumnIterator(); 161 for ( int k=0; k<idColumnSpan; k++ ) { 162 key[k] = ( (Column) citer.next() ).getQuotedName( factory.getDialect() ); 163 } 164 keyColumns.add(key); 165 } 166 subclassTableNameClosure = ArrayHelper.toStringArray(subtables); 167 subclassTableKeyColumnClosure = ArrayHelper.to2DStringArray(keyColumns); 168 isClassOrSuperclassTable = ArrayHelper.toBooleanArray(isConcretes); 169 170 constraintOrderedTableNames = new String [subclassTableNameClosure.length]; 171 constraintOrderedKeyColumnNames = new String [subclassTableNameClosure.length][]; 172 int currentPosition = 0; 173 for ( int i = subclassTableNameClosure.length - 1; i >= 0 ; i--, currentPosition++ ) { 174 constraintOrderedTableNames[currentPosition] = subclassTableNameClosure[i]; 175 constraintOrderedKeyColumnNames[currentPosition] = subclassTableKeyColumnClosure[i]; 176 } 177 178 tableSpan = naturalOrderTableNames.length; 179 tableNames = reverse(naturalOrderTableNames); 180 tableKeyColumns = reverse(naturalOrderTableKeyColumns); 181 reverse(subclassTableNameClosure, tableSpan); 182 reverse(subclassTableKeyColumnClosure, tableSpan); 183 184 spaces = ArrayHelper.join( 185 tableNames, 186 ArrayHelper.toStringArray( persistentClass.getSynchronizedTables() ) 187 ); 188 189 customSQLInsert = new String [tableSpan]; 191 customSQLUpdate = new String [tableSpan]; 192 customSQLDelete = new String [tableSpan]; 193 insertCallable = new boolean[tableSpan]; 194 updateCallable = new boolean[tableSpan]; 195 deleteCallable = new boolean[tableSpan]; 196 197 PersistentClass pc = persistentClass; 198 int jk = tableSpan-1; 199 while (pc!=null) { 200 customSQLInsert[jk] = pc.getCustomSQLInsert(); 201 customSQLUpdate[jk] = pc.getCustomSQLUpdate(); 202 customSQLDelete[jk] = pc.getCustomSQLDelete(); 203 insertCallable[jk] = pc.isCustomInsertCallable(); 204 updateCallable[jk] = pc.isCustomUpdateCallable(); 205 deleteCallable[jk] = pc.isCustomDeleteCallable(); 206 jk--; 207 pc = pc.getSuperclass(); 208 } 209 if (jk!=-1) throw new AssertionFailure("Tablespan does not match height of joined-subclass hiearchy."); 210 211 213 int hydrateSpan = getPropertySpan(); 214 naturalOrderPropertyTableNumbers = new int[hydrateSpan]; 215 propertyTableNumbers = new int[hydrateSpan]; 216 Iterator iter = persistentClass.getPropertyClosureIterator(); 217 int i=0; 218 while( iter.hasNext() ) { 219 Property prop = (Property) iter.next(); 220 String tabname = prop.getValue().getTable().getQualifiedName( 221 factory.getDialect(), 222 factory.getSettings().getDefaultCatalogName(), 223 factory.getSettings().getDefaultSchemaName() 224 ); 225 propertyTableNumbers[i] = getTableId(tabname, tableNames); 226 naturalOrderPropertyTableNumbers[i] = getTableId(tabname, naturalOrderTableNames); 227 i++; 228 } 229 230 232 234 ArrayList columnTableNumbers = new ArrayList (); 235 ArrayList formulaTableNumbers = new ArrayList (); 236 ArrayList propTableNumbers = new ArrayList (); 237 238 iter = persistentClass.getSubclassPropertyClosureIterator(); 239 while ( iter.hasNext() ) { 240 Property prop = (Property) iter.next(); 241 Table tab = prop.getValue().getTable(); 242 String tabname = tab.getQualifiedName( 243 factory.getDialect(), 244 factory.getSettings().getDefaultCatalogName(), 245 factory.getSettings().getDefaultSchemaName() 246 ); 247 Integer tabnum = new Integer ( getTableId(tabname, subclassTableNameClosure) ); 248 propTableNumbers.add(tabnum); 249 250 Iterator citer = prop.getColumnIterator(); 251 while ( citer.hasNext() ) { 252 Selectable thing = (Selectable) citer.next(); 253 if ( thing.isFormula() ) { 254 formulaTableNumbers.add(tabnum); 255 } 256 else { 257 columnTableNumbers.add(tabnum); 258 } 259 } 260 261 } 262 263 subclassColumnTableNumberClosure = ArrayHelper.toIntArray(columnTableNumbers); 264 subclassPropertyTableNumberClosure = ArrayHelper.toIntArray(propTableNumbers); 265 subclassFormulaTableNumberClosure = ArrayHelper.toIntArray(formulaTableNumbers); 266 267 269 int subclassSpan = persistentClass.getSubclassSpan() + 1; 270 subclassClosure = new String [subclassSpan]; 271 subclassClosure[subclassSpan-1] = getEntityName(); 272 if ( persistentClass.isPolymorphic() ) { 273 subclassesByDiscriminatorValue.put( discriminatorValue, getEntityName() ); 274 discriminatorValues = new String [subclassSpan]; 275 discriminatorValues[subclassSpan-1] = discriminatorSQLString; 276 notNullColumnTableNumbers = new int[subclassSpan]; 277 final int id = getTableId( 278 persistentClass.getTable().getQualifiedName( 279 factory.getDialect(), 280 factory.getSettings().getDefaultCatalogName(), 281 factory.getSettings().getDefaultSchemaName() 282 ), 283 subclassTableNameClosure 284 ); 285 notNullColumnTableNumbers[subclassSpan-1] = id; 286 notNullColumnNames = new String [subclassSpan]; 287 notNullColumnNames[subclassSpan-1] = subclassTableKeyColumnClosure[id][0]; } 289 else { 290 discriminatorValues = null; 291 notNullColumnTableNumbers = null; 292 notNullColumnNames = null; 293 } 294 295 iter = persistentClass.getSubclassIterator(); 296 int k=0; 297 while ( iter.hasNext() ) { 298 Subclass sc = (Subclass) iter.next(); 299 subclassClosure[k] = sc.getEntityName(); 300 try { 301 if ( persistentClass.isPolymorphic() ) { 302 Integer subclassId = new Integer ( sc.getSubclassId() ); subclassesByDiscriminatorValue.put( subclassId, sc.getEntityName() ); 307 discriminatorValues[k] = subclassId.toString(); 308 int id = getTableId( 309 sc.getTable().getQualifiedName( 310 factory.getDialect(), 311 factory.getSettings().getDefaultCatalogName(), 312 factory.getSettings().getDefaultSchemaName() 313 ), 314 subclassTableNameClosure 315 ); 316 notNullColumnTableNumbers[k] = id; 317 notNullColumnNames[k] = subclassTableKeyColumnClosure[id][0]; } 319 } 320 catch (Exception e) { 321 throw new MappingException("Error parsing discriminator value", e ); 322 } 323 k++; 324 } 325 326 initLockers(); 327 328 initSubclassPropertyAliasesMap(persistentClass); 329 330 postConstruct(mapping); 331 332 } 333 334 339 340 public String getSubclassPropertyTableName(int i) { 341 return subclassTableNameClosure[ subclassPropertyTableNumberClosure[i] ]; 342 } 343 344 public Type getDiscriminatorType() { 345 return Hibernate.INTEGER; 346 } 347 348 public String getDiscriminatorSQLValue() { 349 return discriminatorSQLString; 350 } 351 352 353 public String getSubclassForDiscriminatorValue(Object value) { 354 return (String ) subclassesByDiscriminatorValue.get(value); 355 } 356 357 public Serializable [] getPropertySpaces() { 358 return spaces; } 360 361 362 protected String getTableName(int j) { 363 return naturalOrderTableNames[j]; 364 } 365 366 protected String [] getKeyColumns(int j) { 367 return naturalOrderTableKeyColumns[j]; 368 } 369 370 protected boolean isTableCascadeDeleteEnabled(int j) { 371 return naturalOrderCascadeDeleteEnabled[j]; 372 } 373 374 protected boolean isPropertyOfTable(int property, int j) { 375 return naturalOrderPropertyTableNumbers[property]==j; 376 } 377 378 382 403 404 private static final void reverse(Object [] objects, int len) { 405 Object [] temp = new Object [len]; 406 for (int i=0; i<len; i++) { 407 temp[i] = objects[len-i-1]; 408 } 409 for (int i=0; i<len; i++) { 410 objects[i] = temp[i]; 411 } 412 } 413 414 private static final String [] reverse(String [] objects) { 415 int len = objects.length; 416 String [] temp = new String [len]; 417 for (int i=0; i<len; i++) { 418 temp[i] = objects[len-i-1]; 419 } 420 return temp; 421 } 422 423 private static final String [][] reverse(String [][] objects) { 424 int len = objects.length; 425 String [][] temp = new String [len][]; 426 for (int i=0; i<len; i++) { 427 temp[i] = objects[len-i-1]; 428 } 429 return temp; 430 } 431 432 public String fromTableFragment(String alias) { 433 return getTableName() + ' ' + alias; 434 } 435 436 public String getTableName() { 437 return tableNames[0]; 438 } 439 440 private static int getTableId(String tableName, String [] tables) { 441 for ( int j=0; j<tables.length; j++ ) { 442 if ( tableName.equals( tables[j] ) ) return j; 443 } 444 throw new AssertionFailure("table not found"); 445 } 446 447 public void addDiscriminatorToSelect(SelectFragment select, String name, String suffix) { 448 if ( hasSubclasses() ) { 449 select.setExtraSelectList( discriminatorFragment(name), getDiscriminatorAlias() ); 450 } 451 } 452 453 private CaseFragment discriminatorFragment(String alias) { 454 CaseFragment cases = getFactory().getDialect().createCaseFragment(); 455 456 for ( int i=0; i<discriminatorValues.length; i++ ) { 457 cases.addWhenColumnNotNull( 458 generateTableAlias( alias, notNullColumnTableNumbers[i] ), 459 notNullColumnNames[i], 460 discriminatorValues[i] 461 ); 462 } 463 464 return cases; 465 } 466 467 public String filterFragment(String alias) { 468 return hasWhere() ? 469 " and " + getSQLWhereString( generateFilterConditionAlias( alias ) ) : 470 ""; 471 } 472 473 protected String generateFilterConditionAlias(String rootAlias) { 474 return generateTableAlias( rootAlias, tableSpan-1 ); 475 } 476 477 public String [] getIdentifierColumnNames() { 478 return tableKeyColumns[0]; 479 } 480 481 public String [] toColumns(String alias, String propertyName) throws QueryException { 482 483 if ( ENTITY_CLASS.equals(propertyName) ) { 484 490 return new String [] { discriminatorFragment(alias).toFragmentString() }; 491 } 492 else { 493 return super.toColumns(alias, propertyName); 494 } 495 496 } 497 498 protected int[] getPropertyTableNumbersInSelect() { 499 return propertyTableNumbers; 500 } 501 502 protected int getSubclassPropertyTableNumber(int i) { 503 return subclassPropertyTableNumberClosure[i]; 504 } 505 506 public int getTableSpan() { 507 return tableSpan; 508 } 509 510 public boolean isMultiTable() { 511 return true; 512 } 513 514 protected int[] getSubclassColumnTableNumberClosure() { 515 return subclassColumnTableNumberClosure; 516 } 517 518 protected int[] getSubclassFormulaTableNumberClosure() { 519 return subclassFormulaTableNumberClosure; 520 } 521 522 protected int[] getPropertyTableNumbers() { 523 return naturalOrderPropertyTableNumbers; 524 } 525 526 protected String [] getSubclassTableKeyColumns(int j) { 527 return subclassTableKeyColumnClosure[j]; 528 } 529 530 public String getSubclassTableName(int j) { 531 return subclassTableNameClosure[j]; 532 } 533 534 public int getSubclassTableSpan() { 535 return subclassTableNameClosure.length; 536 } 537 538 protected boolean isClassOrSuperclassTable(int j) { 539 return isClassOrSuperclassTable[j]; 540 } 541 542 public String getPropertyTableName(String propertyName) { 543 Integer index = getEntityMetamodel().getPropertyIndexOrNull(propertyName); 544 if (index==null) return null; 545 return tableNames[ propertyTableNumbers[ index.intValue() ] ]; 546 } 547 548 public String [] getConstraintOrderedTableNameClosure() { 549 return constraintOrderedTableNames; 550 } 551 552 public String [][] getContraintOrderedTableKeyColumnClosure() { 553 return constraintOrderedKeyColumnNames; 554 } 555 } 556 | Popular Tags |