FileDocCategorySizeDatePackage
JoinedSubclassEntityPersister.javaAPI DocHibernate 3.2.519643Tue Jan 30 07:29:40 GMT 2007org.hibernate.persister.entity

JoinedSubclassEntityPersister

public class JoinedSubclassEntityPersister extends AbstractEntityPersister
An EntityPersister implementing the normalized "table-per-subclass" mapping strategy
author
Gavin King

Fields Summary
private final int
tableSpan
private final String[]
tableNames
private final String[]
naturalOrderTableNames
private final String[]
tableKeyColumns
private final String[]
naturalOrderTableKeyColumns
private final boolean[]
naturalOrderCascadeDeleteEnabled
private final String[]
spaces
private final String[]
subclassClosure
private final String[]
subclassTableNameClosure
private final String[]
subclassTableKeyColumnClosure
private final boolean[]
isClassOrSuperclassTable
private final int[]
naturalOrderPropertyTableNumbers
private final int[]
propertyTableNumbers
private final int[]
subclassPropertyTableNumberClosure
private final int[]
subclassColumnTableNumberClosure
private final int[]
subclassFormulaTableNumberClosure
private final Map
subclassesByDiscriminatorValue
private final String[]
discriminatorValues
private final String[]
notNullColumnNames
private final int[]
notNullColumnTableNumbers
private final String[]
constraintOrderedTableNames
private final String[]
constraintOrderedKeyColumnNames
private final String
discriminatorSQLString
Constructors Summary
public JoinedSubclassEntityPersister(org.hibernate.mapping.PersistentClass persistentClass, org.hibernate.cache.CacheConcurrencyStrategy cache, org.hibernate.engine.SessionFactoryImplementor factory, org.hibernate.engine.Mapping mapping)


	//INITIALIZATION:

	 
			   
			  
			  
			  
	  

		super(persistentClass, cache, factory);

		// DISCRIMINATOR

		final Object discriminatorValue;
		if ( persistentClass.isPolymorphic() ) {
			try {
				discriminatorValue = new Integer( persistentClass.getSubclassId() );
				discriminatorSQLString = discriminatorValue.toString();
			}
			catch (Exception e) {
				throw new MappingException("Could not format discriminator value to SQL string", e );
			}
		}
		else {
			discriminatorValue = null;
			discriminatorSQLString = null;
		}

		if ( optimisticLockMode() > Versioning.OPTIMISTIC_LOCK_VERSION ) {
			throw new MappingException( "optimistic-lock=all|dirty not supported for joined-subclass mappings [" + getEntityName() + "]" );
		}

		//MULTITABLES

		final int idColumnSpan = getIdentifierColumnSpan();

		ArrayList tables = new ArrayList();
		ArrayList keyColumns = new ArrayList();
		ArrayList cascadeDeletes = new ArrayList();
		Iterator titer = persistentClass.getTableClosureIterator();
		Iterator kiter = persistentClass.getKeyClosureIterator();
		while ( titer.hasNext() ) {
			Table tab = (Table) titer.next();
			KeyValue key = (KeyValue) kiter.next();
			String tabname = tab.getQualifiedName( 
					factory.getDialect(), 
					factory.getSettings().getDefaultCatalogName(), 
					factory.getSettings().getDefaultSchemaName() 
			);
			tables.add(tabname);
			String[] keyCols = new String[idColumnSpan];
			Iterator citer = key.getColumnIterator();
			for ( int k=0; k<idColumnSpan; k++ ) {
				keyCols[k] = ( (Column) citer.next() ).getQuotedName( factory.getDialect() );
			}
			keyColumns.add(keyCols);
			cascadeDeletes.add( new Boolean( key.isCascadeDeleteEnabled() && factory.getDialect().supportsCascadeDelete() ) );
		}
		naturalOrderTableNames = ArrayHelper.toStringArray(tables);
		naturalOrderTableKeyColumns = ArrayHelper.to2DStringArray(keyColumns);
		naturalOrderCascadeDeleteEnabled = ArrayHelper.toBooleanArray(cascadeDeletes);

		ArrayList subtables = new ArrayList();
		ArrayList isConcretes = new ArrayList();
		keyColumns = new ArrayList();
		titer = persistentClass.getSubclassTableClosureIterator();
		while ( titer.hasNext() ) {
			Table tab = (Table) titer.next();
			isConcretes.add( new Boolean( persistentClass.isClassOrSuperclassTable(tab) ) );
			String tabname = tab.getQualifiedName( 
					factory.getDialect(), 
					factory.getSettings().getDefaultCatalogName(), 
					factory.getSettings().getDefaultSchemaName() 
			);
			subtables.add(tabname);
			String[] key = new String[idColumnSpan];
			Iterator citer = tab.getPrimaryKey().getColumnIterator();
			for ( int k=0; k<idColumnSpan; k++ ) {
				key[k] = ( (Column) citer.next() ).getQuotedName( factory.getDialect() );
			}
			keyColumns.add(key);
		}
		subclassTableNameClosure = ArrayHelper.toStringArray(subtables);
		subclassTableKeyColumnClosure = ArrayHelper.to2DStringArray(keyColumns);
		isClassOrSuperclassTable = ArrayHelper.toBooleanArray(isConcretes);

		constraintOrderedTableNames = new String[subclassTableNameClosure.length];
		constraintOrderedKeyColumnNames = new String[subclassTableNameClosure.length][];
		int currentPosition = 0;
		for ( int i = subclassTableNameClosure.length - 1; i >= 0 ; i--, currentPosition++ ) {
			constraintOrderedTableNames[currentPosition] = subclassTableNameClosure[i];
			constraintOrderedKeyColumnNames[currentPosition] = subclassTableKeyColumnClosure[i];
		}

		tableSpan = naturalOrderTableNames.length;
		tableNames = reverse(naturalOrderTableNames);
		tableKeyColumns = reverse(naturalOrderTableKeyColumns);
		reverse(subclassTableNameClosure, tableSpan);
		reverse(subclassTableKeyColumnClosure, tableSpan);

		spaces = ArrayHelper.join( 
				tableNames, 
				ArrayHelper.toStringArray( persistentClass.getSynchronizedTables() )
		);

		// Custom sql
		customSQLInsert = new String[tableSpan];
		customSQLUpdate = new String[tableSpan];
		customSQLDelete = new String[tableSpan];
		insertCallable = new boolean[tableSpan];
		updateCallable = new boolean[tableSpan];
		deleteCallable = new boolean[tableSpan];
		insertResultCheckStyles = new ExecuteUpdateResultCheckStyle[tableSpan];
		updateResultCheckStyles = new ExecuteUpdateResultCheckStyle[tableSpan];
		deleteResultCheckStyles = new ExecuteUpdateResultCheckStyle[tableSpan];

		PersistentClass pc = persistentClass;
		int jk = tableSpan-1;
		while (pc!=null) {
			customSQLInsert[jk] = pc.getCustomSQLInsert();
			insertCallable[jk] = customSQLInsert[jk] != null && pc.isCustomInsertCallable();
			insertResultCheckStyles[jk] = pc.getCustomSQLInsertCheckStyle() == null
			                              ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLInsert[jk], insertCallable[jk] )
		                                  : pc.getCustomSQLInsertCheckStyle();
			customSQLUpdate[jk] = pc.getCustomSQLUpdate();
			updateCallable[jk] = customSQLUpdate[jk] != null && pc.isCustomUpdateCallable();
			updateResultCheckStyles[jk] = pc.getCustomSQLUpdateCheckStyle() == null
			                              ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLUpdate[jk], updateCallable[jk] )
		                                  : pc.getCustomSQLUpdateCheckStyle();
			customSQLDelete[jk] = pc.getCustomSQLDelete();
			deleteCallable[jk] = customSQLDelete[jk] != null && pc.isCustomDeleteCallable();
			deleteResultCheckStyles[jk] = pc.getCustomSQLDeleteCheckStyle() == null
			                              ? ExecuteUpdateResultCheckStyle.determineDefault( customSQLDelete[jk], deleteCallable[jk] )
		                                  : pc.getCustomSQLDeleteCheckStyle();
			jk--;
			pc = pc.getSuperclass();
		}
		if ( jk != -1 ) {
			throw new AssertionFailure( "Tablespan does not match height of joined-subclass hiearchy." );
		}

		// PROPERTIES

		int hydrateSpan = getPropertySpan();
		naturalOrderPropertyTableNumbers = new int[hydrateSpan];
		propertyTableNumbers = new int[hydrateSpan];
		Iterator iter = persistentClass.getPropertyClosureIterator();
		int i=0;
		while( iter.hasNext() ) {
			Property prop = (Property) iter.next();
			String tabname = prop.getValue().getTable().getQualifiedName(
				factory.getDialect(), 
				factory.getSettings().getDefaultCatalogName(), 
				factory.getSettings().getDefaultSchemaName()
			);
			propertyTableNumbers[i] = getTableId(tabname, tableNames);
			naturalOrderPropertyTableNumbers[i] = getTableId(tabname, naturalOrderTableNames);
			i++;
		}

		// subclass closure properties

		//TODO: code duplication with SingleTableEntityPersister
		
		ArrayList columnTableNumbers = new ArrayList();
		ArrayList formulaTableNumbers = new ArrayList();
		ArrayList propTableNumbers = new ArrayList();
		
		iter = persistentClass.getSubclassPropertyClosureIterator();
		while ( iter.hasNext() ) {
			Property prop = (Property) iter.next();
			Table tab = prop.getValue().getTable();
			String tabname = tab.getQualifiedName( 
					factory.getDialect(), 
					factory.getSettings().getDefaultCatalogName(), 
					factory.getSettings().getDefaultSchemaName() 
			);
			Integer tabnum = new Integer( getTableId(tabname, subclassTableNameClosure) );
			propTableNumbers.add(tabnum);

			Iterator citer = prop.getColumnIterator();
			while ( citer.hasNext() ) {
				Selectable thing = (Selectable) citer.next();
				if ( thing.isFormula() ) {
					formulaTableNumbers.add(tabnum);
				}
				else {
					columnTableNumbers.add(tabnum);
				}
			}

		}

		subclassColumnTableNumberClosure = ArrayHelper.toIntArray(columnTableNumbers);
		subclassPropertyTableNumberClosure = ArrayHelper.toIntArray(propTableNumbers);
		subclassFormulaTableNumberClosure = ArrayHelper.toIntArray(formulaTableNumbers);

		// SUBCLASSES

		int subclassSpan = persistentClass.getSubclassSpan() + 1;
		subclassClosure = new String[subclassSpan];
		subclassClosure[subclassSpan-1] = getEntityName();
		if ( persistentClass.isPolymorphic() ) {
			subclassesByDiscriminatorValue.put( discriminatorValue, getEntityName() );
			discriminatorValues = new String[subclassSpan];
			discriminatorValues[subclassSpan-1] = discriminatorSQLString;
			notNullColumnTableNumbers = new int[subclassSpan];
			final int id = getTableId(
				persistentClass.getTable().getQualifiedName( 
						factory.getDialect(), 
						factory.getSettings().getDefaultCatalogName(), 
						factory.getSettings().getDefaultSchemaName() 
				),
				subclassTableNameClosure
			);
			notNullColumnTableNumbers[subclassSpan-1] = id;
			notNullColumnNames = new String[subclassSpan];
			notNullColumnNames[subclassSpan-1] =  subclassTableKeyColumnClosure[id][0]; //( (Column) model.getTable().getPrimaryKey().getColumnIterator().next() ).getName();
		}
		else {
			discriminatorValues = null;
			notNullColumnTableNumbers = null;
			notNullColumnNames = null;
		}

		iter = persistentClass.getSubclassIterator();
		int k=0;
		while ( iter.hasNext() ) {
			Subclass sc = (Subclass) iter.next();
			subclassClosure[k] = sc.getEntityName();
			try {
				if ( persistentClass.isPolymorphic() ) {
					// we now use subclass ids that are consistent across all
					// persisters for a class hierarchy, so that the use of 
					// "foo.class = Bar" works in HQL
					Integer subclassId = new Integer( sc.getSubclassId() );//new Integer(k+1);
					subclassesByDiscriminatorValue.put( subclassId, sc.getEntityName() );
					discriminatorValues[k] = subclassId.toString();
					int id = getTableId(
						sc.getTable().getQualifiedName( 
								factory.getDialect(), 
								factory.getSettings().getDefaultCatalogName(), 
								factory.getSettings().getDefaultSchemaName() 
						),
						subclassTableNameClosure
					);
					notNullColumnTableNumbers[k] = id;
					notNullColumnNames[k] = subclassTableKeyColumnClosure[id][0]; //( (Column) sc.getTable().getPrimaryKey().getColumnIterator().next() ).getName();
				}
			}
			catch (Exception e) {
				throw new MappingException("Error parsing discriminator value", e );
			}
			k++;
		}

		initLockers();

		initSubclassPropertyAliasesMap(persistentClass);
		
		postConstruct(mapping);

	
