1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
5 * indicated by the @author tags or express copyright attribution
6 * statements applied by the authors. All third-party contributions are
7 * distributed under license by Red Hat Middleware LLC.
8 *
9 * This copyrighted material is made available to anyone wishing to use, modify,
10 * copy, or redistribute it subject to the terms and conditions of the GNU
11 * Lesser General Public License, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this distribution; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301 USA
23 *
24 */
25 package org.hibernate.persister.entity;
26
27 import java.io.Serializable;
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.Iterator;
32 import java.util.Map;
33
34 import org.hibernate.EntityMode;
35 import org.hibernate.HibernateException;
36 import org.hibernate.MappingException;
37 import org.hibernate.cache.access.EntityRegionAccessStrategy;
38 import org.hibernate.engine.Mapping;
39 import org.hibernate.engine.SessionFactoryImplementor;
40 import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
41 import org.hibernate.mapping.Column;
42 import org.hibernate.mapping.Formula;
43 import org.hibernate.mapping.Join;
44 import org.hibernate.mapping.PersistentClass;
45 import org.hibernate.mapping.Property;
46 import org.hibernate.mapping.Selectable;
47 import org.hibernate.mapping.Subclass;
48 import org.hibernate.mapping.Table;
49 import org.hibernate.mapping.Value;
50 import org.hibernate.sql.InFragment;
51 import org.hibernate.sql.Insert;
52 import org.hibernate.sql.SelectFragment;
53 import org.hibernate.type.AssociationType;
54 import org.hibernate.type.DiscriminatorType;
55 import org.hibernate.type.Type;
56 import org.hibernate.util.ArrayHelper;
57 import org.hibernate.util.MarkerObject;
58
59 /**
60 * The default implementation of the <tt>EntityPersister</tt> interface.
61 * Implements the "table-per-class-hierarchy" or "roll-up" mapping strategy
62 * for an entity class and its inheritence hierarchy. This is implemented
63 * as a single table holding all classes in the hierarchy with a discrimator
64 * column used to determine which concrete class is referenced.
65 *
66 * @author Gavin King
67 */
68 public class SingleTableEntityPersister extends AbstractEntityPersister {
69
70 // the class hierarchy structure
71 private final int joinSpan;
72 private final String[] qualifiedTableNames;
73 private final boolean[] isInverseTable;
74 private final boolean[] isNullableTable;
75 private final String[][] keyColumnNames;
76 private final boolean[] cascadeDeleteEnabled;
77 private final boolean hasSequentialSelects;
78
79 private final String[] spaces;
80
81 private final String[] subclassClosure;
82
83 private final String[] subclassTableNameClosure;
84 private final boolean[] subclassTableIsLazyClosure;
85 private final boolean[] isInverseSubclassTable;
86 private final boolean[] isNullableSubclassTable;
87 private final boolean[] subclassTableSequentialSelect;
88 private final String[][] subclassTableKeyColumnClosure;
89 private final boolean[] isClassOrSuperclassTable;
90
91 // properties of this class, including inherited properties
92 private final int[] propertyTableNumbers;
93
94 // the closure of all columns used by the entire hierarchy including
95 // subclasses and superclasses of this class
96 private final int[] subclassPropertyTableNumberClosure;
97
98 private final int[] subclassColumnTableNumberClosure;
99 private final int[] subclassFormulaTableNumberClosure;
100
101 // discriminator column
102 private final Map subclassesByDiscriminatorValue = new HashMap();
103 private final boolean forceDiscriminator;
104 private final String discriminatorColumnName;
105 private final String discriminatorFormula;
106 private final String discriminatorFormulaTemplate;
107 private final String discriminatorAlias;
108 private final Type discriminatorType;
109 private final String discriminatorSQLValue;
110 private final boolean discriminatorInsertable;
111
112 private final String[] constraintOrderedTableNames;
113 private final String[][] constraintOrderedKeyColumnNames;
114
115 //private final Map propertyTableNumbersByName = new HashMap();
116 private final Map propertyTableNumbersByNameAndSubclass = new HashMap();
117
118 private final Map sequentialSelectStringsByEntityName = new HashMap();
119
120 private static final Object NULL_DISCRIMINATOR = new MarkerObject("<null discriminator>");
121 private static final Object NOT_NULL_DISCRIMINATOR = new MarkerObject("<not null discriminator>");
122
123 //INITIALIZATION:
124
125 public SingleTableEntityPersister(
126 final PersistentClass persistentClass,
127 final EntityRegionAccessStrategy cacheAccessStrategy,
128 final SessionFactoryImplementor factory,
129 final Mapping mapping) throws HibernateException {
130
131 super( persistentClass, cacheAccessStrategy, factory );
132
133 // CLASS + TABLE
134
135 joinSpan = persistentClass.getJoinClosureSpan()+1;
136 qualifiedTableNames = new String[joinSpan];
137 isInverseTable = new boolean[joinSpan];
138 isNullableTable = new boolean[joinSpan];
139 keyColumnNames = new String[joinSpan][];
140 final Table table = persistentClass.getRootTable();
141 qualifiedTableNames[0] = table.getQualifiedName(
142 factory.getDialect(),
143 factory.getSettings().getDefaultCatalogName(),
144 factory.getSettings().getDefaultSchemaName()
145 );
146 isInverseTable[0] = false;
147 isNullableTable[0] = false;
148 keyColumnNames[0] = getIdentifierColumnNames();
149 cascadeDeleteEnabled = new boolean[joinSpan];
150
151 // Custom sql
152 customSQLInsert = new String[joinSpan];
153 customSQLUpdate = new String[joinSpan];
154 customSQLDelete = new String[joinSpan];
155 insertCallable = new boolean[joinSpan];
156 updateCallable = new boolean[joinSpan];
157 deleteCallable = new boolean[joinSpan];
158 insertResultCheckStyles = new ExecuteUpdateResultCheckStyle[joinSpan];
159 updateResultCheckStyles = new ExecuteUpdateResultCheckStyle[joinSpan];
160 deleteResultCheckStyles = new ExecuteUpdateResultCheckStyle[joinSpan];
161
162 customSQLInsert[0] = persistentClass.getCustomSQLInsert();
163 insertCallable[0] = customSQLInsert[0] != null && persistentClass.isCustomInsertCallable();
164 insertResultCheckStyles[0] = persistentClass.getCustomSQLInsertCheckStyle() == null
165 ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLInsert[0], insertCallable[0] )
166 : persistentClass.getCustomSQLInsertCheckStyle();
167 customSQLUpdate[0] = persistentClass.getCustomSQLUpdate();
168 updateCallable[0] = customSQLUpdate[0] != null && persistentClass.isCustomUpdateCallable();
169 updateResultCheckStyles[0] = persistentClass.getCustomSQLUpdateCheckStyle() == null
170 ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLUpdate[0], updateCallable[0] )
171 : persistentClass.getCustomSQLUpdateCheckStyle();
172 customSQLDelete[0] = persistentClass.getCustomSQLDelete();
173 deleteCallable[0] = customSQLDelete[0] != null && persistentClass.isCustomDeleteCallable();
174 deleteResultCheckStyles[0] = persistentClass.getCustomSQLDeleteCheckStyle() == null
175 ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLDelete[0], deleteCallable[0] )
176 : persistentClass.getCustomSQLDeleteCheckStyle();
177
178 // JOINS
179
180 Iterator joinIter = persistentClass.getJoinClosureIterator();
181 int j = 1;
182 while ( joinIter.hasNext() ) {
183 Join join = (Join) joinIter.next();
184 qualifiedTableNames[j] = join.getTable().getQualifiedName(
185 factory.getDialect(),
186 factory.getSettings().getDefaultCatalogName(),
187 factory.getSettings().getDefaultSchemaName()
188 );
189 isInverseTable[j] = join.isInverse();
190 isNullableTable[j] = join.isOptional();
191 cascadeDeleteEnabled[j] = join.getKey().isCascadeDeleteEnabled() &&
192 factory.getDialect().supportsCascadeDelete();
193
194 customSQLInsert[j] = join.getCustomSQLInsert();
195 insertCallable[j] = customSQLInsert[j] != null && join.isCustomInsertCallable();
196 insertResultCheckStyles[j] = join.getCustomSQLInsertCheckStyle() == null
197 ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLInsert[j], insertCallable[j] )
198 : join.getCustomSQLInsertCheckStyle();
199 customSQLUpdate[j] = join.getCustomSQLUpdate();
200 updateCallable[j] = customSQLUpdate[j] != null && join.isCustomUpdateCallable();
201 updateResultCheckStyles[j] = join.getCustomSQLUpdateCheckStyle() == null
202 ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLUpdate[j], updateCallable[j] )
203 : join.getCustomSQLUpdateCheckStyle();
204 customSQLDelete[j] = join.getCustomSQLDelete();
205 deleteCallable[j] = customSQLDelete[j] != null && join.isCustomDeleteCallable();
206 deleteResultCheckStyles[j] = join.getCustomSQLDeleteCheckStyle() == null
207 ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLDelete[j], deleteCallable[j] )
208 : join.getCustomSQLDeleteCheckStyle();
209
210 Iterator iter = join.getKey().getColumnIterator();
211 keyColumnNames[j] = new String[ join.getKey().getColumnSpan() ];
212 int i = 0;
213 while ( iter.hasNext() ) {
214 Column col = (Column) iter.next();
215 keyColumnNames[j][i++] = col.getQuotedName( factory.getDialect() );
216 }
217
218 j++;
219 }
220
221 constraintOrderedTableNames = new String[qualifiedTableNames.length];
222 constraintOrderedKeyColumnNames = new String[qualifiedTableNames.length][];
223 for ( int i = qualifiedTableNames.length - 1, position = 0; i >= 0; i--, position++ ) {
224 constraintOrderedTableNames[position] = qualifiedTableNames[i];
225 constraintOrderedKeyColumnNames[position] = keyColumnNames[i];
226 }
227
228 spaces = ArrayHelper.join(
229 qualifiedTableNames,
230 ArrayHelper.toStringArray( persistentClass.getSynchronizedTables() )
231 );
232
233 final boolean lazyAvailable = isInstrumented(EntityMode.POJO);
234
235 boolean hasDeferred = false;
236 ArrayList subclassTables = new ArrayList();
237 ArrayList joinKeyColumns = new ArrayList();
238 ArrayList isConcretes = new ArrayList();
239 ArrayList isDeferreds = new ArrayList();
240 ArrayList isInverses = new ArrayList();
241 ArrayList isNullables = new ArrayList();
242 ArrayList isLazies = new ArrayList();
243 subclassTables.add( qualifiedTableNames[0] );
244 joinKeyColumns.add( getIdentifierColumnNames() );
245 isConcretes.add(Boolean.TRUE);
246 isDeferreds.add(Boolean.FALSE);
247 isInverses.add(Boolean.FALSE);
248 isNullables.add(Boolean.FALSE);
249 isLazies.add(Boolean.FALSE);
250 joinIter = persistentClass.getSubclassJoinClosureIterator();
251 while ( joinIter.hasNext() ) {
252 Join join = (Join) joinIter.next();
253 isConcretes.add( new Boolean( persistentClass.isClassOrSuperclassJoin(join) ) );
254 isDeferreds.add( new Boolean( join.isSequentialSelect() ) );
255 isInverses.add( new Boolean( join.isInverse() ) );
256 isNullables.add( new Boolean( join.isOptional() ) );
257 isLazies.add( new Boolean( lazyAvailable && join.isLazy() ) );
258 if ( join.isSequentialSelect() && !persistentClass.isClassOrSuperclassJoin(join) ) hasDeferred = true;
259 subclassTables.add( join.getTable().getQualifiedName(
260 factory.getDialect(),
261 factory.getSettings().getDefaultCatalogName(),
262 factory.getSettings().getDefaultSchemaName()
263 ) );
264 Iterator iter = join.getKey().getColumnIterator();
265 String[] keyCols = new String[ join.getKey().getColumnSpan() ];
266 int i = 0;
267 while ( iter.hasNext() ) {
268 Column col = (Column) iter.next();
269 keyCols[i++] = col.getQuotedName( factory.getDialect() );
270 }
271 joinKeyColumns.add(keyCols);
272 }
273
274 subclassTableSequentialSelect = ArrayHelper.toBooleanArray(isDeferreds);
275 subclassTableNameClosure = ArrayHelper.toStringArray(subclassTables);
276 subclassTableIsLazyClosure = ArrayHelper.toBooleanArray(isLazies);
277 subclassTableKeyColumnClosure = ArrayHelper.to2DStringArray(joinKeyColumns);
278 isClassOrSuperclassTable = ArrayHelper.toBooleanArray(isConcretes);
279 isInverseSubclassTable = ArrayHelper.toBooleanArray(isInverses);
280 isNullableSubclassTable = ArrayHelper.toBooleanArray(isNullables);
281 hasSequentialSelects = hasDeferred;
282
283 // DISCRIMINATOR
284
285 final Object discriminatorValue;
286 if ( persistentClass.isPolymorphic() ) {
287 Value discrimValue = persistentClass.getDiscriminator();
288 if (discrimValue==null) {
289 throw new MappingException("discriminator mapping required for single table polymorphic persistence");
290 }
291 forceDiscriminator = persistentClass.isForceDiscriminator();
292 Selectable selectable = (Selectable) discrimValue.getColumnIterator().next();
293 if ( discrimValue.hasFormula() ) {
294 Formula formula = (Formula) selectable;
295 discriminatorFormula = formula.getFormula();
296 discriminatorFormulaTemplate = formula.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
297 discriminatorColumnName = null;
298 discriminatorAlias = "clazz_";
299 }
300 else {
301 Column column = (Column) selectable;
302 discriminatorColumnName = column.getQuotedName( factory.getDialect() );
303 discriminatorAlias = column.getAlias( factory.getDialect(), persistentClass.getRootTable() );
304 discriminatorFormula = null;
305 discriminatorFormulaTemplate = null;
306 }
307 discriminatorType = persistentClass.getDiscriminator().getType();
308 if ( persistentClass.isDiscriminatorValueNull() ) {
309 discriminatorValue = NULL_DISCRIMINATOR;
310 discriminatorSQLValue = InFragment.NULL;
311 discriminatorInsertable = false;
312 }
313 else if ( persistentClass.isDiscriminatorValueNotNull() ) {
314 discriminatorValue = NOT_NULL_DISCRIMINATOR;
315 discriminatorSQLValue = InFragment.NOT_NULL;
316 discriminatorInsertable = false;
317 }
318 else {
319 discriminatorInsertable = persistentClass.isDiscriminatorInsertable() && !discrimValue.hasFormula();
320 try {
321 DiscriminatorType dtype = (DiscriminatorType) discriminatorType;
322 discriminatorValue = dtype.stringToObject( persistentClass.getDiscriminatorValue() );
323 discriminatorSQLValue = dtype.objectToSQLString( discriminatorValue, factory.getDialect() );
324 }
325 catch (ClassCastException cce) {
326 throw new MappingException("Illegal discriminator type: " + discriminatorType.getName() );
327 }
328 catch (Exception e) {
329 throw new MappingException("Could not format discriminator value to SQL string", e);
330 }
331 }
332 }
333 else {
334 forceDiscriminator = false;
335 discriminatorInsertable = false;
336 discriminatorColumnName = null;
337 discriminatorAlias = null;
338 discriminatorType = null;
339 discriminatorValue = null;
340 discriminatorSQLValue = null;
341 discriminatorFormula = null;
342 discriminatorFormulaTemplate = null;
343 }
344
345 // PROPERTIES
346
347 propertyTableNumbers = new int[ getPropertySpan() ];
348 Iterator iter = persistentClass.getPropertyClosureIterator();
349 int i=0;
350 while( iter.hasNext() ) {
351 Property prop = (Property) iter.next();
352 propertyTableNumbers[i++] = persistentClass.getJoinNumber(prop);
353
354 }
355
356 //TODO: code duplication with JoinedSubclassEntityPersister
357
358 ArrayList columnJoinNumbers = new ArrayList();
359 ArrayList formulaJoinedNumbers = new ArrayList();
360 ArrayList propertyJoinNumbers = new ArrayList();
361
362 iter = persistentClass.getSubclassPropertyClosureIterator();
363 while ( iter.hasNext() ) {
364 Property prop = (Property) iter.next();
365 Integer join = new Integer( persistentClass.getJoinNumber(prop) );
366 propertyJoinNumbers.add(join);
367
368 //propertyTableNumbersByName.put( prop.getName(), join );
369 propertyTableNumbersByNameAndSubclass.put(
370 prop.getPersistentClass().getEntityName() + '.' + prop.getName(),
371 join
372 );
373
374 Iterator citer = prop.getColumnIterator();
375 while ( citer.hasNext() ) {
376 Selectable thing = (Selectable) citer.next();
377 if ( thing.isFormula() ) {
378 formulaJoinedNumbers.add(join);
379 }
380 else {
381 columnJoinNumbers.add(join);
382 }
383 }
384 }
385 subclassColumnTableNumberClosure = ArrayHelper.toIntArray(columnJoinNumbers);
386 subclassFormulaTableNumberClosure = ArrayHelper.toIntArray(formulaJoinedNumbers);
387 subclassPropertyTableNumberClosure = ArrayHelper.toIntArray(propertyJoinNumbers);
388
389 int subclassSpan = persistentClass.getSubclassSpan() + 1;
390 subclassClosure = new String[subclassSpan];
391 subclassClosure[0] = getEntityName();
392 if ( persistentClass.isPolymorphic() ) {
393 subclassesByDiscriminatorValue.put( discriminatorValue, getEntityName() );
394 }
395
396 // SUBCLASSES
397 if ( persistentClass.isPolymorphic() ) {
398 iter = persistentClass.getSubclassIterator();
399 int k=1;
400 while ( iter.hasNext() ) {
401 Subclass sc = (Subclass) iter.next();
402 subclassClosure[k++] = sc.getEntityName();
403 if ( sc.isDiscriminatorValueNull() ) {
404 subclassesByDiscriminatorValue.put( NULL_DISCRIMINATOR, sc.getEntityName() );
405 }
406 else if ( sc.isDiscriminatorValueNotNull() ) {
407 subclassesByDiscriminatorValue.put( NOT_NULL_DISCRIMINATOR, sc.getEntityName() );
408 }
409 else {
410 try {
411 DiscriminatorType dtype = (DiscriminatorType) discriminatorType;
412 subclassesByDiscriminatorValue.put(
413 dtype.stringToObject( sc.getDiscriminatorValue() ),
414 sc.getEntityName()
415 );
416 }
417 catch (ClassCastException cce) {
418 throw new MappingException("Illegal discriminator type: " + discriminatorType.getName() );
419 }
420 catch (Exception e) {
421 throw new MappingException("Error parsing discriminator value", e);
422 }
423 }
424 }
425 }
426
427 initLockers();
428
429 initSubclassPropertyAliasesMap(persistentClass);
430
431 postConstruct(mapping);
432
433 }
434
435 protected boolean isInverseTable(int j) {
436 return isInverseTable[j];
437 }
438
439 protected boolean isInverseSubclassTable(int j) {
440 return isInverseSubclassTable[j];
441 }
442
443 public String getDiscriminatorColumnName() {
444 return discriminatorColumnName;
445 }
446
447 protected String getDiscriminatorAlias() {
448 return discriminatorAlias;
449 }
450
451 protected String getDiscriminatorFormulaTemplate() {
452 return discriminatorFormulaTemplate;
453 }
454
455 public String getTableName() {
456 return qualifiedTableNames[0];
457 }
458
459 public Type getDiscriminatorType() {
460 return discriminatorType;
461 }
462
463 public String getDiscriminatorSQLValue() {
464 return discriminatorSQLValue;
465 }
466
467 public String[] getSubclassClosure() {
468 return subclassClosure;
469 }
470
471 public String getSubclassForDiscriminatorValue(Object value) {
472 if (value==null) {
473 return (String) subclassesByDiscriminatorValue.get(NULL_DISCRIMINATOR);
474 }
475 else {
476 String result = (String) subclassesByDiscriminatorValue.get(value);
477 if (result==null) result = (String) subclassesByDiscriminatorValue.get(NOT_NULL_DISCRIMINATOR);
478 return result;
479 }
480 }
481
482 public Serializable[] getPropertySpaces() {
483 return spaces;
484 }
485
486 //Access cached SQL
487
488 protected boolean isDiscriminatorFormula() {
489 return discriminatorColumnName==null;
490 }
491
492 protected String getDiscriminatorFormula() {
493 return discriminatorFormula;
494 }
495
496 protected String getTableName(int j) {
497 return qualifiedTableNames[j];
498 }
499
500 protected String[] getKeyColumns(int j) {
501 return keyColumnNames[j];
502 }
503
504 protected boolean isTableCascadeDeleteEnabled(int j) {
505 return cascadeDeleteEnabled[j];
506 }
507
508 protected boolean isPropertyOfTable(int property, int j) {
509 return propertyTableNumbers[property]==j;
510 }
511
512 protected boolean isSubclassTableSequentialSelect(int j) {
513 return subclassTableSequentialSelect[j] && !isClassOrSuperclassTable[j];
514 }
515
516 // Execute the SQL:
517
518 public String fromTableFragment(String name) {
519 return getTableName() + ' ' + name;
520 }
521
522 public String filterFragment(String alias) throws MappingException {
523 String result = discriminatorFilterFragment(alias);
524 if ( hasWhere() ) result += " and " + getSQLWhereString(alias);
525 return result;
526 }
527
528 public String oneToManyFilterFragment(String alias) throws MappingException {
529 return forceDiscriminator ?
530 discriminatorFilterFragment(alias) :
531 "";
532 }
533
534 private String discriminatorFilterFragment(String alias) throws MappingException {
535 if ( needsDiscriminator() ) {
536 InFragment frag = new InFragment();
537
538 if ( isDiscriminatorFormula() ) {
539 frag.setFormula( alias, getDiscriminatorFormulaTemplate() );
540 }
541 else {
542 frag.setColumn( alias, getDiscriminatorColumnName() );
543 }
544
545 String[] subclasses = getSubclassClosure();
546 for ( int i=0; i<subclasses.length; i++ ) {
547 final Queryable queryable = (Queryable) getFactory().getEntityPersister( subclasses[i] );
548 if ( !queryable.isAbstract() ) frag.addValue( queryable.getDiscriminatorSQLValue() );
549 }
550
551 StringBuffer buf = new StringBuffer(50)
552 .append(" and ")
553 .append( frag.toFragmentString() );
554
555 return buf.toString();
556 }
557 else {
558 return "";
559 }
560 }
561
562 private boolean needsDiscriminator() {
563 return forceDiscriminator || isInherited();
564 }
565
566 public String getSubclassPropertyTableName(int i) {
567 return subclassTableNameClosure[ subclassPropertyTableNumberClosure[i] ];
568 }
569
570 protected void addDiscriminatorToSelect(SelectFragment select, String name, String suffix) {
571 if ( isDiscriminatorFormula() ) {
572 select.addFormula( name, getDiscriminatorFormulaTemplate(), getDiscriminatorAlias() );
573 }
574 else {
575 select.addColumn( name, getDiscriminatorColumnName(), getDiscriminatorAlias() );
576 }
577 }
578
579 protected int[] getPropertyTableNumbersInSelect() {
580 return propertyTableNumbers;
581 }
582
583 protected int getSubclassPropertyTableNumber(int i) {
584 return subclassPropertyTableNumberClosure[i];
585 }
586
587 public int getTableSpan() {
588 return joinSpan;
589 }
590
591 protected void addDiscriminatorToInsert(Insert insert) {
592
593 if (discriminatorInsertable) {
594 insert.addColumn( getDiscriminatorColumnName(), discriminatorSQLValue );
595 }
596
597 }
598
599 protected int[] getSubclassColumnTableNumberClosure() {
600 return subclassColumnTableNumberClosure;
601 }
602
603 protected int[] getSubclassFormulaTableNumberClosure() {
604 return subclassFormulaTableNumberClosure;
605 }
606
607 protected int[] getPropertyTableNumbers() {
608 return propertyTableNumbers;
609 }
610
611 protected boolean isSubclassPropertyDeferred(String propertyName, String entityName) {
612 return hasSequentialSelects &&
613 isSubclassTableSequentialSelect( getSubclassPropertyTableNumber(propertyName, entityName) );
614 }
615
616 public boolean hasSequentialSelect() {
617 return hasSequentialSelects;
618 }
619
620 private int getSubclassPropertyTableNumber(String propertyName, String entityName) {
621 Type type = propertyMapping.toType(propertyName);
622 if ( type.isAssociationType() && ( (AssociationType) type ).useLHSPrimaryKey() ) return 0;
623 final Integer tabnum = (Integer) propertyTableNumbersByNameAndSubclass.get(entityName + '.' + propertyName);
624 return tabnum==null ? 0 : tabnum.intValue();
625 }
626
627 protected String getSequentialSelect(String entityName) {
628 return (String) sequentialSelectStringsByEntityName.get(entityName);
629 }
630
631 private String generateSequentialSelect(Loadable persister) {
632 //if ( this==persister || !hasSequentialSelects ) return null;
633
634 //note that this method could easily be moved up to BasicEntityPersister,
635 //if we ever needed to reuse it from other subclasses
636
637 //figure out which tables need to be fetched
638 AbstractEntityPersister subclassPersister = (AbstractEntityPersister) persister;
639 HashSet tableNumbers = new HashSet();
640 String[] props = subclassPersister.getPropertyNames();
641 String[] classes = subclassPersister.getPropertySubclassNames();
642 for ( int i=0; i<props.length; i++ ) {
643 int propTableNumber = getSubclassPropertyTableNumber( props[i], classes[i] );
644 if ( isSubclassTableSequentialSelect(propTableNumber) && !isSubclassTableLazy(propTableNumber) ) {
645 tableNumbers.add( new Integer(propTableNumber) );
646 }
647 }
648 if ( tableNumbers.isEmpty() ) return null;
649
650 //figure out which columns are needed
651 ArrayList columnNumbers = new ArrayList();
652 final int[] columnTableNumbers = getSubclassColumnTableNumberClosure();
653 for ( int i=0; i<getSubclassColumnClosure().length; i++ ) {
654 if ( tableNumbers.contains( new Integer( columnTableNumbers[i] ) ) ) {
655 columnNumbers.add( new Integer(i) );
656 }
657 }
658
659 //figure out which formulas are needed
660 ArrayList formulaNumbers = new ArrayList();
661 final int[] formulaTableNumbers = getSubclassColumnTableNumberClosure();
662 for ( int i=0; i<getSubclassFormulaTemplateClosure().length; i++ ) {
663 if ( tableNumbers.contains( new Integer( formulaTableNumbers[i] ) ) ) {
664 formulaNumbers.add( new Integer(i) );
665 }
666 }
667
668 //render the SQL
669 return renderSelect(
670 ArrayHelper.toIntArray(tableNumbers),
671 ArrayHelper.toIntArray(columnNumbers),
672 ArrayHelper.toIntArray(formulaNumbers)
673 );
674 }
675
676
677 protected String[] getSubclassTableKeyColumns(int j) {
678 return subclassTableKeyColumnClosure[j];
679 }
680
681 public String getSubclassTableName(int j) {
682 return subclassTableNameClosure[j];
683 }
684
685 public int getSubclassTableSpan() {
686 return subclassTableNameClosure.length;
687 }
688
689 protected boolean isClassOrSuperclassTable(int j) {
690 return isClassOrSuperclassTable[j];
691 }
692
693 protected boolean isSubclassTableLazy(int j) {
694 return subclassTableIsLazyClosure[j];
695 }
696
697 protected boolean isNullableTable(int j) {
698 return isNullableTable[j];
699 }
700
701 protected boolean isNullableSubclassTable(int j) {
702 return isNullableSubclassTable[j];
703 }
704
705 public String getPropertyTableName(String propertyName) {
706 Integer index = getEntityMetamodel().getPropertyIndexOrNull(propertyName);
707 if (index==null) return null;
708 return qualifiedTableNames[ propertyTableNumbers[ index.intValue() ] ];
709 }
710
711 public void postInstantiate() {
712 super.postInstantiate();
713 if (hasSequentialSelects) {
714 String[] entityNames = getSubclassClosure();
715 for ( int i=1; i<entityNames.length; i++ ) {
716 Loadable loadable = (Loadable) getFactory().getEntityPersister( entityNames[i] );
717 if ( !loadable.isAbstract() ) { //perhaps not really necessary...
718 String sequentialSelect = generateSequentialSelect(loadable);
719 sequentialSelectStringsByEntityName.put( entityNames[i], sequentialSelect );
720 }
721 }
722 }
723 }
724
725 public boolean isMultiTable() {
726 return getTableSpan() > 1;
727 }
728
729 public String[] getConstraintOrderedTableNameClosure() {
730 return constraintOrderedTableNames;
731 }
732
733 public String[][] getContraintOrderedTableKeyColumnClosure() {
734 return constraintOrderedKeyColumnNames;
735 }
736 }