KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > persister > entity > JoinedSubclassEntityPersister


1 //$Id: JoinedSubclassEntityPersister.java,v 1.8 2005/07/21 01:11:52 oneovthafew Exp $
2
package org.hibernate.persister.entity;
3
4 import java.io.Serializable JavaDoc;
5 import java.util.ArrayList JavaDoc;
6 import java.util.HashMap JavaDoc;
7 import java.util.Iterator JavaDoc;
8 import java.util.Map JavaDoc;
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 /**
32  * An <tt>EntityPersister</tt> implementing the normalized "table-per-subclass"
33  * mapping strategy
34  *
35  * @author Gavin King
36  */

37 public class JoinedSubclassEntityPersister extends BasicEntityPersister {
38
39     // the class hierarchy structure
40
private final int tableSpan;
41     private final String JavaDoc[] tableNames;
42     private final String JavaDoc[] naturalOrderTableNames;
43     private final String JavaDoc[][] tableKeyColumns;
44     private final String JavaDoc[][] naturalOrderTableKeyColumns;
45     private final boolean[] naturalOrderCascadeDeleteEnabled;
46     
47     private final String JavaDoc[] spaces;
48
49     private final String JavaDoc[] subclassClosure;
50
51     private final String JavaDoc[] subclassTableNameClosure;
52     private final String JavaDoc[][] subclassTableKeyColumnClosure;
53     private final boolean[] isClassOrSuperclassTable;
54
55     // properties of this class, including inherited properties
56
private final int[] naturalOrderPropertyTableNumbers;
57     private final int[] propertyTableNumbers;
58
59     // the closure of all properties in the entire hierarchy including
60
// subclasses and superclasses of this class
61
private final int[] subclassPropertyTableNumberClosure;
62
63     // the closure of all columns used by the entire hierarchy including
64
// subclasses and superclasses of this class
65
private final int[] subclassColumnTableNumberClosure;
66     private final int[] subclassFormulaTableNumberClosure;
67
68     // subclass discrimination works by assigning particular
69
// values to certain combinations of null primary key
70
// values in the outer join using an SQL CASE
71
private final Map JavaDoc subclassesByDiscriminatorValue = new HashMap JavaDoc();
72     private final String JavaDoc[] discriminatorValues;
73     private final String JavaDoc[] notNullColumnNames;
74     private final int[] notNullColumnTableNumbers;
75
76     private final String JavaDoc[] constraintOrderedTableNames;
77     private final String JavaDoc[][] constraintOrderedKeyColumnNames;
78
79     private final String JavaDoc discriminatorSQLString;
80
81     //INITIALIZATION:
82

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         // DISCRIMINATOR
93

94         final Object JavaDoc discriminatorValue;
95         if ( persistentClass.isPolymorphic() ) {
96             try {
97                 discriminatorValue = new Integer JavaDoc( persistentClass.getSubclassId() );
98                 discriminatorSQLString = discriminatorValue.toString();
99             }
100             catch (Exception JavaDoc 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         //MULTITABLES
117

118         final int idColumnSpan = getIdentifierColumnSpan();
119
120         ArrayList JavaDoc tables = new ArrayList JavaDoc();
121         ArrayList JavaDoc keyColumns = new ArrayList JavaDoc();
122         ArrayList JavaDoc cascadeDeletes = new ArrayList JavaDoc();
123         Iterator JavaDoc titer = persistentClass.getTableClosureIterator();
124         Iterator JavaDoc kiter = persistentClass.getKeyClosureIterator();
125         while ( titer.hasNext() ) {
126             Table tab = (Table) titer.next();
127             KeyValue key = (KeyValue) kiter.next();
128             String JavaDoc tabname = tab.getQualifiedName(
129                     factory.getDialect(),
130                     factory.getSettings().getDefaultCatalogName(),
131                     factory.getSettings().getDefaultSchemaName()
132             );
133             tables.add(tabname);
134             String JavaDoc[] keyCols = new String JavaDoc[idColumnSpan];
135             Iterator JavaDoc 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 JavaDoc( key.isCascadeDeleteEnabled() && factory.getDialect().supportsCascadeDelete() ) );
141         }
142         naturalOrderTableNames = ArrayHelper.toStringArray(tables);
143         naturalOrderTableKeyColumns = ArrayHelper.to2DStringArray(keyColumns);
144         naturalOrderCascadeDeleteEnabled = ArrayHelper.toBooleanArray(cascadeDeletes);
145
146         ArrayList JavaDoc subtables = new ArrayList JavaDoc();
147         ArrayList JavaDoc isConcretes = new ArrayList JavaDoc();
148         keyColumns = new ArrayList JavaDoc();
149         titer = persistentClass.getSubclassTableClosureIterator();
150         while ( titer.hasNext() ) {
151             Table tab = (Table) titer.next();
152             isConcretes.add( new Boolean JavaDoc( persistentClass.isClassOrSuperclassTable(tab) ) );
153             String JavaDoc tabname = tab.getQualifiedName(
154                     factory.getDialect(),
155                     factory.getSettings().getDefaultCatalogName(),
156                     factory.getSettings().getDefaultSchemaName()
157             );
158             subtables.add(tabname);
159             String JavaDoc[] key = new String JavaDoc[idColumnSpan];
160             Iterator JavaDoc 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 JavaDoc[subclassTableNameClosure.length];
171         constraintOrderedKeyColumnNames = new String JavaDoc[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         // Custom sql
190
customSQLInsert = new String JavaDoc[tableSpan];
191         customSQLUpdate = new String JavaDoc[tableSpan];
192         customSQLDelete = new String JavaDoc[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         // PROPERTIES
212

213         int hydrateSpan = getPropertySpan();
214         naturalOrderPropertyTableNumbers = new int[hydrateSpan];
215         propertyTableNumbers = new int[hydrateSpan];
216         Iterator JavaDoc iter = persistentClass.getPropertyClosureIterator();
217         int i=0;
218         while( iter.hasNext() ) {
219             Property prop = (Property) iter.next();
220             String JavaDoc 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         // subclass closure properties
231

232         //TODO: code duplication with SingleTableEntityPersister
233

234         ArrayList JavaDoc columnTableNumbers = new ArrayList JavaDoc();
235         ArrayList JavaDoc formulaTableNumbers = new ArrayList JavaDoc();
236         ArrayList JavaDoc propTableNumbers = new ArrayList JavaDoc();
237         
238         iter = persistentClass.getSubclassPropertyClosureIterator();
239         while ( iter.hasNext() ) {
240             Property prop = (Property) iter.next();
241             Table tab = prop.getValue().getTable();
242             String JavaDoc tabname = tab.getQualifiedName(
243                     factory.getDialect(),
244                     factory.getSettings().getDefaultCatalogName(),
245                     factory.getSettings().getDefaultSchemaName()
246             );
247             Integer JavaDoc tabnum = new Integer JavaDoc( getTableId(tabname, subclassTableNameClosure) );
248             propTableNumbers.add(tabnum);
249
250             Iterator JavaDoc 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         // SUBCLASSES
268

269         int subclassSpan = persistentClass.getSubclassSpan() + 1;
270         subclassClosure = new String JavaDoc[subclassSpan];
271         subclassClosure[subclassSpan-1] = getEntityName();
272         if ( persistentClass.isPolymorphic() ) {
273             subclassesByDiscriminatorValue.put( discriminatorValue, getEntityName() );
274             discriminatorValues = new String JavaDoc[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 JavaDoc[subclassSpan];
287             notNullColumnNames[subclassSpan-1] = subclassTableKeyColumnClosure[id][0]; //( (Column) model.getTable().getPrimaryKey().getColumnIterator().next() ).getName();
288
}
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                     // we now use subclass ids that are consistent across all
303
// persisters for a class hierarchy, so that the use of
304
// "foo.class = Bar" works in HQL
305
Integer JavaDoc subclassId = new Integer JavaDoc( sc.getSubclassId() );//new Integer(k+1);
306
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]; //( (Column) sc.getTable().getPrimaryKey().getColumnIterator().next() ).getName();
318
}
319             }
320             catch (Exception JavaDoc 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     /*public void postInstantiate() throws MappingException {
335         super.postInstantiate();
336         //TODO: other lock modes?
337         loader = createEntityLoader(LockMode.NONE, CollectionHelper.EMPTY_MAP);
338     }*/

339     
340     public String JavaDoc getSubclassPropertyTableName(int i) {
341         return subclassTableNameClosure[ subclassPropertyTableNumberClosure[i] ];
342     }
343
344     public Type getDiscriminatorType() {
345         return Hibernate.INTEGER;
346     }
347
348     public String JavaDoc getDiscriminatorSQLValue() {
349         return discriminatorSQLString;
350     }
351
352
353     public String JavaDoc getSubclassForDiscriminatorValue(Object JavaDoc value) {
354         return (String JavaDoc) subclassesByDiscriminatorValue.get(value);
355     }
356
357     public Serializable JavaDoc[] getPropertySpaces() {
358         return spaces; // don't need subclass tables, because they can't appear in conditions
359
}
360
361
362     protected String JavaDoc getTableName(int j) {
363         return naturalOrderTableNames[j];
364     }
365     
366     protected String JavaDoc[] 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     /**
379      * Load an instance using either the <tt>forUpdateLoader</tt> or the outer joining <tt>loader</tt>,
380      * depending upon the value of the <tt>lock</tt> parameter
381      */

382     /*public Object load(Serializable id, Object optionalObject, LockMode lockMode, SessionImplementor session)
383     throws HibernateException {
384
385         if ( log.isTraceEnabled() ) log.trace( "Materializing entity: " + MessageHelper.infoString(this, id) );
386
387         final UniqueEntityLoader loader = hasQueryLoader() ?
388                 getQueryLoader() :
389                 this.loader;
390         try {
391             
392             final Object result = loader.load(id, optionalObject, session);
393
394             if (result!=null) lock(id, getVersion(result), result, lockMode, session);
395
396             return result;
397
398         }
399         catch (SQLException sqle) {
400             throw new JDBCException( "could not load by id: " + MessageHelper.infoString(this, id), sqle );
401         }
402     }*/

403
404     private static final void reverse(Object JavaDoc[] objects, int len) {
405         Object JavaDoc[] temp = new Object JavaDoc[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 JavaDoc[] reverse(String JavaDoc[] objects) {
415         int len = objects.length;
416         String JavaDoc[] temp = new String JavaDoc[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 JavaDoc[][] reverse(String JavaDoc[][] objects) {
424         int len = objects.length;
425         String JavaDoc[][] temp = new String JavaDoc[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 JavaDoc fromTableFragment(String JavaDoc alias) {
433         return getTableName() + ' ' + alias;
434     }
435
436     public String JavaDoc getTableName() {
437         return tableNames[0];
438     }
439     
440     private static int getTableId(String JavaDoc tableName, String JavaDoc[] 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 JavaDoc name, String JavaDoc suffix) {
448         if ( hasSubclasses() ) {
449             select.setExtraSelectList( discriminatorFragment(name), getDiscriminatorAlias() );
450         }
451     }
452
453     private CaseFragment discriminatorFragment(String JavaDoc 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 JavaDoc filterFragment(String JavaDoc alias) {
468         return hasWhere() ?
469             " and " + getSQLWhereString( generateFilterConditionAlias( alias ) ) :
470             "";
471     }
472
473     protected String JavaDoc generateFilterConditionAlias(String JavaDoc rootAlias) {
474         return generateTableAlias( rootAlias, tableSpan-1 );
475     }
476
477     public String JavaDoc[] getIdentifierColumnNames() {
478         return tableKeyColumns[0];
479     }
480
481     public String JavaDoc[] toColumns(String JavaDoc alias, String JavaDoc propertyName) throws QueryException {
482
483         if ( ENTITY_CLASS.equals(propertyName) ) {
484             // This doesn't actually seem to work but it *might*
485
// work on some dbs. Also it doesn't work if there
486
// are multiple columns of results because it
487
// is not accounting for the suffix:
488
// return new String[] { getDiscriminatorColumnName() };
489

490             return new String JavaDoc[] { 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 JavaDoc[] getSubclassTableKeyColumns(int j) {
527         return subclassTableKeyColumnClosure[j];
528     }
529
530     public String JavaDoc 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 JavaDoc getPropertyTableName(String JavaDoc propertyName) {
543         Integer JavaDoc index = getEntityMetamodel().getPropertyIndexOrNull(propertyName);
544         if (index==null) return null;
545         return tableNames[ propertyTableNumbers[ index.intValue() ] ];
546     }
547
548     public String JavaDoc[] getConstraintOrderedTableNameClosure() {
549         return constraintOrderedTableNames;
550     }
551
552     public String JavaDoc[][] getContraintOrderedTableKeyColumnClosure() {
553         return constraintOrderedKeyColumnNames;
554     }
555 }
556
Popular Tags