Methods Summary
public voidaddDiscriminatorToSelect(org.hibernate.sql.SelectFragment select, java.lang.String name, java.lang.String suffix)

		if ( hasSubclasses() ) {
			select.setExtraSelectList( discriminatorFragment(name), getDiscriminatorAlias() );
		}
	
private org.hibernate.sql.CaseFragmentdiscriminatorFragment(java.lang.String alias)

		CaseFragment cases = getFactory().getDialect().createCaseFragment();

		for ( int i=0; i<discriminatorValues.length; i++ ) {
			cases.addWhenColumnNotNull(
				generateTableAlias( alias, notNullColumnTableNumbers[i] ),
				notNullColumnNames[i],
				discriminatorValues[i]
			);
		}

		return cases;
	
public java.lang.StringfilterFragment(java.lang.String alias)

		return hasWhere() ?
			" and " + getSQLWhereString( generateFilterConditionAlias( alias ) ) :
			"";
	
public java.lang.StringfromTableFragment(java.lang.String alias)

		return getTableName() + ' " + alias;
	
public java.lang.StringgenerateFilterConditionAlias(java.lang.String rootAlias)

		return generateTableAlias( rootAlias, tableSpan-1 );
	
public java.lang.String[]getConstraintOrderedTableNameClosure()

		return constraintOrderedTableNames;
	
