001: //$Id: SingleTableEntityPersister.java 10040 2006-06-22 19:51:43Z steve.ebersole@jboss.com $
002: package org.hibernate.persister.entity;
003:
004: import java.io.Serializable;
005: import java.util.ArrayList;
006: import java.util.HashMap;
007: import java.util.HashSet;
008: import java.util.Iterator;
009: import java.util.Map;
010:
011: import org.hibernate.EntityMode;
012: import org.hibernate.HibernateException;
013: import org.hibernate.MappingException;
014: import org.hibernate.cache.CacheConcurrencyStrategy;
015: import org.hibernate.engine.Mapping;
016: import org.hibernate.engine.SessionFactoryImplementor;
017: import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
018: import org.hibernate.mapping.Column;
019: import org.hibernate.mapping.Formula;
020: import org.hibernate.mapping.Join;
021: import org.hibernate.mapping.PersistentClass;
022: import org.hibernate.mapping.Property;
023: import org.hibernate.mapping.Selectable;
024: import org.hibernate.mapping.Subclass;
025: import org.hibernate.mapping.Table;
026: import org.hibernate.mapping.Value;
027: import org.hibernate.sql.InFragment;
028: import org.hibernate.sql.Insert;
029: import org.hibernate.sql.SelectFragment;
030: import org.hibernate.type.AssociationType;
031: import org.hibernate.type.DiscriminatorType;
032: import org.hibernate.type.Type;
033: import org.hibernate.util.ArrayHelper;
034: import org.hibernate.util.MarkerObject;
035:
036: /**
037: * The default implementation of the <tt>EntityPersister</tt> interface.
038: * Implements the "table-per-class-hierarchy" or "roll-up" mapping strategy
039: * for an entity class and its inheritence hierarchy. This is implemented
040: * as a single table holding all classes in the hierarchy with a discrimator
041: * column used to determine which concrete class is referenced.
042: *
043: * @author Gavin King
044: */
045: public class SingleTableEntityPersister extends AbstractEntityPersister {
046:
047: // the class hierarchy structure
048: private final int joinSpan;
049: private final String[] qualifiedTableNames;
050: private final boolean[] isInverseTable;
051: private final boolean[] isNullableTable;
052: private final String[][] keyColumnNames;
053: private final boolean[] cascadeDeleteEnabled;
054: private final boolean hasSequentialSelects;
055:
056: private final String[] spaces;
057:
058: private final String[] subclassClosure;
059:
060: private final String[] subclassTableNameClosure;
061: private final boolean[] subclassTableIsLazyClosure;
062: private final boolean[] isInverseSubclassTable;
063: private final boolean[] isNullableSubclassTable;
064: private final boolean[] subclassTableSequentialSelect;
065: private final String[][] subclassTableKeyColumnClosure;
066: private final boolean[] isClassOrSuperclassTable;
067:
068: // properties of this class, including inherited properties
069: private final int[] propertyTableNumbers;
070:
071: // the closure of all columns used by the entire hierarchy including
072: // subclasses and superclasses of this class
073: private final int[] subclassPropertyTableNumberClosure;
074:
075: private final int[] subclassColumnTableNumberClosure;
076: private final int[] subclassFormulaTableNumberClosure;
077:
078: // discriminator column
079: private final Map subclassesByDiscriminatorValue = new HashMap();
080: private final boolean forceDiscriminator;
081: private final String discriminatorColumnName;
082: private final String discriminatorFormula;
083: private final String discriminatorFormulaTemplate;
084: private final String discriminatorAlias;
085: private final Type discriminatorType;
086: private final String discriminatorSQLValue;
087: private final boolean discriminatorInsertable;
088:
089: private final String[] constraintOrderedTableNames;
090: private final String[][] constraintOrderedKeyColumnNames;
091:
092: //private final Map propertyTableNumbersByName = new HashMap();
093: private final Map propertyTableNumbersByNameAndSubclass = new HashMap();
094:
095: private final Map sequentialSelectStringsByEntityName = new HashMap();
096:
097: private static final Object NULL_DISCRIMINATOR = new MarkerObject(
098: "<null discriminator>");
099: private static final Object NOT_NULL_DISCRIMINATOR = new MarkerObject(
100: "<not null discriminator>");
101:
102: //INITIALIZATION:
103:
104: public SingleTableEntityPersister(
105: final PersistentClass persistentClass,
106: final CacheConcurrencyStrategy cache,
107: final SessionFactoryImplementor factory,
108: final Mapping mapping) throws HibernateException {
109:
110: super (persistentClass, cache, factory);
111:
112: // CLASS + TABLE
113:
114: joinSpan = persistentClass.getJoinClosureSpan() + 1;
115: qualifiedTableNames = new String[joinSpan];
116: isInverseTable = new boolean[joinSpan];
117: isNullableTable = new boolean[joinSpan];
118: keyColumnNames = new String[joinSpan][];
119: final Table table = persistentClass.getRootTable();
120: qualifiedTableNames[0] = table.getQualifiedName(factory
121: .getDialect(), factory.getSettings()
122: .getDefaultCatalogName(), factory.getSettings()
123: .getDefaultSchemaName());
124: isInverseTable[0] = false;
125: isNullableTable[0] = false;
126: keyColumnNames[0] = getIdentifierColumnNames();
127: cascadeDeleteEnabled = new boolean[joinSpan];
128:
129: // Custom sql
130: customSQLInsert = new String[joinSpan];
131: customSQLUpdate = new String[joinSpan];
132: customSQLDelete = new String[joinSpan];
133: insertCallable = new boolean[joinSpan];
134: updateCallable = new boolean[joinSpan];
135: deleteCallable = new boolean[joinSpan];
136: insertResultCheckStyles = new ExecuteUpdateResultCheckStyle[joinSpan];
137: updateResultCheckStyles = new ExecuteUpdateResultCheckStyle[joinSpan];
138: deleteResultCheckStyles = new ExecuteUpdateResultCheckStyle[joinSpan];
139:
140: customSQLInsert[0] = persistentClass.getCustomSQLInsert();
141: insertCallable[0] = customSQLInsert[0] != null
142: && persistentClass.isCustomInsertCallable();
143: insertResultCheckStyles[0] = persistentClass
144: .getCustomSQLInsertCheckStyle() == null ? ExecuteUpdateResultCheckStyle
145: .determineDefault(customSQLInsert[0], insertCallable[0])
146: : persistentClass.getCustomSQLInsertCheckStyle();
147: customSQLUpdate[0] = persistentClass.getCustomSQLUpdate();
148: updateCallable[0] = customSQLUpdate[0] != null
149: && persistentClass.isCustomUpdateCallable();
150: updateResultCheckStyles[0] = persistentClass
151: .getCustomSQLUpdateCheckStyle() == null ? ExecuteUpdateResultCheckStyle
152: .determineDefault(customSQLUpdate[0], updateCallable[0])
153: : persistentClass.getCustomSQLUpdateCheckStyle();
154: customSQLDelete[0] = persistentClass.getCustomSQLDelete();
155: deleteCallable[0] = customSQLDelete[0] != null
156: && persistentClass.isCustomDeleteCallable();
157: deleteResultCheckStyles[0] = persistentClass
158: .getCustomSQLDeleteCheckStyle() == null ? ExecuteUpdateResultCheckStyle
159: .determineDefault(customSQLDelete[0], deleteCallable[0])
160: : persistentClass.getCustomSQLDeleteCheckStyle();
161:
162: // JOINS
163:
164: Iterator joinIter = persistentClass.getJoinClosureIterator();
165: int j = 1;
166: while (joinIter.hasNext()) {
167: Join join = (Join) joinIter.next();
168: qualifiedTableNames[j] = join.getTable().getQualifiedName(
169: factory.getDialect(),
170: factory.getSettings().getDefaultCatalogName(),
171: factory.getSettings().getDefaultSchemaName());
172: isInverseTable[j] = join.isInverse();
173: isNullableTable[j] = join.isOptional();
174: cascadeDeleteEnabled[j] = join.getKey()
175: .isCascadeDeleteEnabled()
176: && factory.getDialect().supportsCascadeDelete();
177:
178: customSQLInsert[j] = join.getCustomSQLInsert();
179: insertCallable[j] = customSQLInsert[j] != null
180: && join.isCustomInsertCallable();
181: insertResultCheckStyles[j] = join
182: .getCustomSQLInsertCheckStyle() == null ? ExecuteUpdateResultCheckStyle
183: .determineDefault(customSQLInsert[j],
184: insertCallable[j])
185: : join.getCustomSQLInsertCheckStyle();
186: customSQLUpdate[j] = join.getCustomSQLUpdate();
187: updateCallable[j] = customSQLUpdate[j] != null
188: && join.isCustomUpdateCallable();
189: updateResultCheckStyles[j] = join
190: .getCustomSQLUpdateCheckStyle() == null ? ExecuteUpdateResultCheckStyle
191: .determineDefault(customSQLUpdate[j],
192: updateCallable[j])
193: : join.getCustomSQLUpdateCheckStyle();
194: customSQLDelete[j] = join.getCustomSQLDelete();
195: deleteCallable[j] = customSQLDelete[j] != null
196: && join.isCustomDeleteCallable();
197: deleteResultCheckStyles[j] = join
198: .getCustomSQLDeleteCheckStyle() == null ? ExecuteUpdateResultCheckStyle
199: .determineDefault(customSQLDelete[j],
200: deleteCallable[j])
201: : join.getCustomSQLDeleteCheckStyle();
202:
203: Iterator iter = join.getKey().getColumnIterator();
204: keyColumnNames[j] = new String[join.getKey()
205: .getColumnSpan()];
206: int i = 0;
207: while (iter.hasNext()) {
208: Column col = (Column) iter.next();
209: keyColumnNames[j][i++] = col.getQuotedName(factory
210: .getDialect());
211: }
212:
213: j++;
214: }
215:
216: constraintOrderedTableNames = new String[qualifiedTableNames.length];
217: constraintOrderedKeyColumnNames = new String[qualifiedTableNames.length][];
218: for (int i = qualifiedTableNames.length - 1, position = 0; i >= 0; i--, position++) {
219: constraintOrderedTableNames[position] = qualifiedTableNames[i];
220: constraintOrderedKeyColumnNames[position] = keyColumnNames[i];
221: }
222:
223: spaces = ArrayHelper
224: .join(qualifiedTableNames, ArrayHelper
225: .toStringArray(persistentClass
226: .getSynchronizedTables()));
227:
228: final boolean lazyAvailable = isInstrumented(EntityMode.POJO);
229:
230: boolean hasDeferred = false;
231: ArrayList subclassTables = new ArrayList();
232: ArrayList joinKeyColumns = new ArrayList();
233: ArrayList isConcretes = new ArrayList();
234: ArrayList isDeferreds = new ArrayList();
235: ArrayList isInverses = new ArrayList();
236: ArrayList isNullables = new ArrayList();
237: ArrayList isLazies = new ArrayList();
238: subclassTables.add(qualifiedTableNames[0]);
239: joinKeyColumns.add(getIdentifierColumnNames());
240: isConcretes.add(Boolean.TRUE);
241: isDeferreds.add(Boolean.FALSE);
242: isInverses.add(Boolean.FALSE);
243: isNullables.add(Boolean.FALSE);
244: isLazies.add(Boolean.FALSE);
245: joinIter = persistentClass.getSubclassJoinClosureIterator();
246: while (joinIter.hasNext()) {
247: Join join = (Join) joinIter.next();
248: isConcretes.add(new Boolean(persistentClass
249: .isClassOrSuperclassJoin(join)));
250: isDeferreds.add(new Boolean(join.isSequentialSelect()));
251: isInverses.add(new Boolean(join.isInverse()));
252: isNullables.add(new Boolean(join.isOptional()));
253: isLazies.add(new Boolean(lazyAvailable && join.isLazy()));
254: if (join.isSequentialSelect()
255: && !persistentClass.isClassOrSuperclassJoin(join))
256: hasDeferred = true;
257: subclassTables.add(join.getTable().getQualifiedName(
258: factory.getDialect(),
259: factory.getSettings().getDefaultCatalogName(),
260: factory.getSettings().getDefaultSchemaName()));
261: Iterator iter = join.getKey().getColumnIterator();
262: String[] keyCols = new String[join.getKey().getColumnSpan()];
263: int i = 0;
264: while (iter.hasNext()) {
265: Column col = (Column) iter.next();
266: keyCols[i++] = col.getQuotedName(factory.getDialect());
267: }
268: joinKeyColumns.add(keyCols);
269: }
270:
271: subclassTableSequentialSelect = ArrayHelper
272: .toBooleanArray(isDeferreds);
273: subclassTableNameClosure = ArrayHelper
274: .toStringArray(subclassTables);
275: subclassTableIsLazyClosure = ArrayHelper
276: .toBooleanArray(isLazies);
277: subclassTableKeyColumnClosure = ArrayHelper
278: .to2DStringArray(joinKeyColumns);
279: isClassOrSuperclassTable = ArrayHelper
280: .toBooleanArray(isConcretes);
281: isInverseSubclassTable = ArrayHelper.toBooleanArray(isInverses);
282: isNullableSubclassTable = ArrayHelper
283: .toBooleanArray(isNullables);
284: hasSequentialSelects = hasDeferred;
285:
286: // DISCRIMINATOR
287:
288: final Object discriminatorValue;
289: if (persistentClass.isPolymorphic()) {
290: Value discrimValue = persistentClass.getDiscriminator();
291: if (discrimValue == null) {
292: throw new MappingException(
293: "discriminator mapping required for single table polymorphic persistence");
294: }
295: forceDiscriminator = persistentClass.isForceDiscriminator();
296: Selectable selectable = (Selectable) discrimValue
297: .getColumnIterator().next();
298: if (discrimValue.hasFormula()) {
299: Formula formula = (Formula) selectable;
300: discriminatorFormula = formula.getFormula();
301: discriminatorFormulaTemplate = formula.getTemplate(
302: factory.getDialect(), factory
303: .getSqlFunctionRegistry());
304: discriminatorColumnName = null;
305: discriminatorAlias = "clazz_";
306: } else {
307: Column column = (Column) selectable;
308: discriminatorColumnName = column.getQuotedName(factory
309: .getDialect());
310: discriminatorAlias = column.getAlias(factory
311: .getDialect(), persistentClass.getRootTable());
312: discriminatorFormula = null;
313: discriminatorFormulaTemplate = null;
314: }
315: discriminatorType = persistentClass.getDiscriminator()
316: .getType();
317: if (persistentClass.isDiscriminatorValueNull()) {
318: discriminatorValue = NULL_DISCRIMINATOR;
319: discriminatorSQLValue = InFragment.NULL;
320: discriminatorInsertable = false;
321: } else if (persistentClass.isDiscriminatorValueNotNull()) {
322: discriminatorValue = NOT_NULL_DISCRIMINATOR;
323: discriminatorSQLValue = InFragment.NOT_NULL;
324: discriminatorInsertable = false;
325: } else {
326: discriminatorInsertable = persistentClass
327: .isDiscriminatorInsertable()
328: && !discrimValue.hasFormula();
329: try {
330: DiscriminatorType dtype = (DiscriminatorType) discriminatorType;
331: discriminatorValue = dtype
332: .stringToObject(persistentClass
333: .getDiscriminatorValue());
334: discriminatorSQLValue = dtype.objectToSQLString(
335: discriminatorValue, factory.getDialect());
336: } catch (ClassCastException cce) {
337: throw new MappingException(
338: "Illegal discriminator type: "
339: + discriminatorType.getName());
340: } catch (Exception e) {
341: throw new MappingException(
342: "Could not format discriminator value to SQL string",
343: e);
344: }
345: }
346: } else {
347: forceDiscriminator = false;
348: discriminatorInsertable = false;
349: discriminatorColumnName = null;
350: discriminatorAlias = null;
351: discriminatorType = null;
352: discriminatorValue = null;
353: discriminatorSQLValue = null;
354: discriminatorFormula = null;
355: discriminatorFormulaTemplate = null;
356: }
357:
358: // PROPERTIES
359:
360: propertyTableNumbers = new int[getPropertySpan()];
361: Iterator iter = persistentClass.getPropertyClosureIterator();
362: int i = 0;
363: while (iter.hasNext()) {
364: Property prop = (Property) iter.next();
365: propertyTableNumbers[i++] = persistentClass
366: .getJoinNumber(prop);
367:
368: }
369:
370: //TODO: code duplication with JoinedSubclassEntityPersister
371:
372: ArrayList columnJoinNumbers = new ArrayList();
373: ArrayList formulaJoinedNumbers = new ArrayList();
374: ArrayList propertyJoinNumbers = new ArrayList();
375:
376: iter = persistentClass.getSubclassPropertyClosureIterator();
377: while (iter.hasNext()) {
378: Property prop = (Property) iter.next();
379: Integer join = new Integer(persistentClass
380: .getJoinNumber(prop));
381: propertyJoinNumbers.add(join);
382:
383: //propertyTableNumbersByName.put( prop.getName(), join );
384: propertyTableNumbersByNameAndSubclass.put(prop
385: .getPersistentClass().getEntityName()
386: + '.' + prop.getName(), join);
387:
388: Iterator citer = prop.getColumnIterator();
389: while (citer.hasNext()) {
390: Selectable thing = (Selectable) citer.next();
391: if (thing.isFormula()) {
392: formulaJoinedNumbers.add(join);
393: } else {
394: columnJoinNumbers.add(join);
395: }
396: }
397: }
398: subclassColumnTableNumberClosure = ArrayHelper
399: .toIntArray(columnJoinNumbers);
400: subclassFormulaTableNumberClosure = ArrayHelper
401: .toIntArray(formulaJoinedNumbers);
402: subclassPropertyTableNumberClosure = ArrayHelper
403: .toIntArray(propertyJoinNumbers);
404:
405: int subclassSpan = persistentClass.getSubclassSpan() + 1;
406: subclassClosure = new String[subclassSpan];
407: subclassClosure[0] = getEntityName();
408: if (persistentClass.isPolymorphic()) {
409: subclassesByDiscriminatorValue.put(discriminatorValue,
410: getEntityName());
411: }
412:
413: // SUBCLASSES
414: if (persistentClass.isPolymorphic()) {
415: iter = persistentClass.getSubclassIterator();
416: int k = 1;
417: while (iter.hasNext()) {
418: Subclass sc = (Subclass) iter.next();
419: subclassClosure[k++] = sc.getEntityName();
420: if (sc.isDiscriminatorValueNull()) {
421: subclassesByDiscriminatorValue.put(
422: NULL_DISCRIMINATOR, sc.getEntityName());
423: } else if (sc.isDiscriminatorValueNotNull()) {
424: subclassesByDiscriminatorValue.put(
425: NOT_NULL_DISCRIMINATOR, sc.getEntityName());
426: } else {
427: try {
428: DiscriminatorType dtype = (DiscriminatorType) discriminatorType;
429: subclassesByDiscriminatorValue.put(dtype
430: .stringToObject(sc
431: .getDiscriminatorValue()), sc
432: .getEntityName());
433: } catch (ClassCastException cce) {
434: throw new MappingException(
435: "Illegal discriminator type: "
436: + discriminatorType.getName());
437: } catch (Exception e) {
438: throw new MappingException(
439: "Error parsing discriminator value", e);
440: }
441: }
442: }
443: }
444:
445: initLockers();
446:
447: initSubclassPropertyAliasesMap(persistentClass);
448:
449: postConstruct(mapping);
450:
451: }
452:
453: protected boolean isInverseTable(int j) {
454: return isInverseTable[j];
455: }
456:
457: protected boolean isInverseSubclassTable(int j) {
458: return isInverseSubclassTable[j];
459: }
460:
461: public String getDiscriminatorColumnName() {
462: return discriminatorColumnName;
463: }
464:
465: protected String getDiscriminatorAlias() {
466: return discriminatorAlias;
467: }
468:
469: protected String getDiscriminatorFormulaTemplate() {
470: return discriminatorFormulaTemplate;
471: }
472:
473: public String getTableName() {
474: return qualifiedTableNames[0];
475: }
476:
477: public Type getDiscriminatorType() {
478: return discriminatorType;
479: }
480:
481: public String getDiscriminatorSQLValue() {
482: return discriminatorSQLValue;
483: }
484:
485: public String[] getSubclassClosure() {
486: return subclassClosure;
487: }
488:
489: public String getSubclassForDiscriminatorValue(Object value) {
490: if (value == null) {
491: return (String) subclassesByDiscriminatorValue
492: .get(NULL_DISCRIMINATOR);
493: } else {
494: String result = (String) subclassesByDiscriminatorValue
495: .get(value);
496: if (result == null)
497: result = (String) subclassesByDiscriminatorValue
498: .get(NOT_NULL_DISCRIMINATOR);
499: return result;
500: }
501: }
502:
503: public Serializable[] getPropertySpaces() {
504: return spaces;
505: }
506:
507: //Access cached SQL
508:
509: protected boolean isDiscriminatorFormula() {
510: return discriminatorColumnName == null;
511: }
512:
513: protected String getDiscriminatorFormula() {
514: return discriminatorFormula;
515: }
516:
517: protected String getTableName(int j) {
518: return qualifiedTableNames[j];
519: }
520:
521: protected String[] getKeyColumns(int j) {
522: return keyColumnNames[j];
523: }
524:
525: protected boolean isTableCascadeDeleteEnabled(int j) {
526: return cascadeDeleteEnabled[j];
527: }
528:
529: protected boolean isPropertyOfTable(int property, int j) {
530: return propertyTableNumbers[property] == j;
531: }
532:
533: protected boolean isSubclassTableSequentialSelect(int j) {
534: return subclassTableSequentialSelect[j]
535: && !isClassOrSuperclassTable[j];
536: }
537:
538: // Execute the SQL:
539:
540: public String fromTableFragment(String name) {
541: return getTableName() + ' ' + name;
542: }
543:
544: public String filterFragment(String alias) throws MappingException {
545: String result = discriminatorFilterFragment(alias);
546: if (hasWhere())
547: result += " and " + getSQLWhereString(alias);
548: return result;
549: }
550:
551: public String oneToManyFilterFragment(String alias)
552: throws MappingException {
553: return forceDiscriminator ? discriminatorFilterFragment(alias)
554: : "";
555: }
556:
557: private String discriminatorFilterFragment(String alias)
558: throws MappingException {
559: if (needsDiscriminator()) {
560: InFragment frag = new InFragment();
561:
562: if (isDiscriminatorFormula()) {
563: frag.setFormula(alias,
564: getDiscriminatorFormulaTemplate());
565: } else {
566: frag.setColumn(alias, getDiscriminatorColumnName());
567: }
568:
569: String[] subclasses = getSubclassClosure();
570: for (int i = 0; i < subclasses.length; i++) {
571: final Queryable queryable = (Queryable) getFactory()
572: .getEntityPersister(subclasses[i]);
573: if (!queryable.isAbstract())
574: frag.addValue(queryable.getDiscriminatorSQLValue());
575: }
576:
577: StringBuffer buf = new StringBuffer(50).append(" and ")
578: .append(frag.toFragmentString());
579:
580: return buf.toString();
581: } else {
582: return "";
583: }
584: }
585:
586: private boolean needsDiscriminator() {
587: return forceDiscriminator || isInherited();
588: }
589:
590: public String getSubclassPropertyTableName(int i) {
591: return subclassTableNameClosure[subclassPropertyTableNumberClosure[i]];
592: }
593:
594: protected void addDiscriminatorToSelect(SelectFragment select,
595: String name, String suffix) {
596: if (isDiscriminatorFormula()) {
597: select.addFormula(name, getDiscriminatorFormulaTemplate(),
598: getDiscriminatorAlias());
599: } else {
600: select.addColumn(name, getDiscriminatorColumnName(),
601: getDiscriminatorAlias());
602: }
603: }
604:
605: protected int[] getPropertyTableNumbersInSelect() {
606: return propertyTableNumbers;
607: }
608:
609: protected int getSubclassPropertyTableNumber(int i) {
610: return subclassPropertyTableNumberClosure[i];
611: }
612:
613: public int getTableSpan() {
614: return joinSpan;
615: }
616:
617: protected void addDiscriminatorToInsert(Insert insert) {
618:
619: if (discriminatorInsertable) {
620: insert.addColumn(getDiscriminatorColumnName(),
621: discriminatorSQLValue);
622: }
623:
624: }
625:
626: protected int[] getSubclassColumnTableNumberClosure() {
627: return subclassColumnTableNumberClosure;
628: }
629:
630: protected int[] getSubclassFormulaTableNumberClosure() {
631: return subclassFormulaTableNumberClosure;
632: }
633:
634: protected int[] getPropertyTableNumbers() {
635: return propertyTableNumbers;
636: }
637:
638: protected boolean isSubclassPropertyDeferred(String propertyName,
639: String entityName) {
640: return hasSequentialSelects
641: && isSubclassTableSequentialSelect(getSubclassPropertyTableNumber(
642: propertyName, entityName));
643: }
644:
645: public boolean hasSequentialSelect() {
646: return hasSequentialSelects;
647: }
648:
649: private int getSubclassPropertyTableNumber(String propertyName,
650: String entityName) {
651: Type type = propertyMapping.toType(propertyName);
652: if (type.isAssociationType()
653: && ((AssociationType) type).useLHSPrimaryKey())
654: return 0;
655: final Integer tabnum = (Integer) propertyTableNumbersByNameAndSubclass
656: .get(entityName + '.' + propertyName);
657: return tabnum == null ? 0 : tabnum.intValue();
658: }
659:
660: protected String getSequentialSelect(String entityName) {
661: return (String) sequentialSelectStringsByEntityName
662: .get(entityName);
663: }
664:
665: private String generateSequentialSelect(Loadable persister) {
666: //if ( this==persister || !hasSequentialSelects ) return null;
667:
668: //note that this method could easily be moved up to BasicEntityPersister,
669: //if we ever needed to reuse it from other subclasses
670:
671: //figure out which tables need to be fetched
672: AbstractEntityPersister subclassPersister = (AbstractEntityPersister) persister;
673: HashSet tableNumbers = new HashSet();
674: String[] props = subclassPersister.getPropertyNames();
675: String[] classes = subclassPersister.getPropertySubclassNames();
676: for (int i = 0; i < props.length; i++) {
677: int propTableNumber = getSubclassPropertyTableNumber(
678: props[i], classes[i]);
679: if (isSubclassTableSequentialSelect(propTableNumber)
680: && !isSubclassTableLazy(propTableNumber)) {
681: tableNumbers.add(new Integer(propTableNumber));
682: }
683: }
684: if (tableNumbers.isEmpty())
685: return null;
686:
687: //figure out which columns are needed
688: ArrayList columnNumbers = new ArrayList();
689: final int[] columnTableNumbers = getSubclassColumnTableNumberClosure();
690: for (int i = 0; i < getSubclassColumnClosure().length; i++) {
691: if (tableNumbers
692: .contains(new Integer(columnTableNumbers[i]))) {
693: columnNumbers.add(new Integer(i));
694: }
695: }
696:
697: //figure out which formulas are needed
698: ArrayList formulaNumbers = new ArrayList();
699: final int[] formulaTableNumbers = getSubclassColumnTableNumberClosure();
700: for (int i = 0; i < getSubclassFormulaTemplateClosure().length; i++) {
701: if (tableNumbers.contains(new Integer(
702: formulaTableNumbers[i]))) {
703: formulaNumbers.add(new Integer(i));
704: }
705: }
706:
707: //render the SQL
708: return renderSelect(ArrayHelper.toIntArray(tableNumbers),
709: ArrayHelper.toIntArray(columnNumbers), ArrayHelper
710: .toIntArray(formulaNumbers));
711: }
712:
713: protected String[] getSubclassTableKeyColumns(int j) {
714: return subclassTableKeyColumnClosure[j];
715: }
716:
717: public String getSubclassTableName(int j) {
718: return subclassTableNameClosure[j];
719: }
720:
721: public int getSubclassTableSpan() {
722: return subclassTableNameClosure.length;
723: }
724:
725: protected boolean isClassOrSuperclassTable(int j) {
726: return isClassOrSuperclassTable[j];
727: }
728:
729: protected boolean isSubclassTableLazy(int j) {
730: return subclassTableIsLazyClosure[j];
731: }
732:
733: protected boolean isNullableTable(int j) {
734: return isNullableTable[j];
735: }
736:
737: protected boolean isNullableSubclassTable(int j) {
738: return isNullableSubclassTable[j];
739: }
740:
741: public String getPropertyTableName(String propertyName) {
742: Integer index = getEntityMetamodel().getPropertyIndexOrNull(
743: propertyName);
744: if (index == null)
745: return null;
746: return qualifiedTableNames[propertyTableNumbers[index
747: .intValue()]];
748: }
749:
750: public void postInstantiate() {
751: super .postInstantiate();
752: if (hasSequentialSelects) {
753: String[] entityNames = getSubclassClosure();
754: for (int i = 1; i < entityNames.length; i++) {
755: Loadable loadable = (Loadable) getFactory()
756: .getEntityPersister(entityNames[i]);
757: if (!loadable.isAbstract()) { //perhaps not really necessary...
758: String sequentialSelect = generateSequentialSelect(loadable);
759: sequentialSelectStringsByEntityName.put(
760: entityNames[i], sequentialSelect);
761: }
762: }
763: }
764: }
765:
766: public boolean isMultiTable() {
767: return getTableSpan() > 1;
768: }
769:
770: public String[] getConstraintOrderedTableNameClosure() {
771: return constraintOrderedTableNames;
772: }
773:
774: public String[][] getContraintOrderedTableKeyColumnClosure() {
775: return constraintOrderedKeyColumnNames;
776: }
777: }
|