001: //$Id: UnionSubclassEntityPersister.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.HashMap;
006: import java.util.HashSet;
007: import java.util.Iterator;
008: import java.util.Map;
009: import java.util.ArrayList;
010:
011: import org.hibernate.AssertionFailure;
012: import org.hibernate.Hibernate;
013: import org.hibernate.HibernateException;
014: import org.hibernate.LockMode;
015: import org.hibernate.MappingException;
016: import org.hibernate.cache.CacheConcurrencyStrategy;
017: import org.hibernate.cfg.Settings;
018: import org.hibernate.dialect.Dialect;
019: import org.hibernate.engine.Mapping;
020: import org.hibernate.engine.SessionFactoryImplementor;
021: import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
022: import org.hibernate.id.IdentityGenerator;
023: import org.hibernate.mapping.Column;
024: import org.hibernate.mapping.PersistentClass;
025: import org.hibernate.mapping.Subclass;
026: import org.hibernate.mapping.Table;
027: import org.hibernate.sql.SelectFragment;
028: import org.hibernate.sql.SimpleSelect;
029: import org.hibernate.type.Type;
030: import org.hibernate.util.ArrayHelper;
031: import org.hibernate.util.JoinedIterator;
032: import org.hibernate.util.SingletonIterator;
033:
034: /**
035: * Implementation of the "table-per-concrete-class" or "roll-down" mapping
036: * strategy for an entity and its inheritence hierarchy.
037: *
038: * @author Gavin King
039: */
040: public class UnionSubclassEntityPersister extends
041: AbstractEntityPersister {
042:
043: // the class hierarchy structure
044: private final String subquery;
045: private final String tableName;
046: //private final String rootTableName;
047: private final String[] subclassClosure;
048: private final String[] spaces;
049: private final String[] subclassSpaces;
050: private final String discriminatorSQLValue;
051: private final Map subclassByDiscriminatorValue = new HashMap();
052:
053: private final String[] constraintOrderedTableNames;
054: private final String[][] constraintOrderedKeyColumnNames;
055:
056: //INITIALIZATION:
057:
058: public UnionSubclassEntityPersister(
059: final PersistentClass persistentClass,
060: final CacheConcurrencyStrategy cache,
061: final SessionFactoryImplementor factory,
062: final Mapping mapping) throws HibernateException {
063:
064: super (persistentClass, cache, factory);
065:
066: if (getIdentifierGenerator() instanceof IdentityGenerator) {
067: throw new MappingException(
068: "Cannot use identity column key generation with <union-subclass> mapping for: "
069: + getEntityName());
070: }
071:
072: // TABLE
073:
074: tableName = persistentClass.getTable().getQualifiedName(
075: factory.getDialect(),
076: factory.getSettings().getDefaultCatalogName(),
077: factory.getSettings().getDefaultSchemaName());
078: /*rootTableName = persistentClass.getRootTable().getQualifiedName(
079: factory.getDialect(),
080: factory.getDefaultCatalog(),
081: factory.getDefaultSchema()
082: );*/
083:
084: //Custom SQL
085: String sql;
086: boolean callable = false;
087: ExecuteUpdateResultCheckStyle checkStyle = null;
088: sql = persistentClass.getCustomSQLInsert();
089: callable = sql != null
090: && persistentClass.isCustomInsertCallable();
091: checkStyle = sql == null ? ExecuteUpdateResultCheckStyle.COUNT
092: : persistentClass.getCustomSQLInsertCheckStyle() == null ? ExecuteUpdateResultCheckStyle
093: .determineDefault(sql, callable)
094: : persistentClass
095: .getCustomSQLInsertCheckStyle();
096: customSQLInsert = new String[] { sql };
097: insertCallable = new boolean[] { callable };
098: insertResultCheckStyles = new ExecuteUpdateResultCheckStyle[] { checkStyle };
099:
100: sql = persistentClass.getCustomSQLUpdate();
101: callable = sql != null
102: && persistentClass.isCustomUpdateCallable();
103: checkStyle = sql == null ? ExecuteUpdateResultCheckStyle.COUNT
104: : persistentClass.getCustomSQLUpdateCheckStyle() == null ? ExecuteUpdateResultCheckStyle
105: .determineDefault(sql, callable)
106: : persistentClass
107: .getCustomSQLUpdateCheckStyle();
108: customSQLUpdate = new String[] { sql };
109: updateCallable = new boolean[] { callable };
110: updateResultCheckStyles = new ExecuteUpdateResultCheckStyle[] { checkStyle };
111:
112: sql = persistentClass.getCustomSQLDelete();
113: callable = sql != null
114: && persistentClass.isCustomDeleteCallable();
115: checkStyle = sql == null ? ExecuteUpdateResultCheckStyle.COUNT
116: : persistentClass.getCustomSQLDeleteCheckStyle() == null ? ExecuteUpdateResultCheckStyle
117: .determineDefault(sql, callable)
118: : persistentClass
119: .getCustomSQLDeleteCheckStyle();
120: customSQLDelete = new String[] { sql };
121: deleteCallable = new boolean[] { callable };
122: deleteResultCheckStyles = new ExecuteUpdateResultCheckStyle[] { checkStyle };
123:
124: discriminatorSQLValue = String.valueOf(persistentClass
125: .getSubclassId());
126:
127: // PROPERTIES
128:
129: int subclassSpan = persistentClass.getSubclassSpan() + 1;
130: subclassClosure = new String[subclassSpan];
131: subclassClosure[0] = getEntityName();
132:
133: // SUBCLASSES
134: subclassByDiscriminatorValue.put(new Integer(persistentClass
135: .getSubclassId()), persistentClass.getEntityName());
136: if (persistentClass.isPolymorphic()) {
137: Iterator iter = persistentClass.getSubclassIterator();
138: int k = 1;
139: while (iter.hasNext()) {
140: Subclass sc = (Subclass) iter.next();
141: subclassClosure[k++] = sc.getEntityName();
142: subclassByDiscriminatorValue.put(new Integer(sc
143: .getSubclassId()), sc.getEntityName());
144: }
145: }
146:
147: //SPACES
148: //TODO: i'm not sure, but perhaps we should exclude
149: // abstract denormalized tables?
150:
151: int spacesSize = 1 + persistentClass.getSynchronizedTables()
152: .size();
153: spaces = new String[spacesSize];
154: spaces[0] = tableName;
155: Iterator iter = persistentClass.getSynchronizedTables()
156: .iterator();
157: for (int i = 1; i < spacesSize; i++) {
158: spaces[i] = (String) iter.next();
159: }
160:
161: HashSet subclassTables = new HashSet();
162: iter = persistentClass.getSubclassTableClosureIterator();
163: while (iter.hasNext()) {
164: Table table = (Table) iter.next();
165: subclassTables.add(table.getQualifiedName(factory
166: .getDialect(), factory.getSettings()
167: .getDefaultCatalogName(), factory.getSettings()
168: .getDefaultSchemaName()));
169: }
170: subclassSpaces = ArrayHelper.toStringArray(subclassTables);
171:
172: subquery = generateSubquery(persistentClass, mapping);
173:
174: if (isMultiTable()) {
175: int idColumnSpan = getIdentifierColumnSpan();
176: ArrayList tableNames = new ArrayList();
177: ArrayList keyColumns = new ArrayList();
178: if (!isAbstract()) {
179: tableNames.add(tableName);
180: keyColumns.add(getIdentifierColumnNames());
181: }
182: iter = persistentClass.getSubclassTableClosureIterator();
183: while (iter.hasNext()) {
184: Table tab = (Table) iter.next();
185: if (!tab.isAbstractUnionTable()) {
186: String tableName = tab.getQualifiedName(factory
187: .getDialect(), factory.getSettings()
188: .getDefaultCatalogName(), factory
189: .getSettings().getDefaultSchemaName());
190: tableNames.add(tableName);
191: String[] key = new String[idColumnSpan];
192: Iterator citer = tab.getPrimaryKey()
193: .getColumnIterator();
194: for (int k = 0; k < idColumnSpan; k++) {
195: key[k] = ((Column) citer.next())
196: .getQuotedName(factory.getDialect());
197: }
198: keyColumns.add(key);
199: }
200: }
201:
202: constraintOrderedTableNames = ArrayHelper
203: .toStringArray(tableNames);
204: constraintOrderedKeyColumnNames = ArrayHelper
205: .to2DStringArray(keyColumns);
206: } else {
207: constraintOrderedTableNames = new String[] { tableName };
208: constraintOrderedKeyColumnNames = new String[][] { getIdentifierColumnNames() };
209: }
210:
211: initLockers();
212:
213: initSubclassPropertyAliasesMap(persistentClass);
214:
215: postConstruct(mapping);
216:
217: }
218:
219: public Serializable[] getQuerySpaces() {
220: return subclassSpaces;
221: }
222:
223: public String getTableName() {
224: return subquery;
225: }
226:
227: public Type getDiscriminatorType() {
228: return Hibernate.INTEGER;
229: }
230:
231: public String getDiscriminatorSQLValue() {
232: return discriminatorSQLValue;
233: }
234:
235: public String[] getSubclassClosure() {
236: return subclassClosure;
237: }
238:
239: public String getSubclassForDiscriminatorValue(Object value) {
240: return (String) subclassByDiscriminatorValue.get(value);
241: }
242:
243: public Serializable[] getPropertySpaces() {
244: return spaces;
245: }
246:
247: protected boolean isDiscriminatorFormula() {
248: return false;
249: }
250:
251: /**
252: * Generate the SQL that selects a row by id
253: */
254: protected String generateSelectString(LockMode lockMode) {
255: SimpleSelect select = new SimpleSelect(getFactory()
256: .getDialect()).setLockMode(lockMode).setTableName(
257: getTableName()).addColumns(getIdentifierColumnNames())
258: .addColumns(getSubclassColumnClosure(),
259: getSubclassColumnAliasClosure(),
260: getSubclassColumnLazyiness()).addColumns(
261: getSubclassFormulaClosure(),
262: getSubclassFormulaAliasClosure(),
263: getSubclassFormulaLazyiness());
264: //TODO: include the rowids!!!!
265: if (hasSubclasses()) {
266: if (isDiscriminatorFormula()) {
267: select.addColumn(getDiscriminatorFormula(),
268: getDiscriminatorAlias());
269: } else {
270: select.addColumn(getDiscriminatorColumnName(),
271: getDiscriminatorAlias());
272: }
273: }
274: if (getFactory().getSettings().isCommentsEnabled()) {
275: select.setComment("load " + getEntityName());
276: }
277: return select.addCondition(getIdentifierColumnNames(), "=?")
278: .toStatementString();
279: }
280:
281: protected String getDiscriminatorFormula() {
282: return null;
283: }
284:
285: protected String getTableName(int j) {
286: return tableName;
287: }
288:
289: protected String[] getKeyColumns(int j) {
290: return getIdentifierColumnNames();
291: }
292:
293: protected boolean isTableCascadeDeleteEnabled(int j) {
294: return false;
295: }
296:
297: protected boolean isPropertyOfTable(int property, int j) {
298: return true;
299: }
300:
301: // Execute the SQL:
302:
303: public String fromTableFragment(String name) {
304: return getTableName() + ' ' + name;
305: }
306:
307: public String filterFragment(String name) {
308: return hasWhere() ? " and " + getSQLWhereString(name) : "";
309: }
310:
311: public String getSubclassPropertyTableName(int i) {
312: return getTableName();//ie. the subquery! yuck!
313: }
314:
315: protected void addDiscriminatorToSelect(SelectFragment select,
316: String name, String suffix) {
317: select.addColumn(name, getDiscriminatorColumnName(),
318: getDiscriminatorAlias());
319: }
320:
321: protected int[] getPropertyTableNumbersInSelect() {
322: return new int[getPropertySpan()];
323: }
324:
325: protected int getSubclassPropertyTableNumber(int i) {
326: return 0;
327: }
328:
329: public int getSubclassPropertyTableNumber(String propertyName) {
330: return 0;
331: }
332:
333: public boolean isMultiTable() {
334: // This could also just be true all the time...
335: return isAbstract() || hasSubclasses();
336: }
337:
338: public int getTableSpan() {
339: return 1;
340: }
341:
342: protected int[] getSubclassColumnTableNumberClosure() {
343: return new int[getSubclassColumnClosure().length];
344: }
345:
346: protected int[] getSubclassFormulaTableNumberClosure() {
347: return new int[getSubclassFormulaClosure().length];
348: }
349:
350: protected boolean[] getTableHasColumns() {
351: return new boolean[] { true };
352: }
353:
354: protected int[] getPropertyTableNumbers() {
355: return new int[getPropertySpan()];
356: }
357:
358: protected String generateSubquery(PersistentClass model,
359: Mapping mapping) {
360:
361: Dialect dialect = getFactory().getDialect();
362: Settings settings = getFactory().getSettings();
363:
364: if (!model.hasSubclasses()) {
365: return model.getTable().getQualifiedName(dialect,
366: settings.getDefaultCatalogName(),
367: settings.getDefaultSchemaName());
368: }
369:
370: HashSet columns = new HashSet();
371: Iterator titer = model.getSubclassTableClosureIterator();
372: while (titer.hasNext()) {
373: Table table = (Table) titer.next();
374: if (!table.isAbstractUnionTable()) {
375: Iterator citer = table.getColumnIterator();
376: while (citer.hasNext())
377: columns.add(citer.next());
378: }
379: }
380:
381: StringBuffer buf = new StringBuffer().append("( ");
382:
383: Iterator siter = new JoinedIterator(
384: new SingletonIterator(model), model
385: .getSubclassIterator());
386:
387: while (siter.hasNext()) {
388: PersistentClass clazz = (PersistentClass) siter.next();
389: Table table = clazz.getTable();
390: if (!table.isAbstractUnionTable()) {
391: //TODO: move to .sql package!!
392: buf.append("select ");
393: Iterator citer = columns.iterator();
394: while (citer.hasNext()) {
395: Column col = (Column) citer.next();
396: if (!table.containsColumn(col)) {
397: int sqlType = col.getSqlTypeCode(mapping);
398: buf
399: .append(
400: dialect
401: .getSelectClauseNullString(sqlType))
402: .append(" as ");
403: }
404: buf.append(col.getName());
405: buf.append(", ");
406: }
407: buf.append(clazz.getSubclassId()).append(" as clazz_");
408: buf.append(" from ").append(
409: table.getQualifiedName(dialect, settings
410: .getDefaultCatalogName(), settings
411: .getDefaultSchemaName()));
412: buf.append(" union ");
413: if (dialect.supportsUnionAll()) {
414: buf.append("all ");
415: }
416: }
417: }
418:
419: if (buf.length() > 2) {
420: //chop the last union (all)
421: buf.setLength(buf.length()
422: - (dialect.supportsUnionAll() ? 11 : 7));
423: }
424:
425: return buf.append(" )").toString();
426: }
427:
428: protected String[] getSubclassTableKeyColumns(int j) {
429: if (j != 0)
430: throw new AssertionFailure("only one table");
431: return getIdentifierColumnNames();
432: }
433:
434: public String getSubclassTableName(int j) {
435: if (j != 0)
436: throw new AssertionFailure("only one table");
437: return tableName;
438: }
439:
440: public int getSubclassTableSpan() {
441: return 1;
442: }
443:
444: protected boolean isClassOrSuperclassTable(int j) {
445: if (j != 0)
446: throw new AssertionFailure("only one table");
447: return true;
448: }
449:
450: public String getPropertyTableName(String propertyName) {
451: //TODO: check this....
452: return getTableName();
453: }
454:
455: public String[] getConstraintOrderedTableNameClosure() {
456: return constraintOrderedTableNames;
457: }
458:
459: public String[][] getContraintOrderedTableKeyColumnClosure() {
460: return constraintOrderedKeyColumnNames;
461: }
462: }
|