public java.lang.String[][]getContraintOrderedTableKeyColumnClosure()

		return constraintOrderedKeyColumnNames;
	
public java.lang.StringgetDiscriminatorSQLValue()

		return discriminatorSQLString;
	
public org.hibernate.type.TypegetDiscriminatorType()

		return Hibernate.INTEGER;
	
public java.lang.String[]getIdentifierColumnNames()

		return tableKeyColumns[0];
	
protected java.lang.String[]getKeyColumns(int j)

		return naturalOrderTableKeyColumns[j];
	
public java.io.Serializable[]getPropertySpaces()

		return spaces; // don't need subclass tables, because they can't appear in conditions
	
public java.lang.StringgetPropertyTableName(java.lang.String propertyName)

		Integer index = getEntityMetamodel().getPropertyIndexOrNull(propertyName);
		if ( index == null ) {
			return null;
		}
		return tableNames[ propertyTableNumbers[ index.intValue() ] ];
	
protected int[]getPropertyTableNumbers()

		return naturalOrderPropertyTableNumbers;
	
protected int[]getPropertyTableNumbersInSelect()

		return propertyTableNumbers;
	
public java.lang.StringgetRootTableAlias(java.lang.String drivingAlias)

		return generateTableAlias( drivingAlias, getTableId( getRootTableName(), tableNames ) );
	
public java.lang.StringgetRootTableName()

		return naturalOrderTableNames[0];
	
protected int[]getSubclassColumnTableNumberClosure()

		return subclassColumnTableNumberClosure;
	
public java.lang.StringgetSubclassForDiscriminatorValue(java.lang.Object value)

		return (String) subclassesByDiscriminatorValue.get(value);
	
protected int[]getSubclassFormulaTableNumberClosure()

		return subclassFormulaTableNumberClosure;
	
public DeclarergetSubclassPropertyDeclarer(java.lang.String propertyPath)

		if ( "class".equals( propertyPath ) ) {
			// special case where we need to force incloude all subclass joins
			return Declarer.SUBCLASS;
		}
		return super.getSubclassPropertyDeclarer( propertyPath );
	
public java.lang.StringgetSubclassPropertyTableName(int i)

		return subclassTableNameClosure[ subclassPropertyTableNumberClosure[i] ];
	
protected intgetSubclassPropertyTableNumber(int i)

		return subclassPropertyTableNumberClosure[i];
	
protected java.lang.String[]getSubclassTableKeyColumns(int j)

		return subclassTableKeyColumnClosure[j];
	
public java.lang.StringgetSubclassTableName(int j)

		return subclassTableNameClosure[j];
	
public intgetSubclassTableSpan()

		return subclassTableNameClosure.length;
	
private static intgetTableId(java.lang.String tableName, java.lang.String[] tables)

		for ( int j=0; j<tables.length; j++ ) {
			if ( tableName.equals( tables[j] ) ) {
				return j;
			}
		}
		throw new AssertionFailure("Table " + tableName + " not found");
	
public java.lang.StringgetTableName()

		return tableNames[0];
	
protected java.lang.StringgetTableName(int j)

		return naturalOrderTableNames[j];
	
public intgetTableSpan()

		return tableSpan;
	
protected booleanisClassOrSuperclassTable(int j)

		return isClassOrSuperclassTable[j];
	
public booleanisMultiTable()

		return true;
	
protected booleanisPropertyOfTable(int property, int j)

		return naturalOrderPropertyTableNumbers[property]==j;
	
protected booleanisTableCascadeDeleteEnabled(int j)

		return naturalOrderCascadeDeleteEnabled[j];
	
private static final voidreverse(java.lang.Object[] objects, int len)
Load an instance using either the forUpdateLoader or the outer joining loader, depending upon the value of the lock parameter

		Object[] temp = new Object[len];
		for (int i=0; i<len; i++) {
			temp[i] = objects[len-i-1];
		}
		for (int i=0; i<len; i++) {
			objects[i] = temp[i];
		}
	
private static final java.lang.String[]reverse(java.lang.String[] objects)

		int len = objects.length;
		String[] temp = new String[len];
		for (int i=0; i<len; i++) {
			temp[i] = objects[len-i-1];
		}
		return temp;
	
private static final java.lang.String[][]reverse(java.lang.String[][] objects)

		int len = objects.length;
		String[][] temp = new String[len][];
		for (int i=0; i<len; i++) {
			temp[i] = objects[len-i-1];
		}
		return temp;
	
public java.lang.String[]toColumns(java.lang.String alias, java.lang.String propertyName)


		if ( ENTITY_CLASS.equals(propertyName) ) {
			// This doesn't actually seem to work but it *might*
			// work on some dbs. Also it doesn't work if there
			// are multiple columns of results because it
			// is not accounting for the suffix:
			// return new String[] { getDiscriminatorColumnName() };

			return new String[] { discriminatorFragment(alias).toFragmentString() };
		}
		else {
			return super.toColumns(alias, propertyName);
		}