Author: sharathjreddy
Date: 2010-07-04 15:44:44 -0400 (Sun, 04 Jul 2010)
New Revision: 19894
Modified:
core/trunk/core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java
Log:
HHH-4250 : @ManyToOne - @OneToMany doesn't work with @Inheritance(strategy=
InheritanceType.JOINED)
Modified:
core/trunk/core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java
===================================================================
---
core/trunk/core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java 2010-07-04
17:35:41 UTC (rev 19893)
+++
core/trunk/core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java 2010-07-04
19:44:44 UTC (rev 19894)
@@ -1,635 +1,781 @@
-/*
- * Hibernate, Relational Persistence for Idiomatic Java
- *
- * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
- * indicated by the @author tags or express copyright attribution
- * statements applied by the authors. All third-party contributions are
- * distributed under license by Red Hat Middleware LLC.
- *
- * This copyrighted material is made available to anyone wishing to use, modify,
- * copy, or redistribute it subject to the terms and conditions of the GNU
- * Lesser General Public License, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this distribution; if not, write to:
- * Free Software Foundation, Inc.
- * 51 Franklin Street, Fifth Floor
- * Boston, MA 02110-1301 USA
- *
- */
-package org.hibernate.persister.entity;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import org.hibernate.AssertionFailure;
-import org.hibernate.Hibernate;
-import org.hibernate.HibernateException;
-import org.hibernate.MappingException;
-import org.hibernate.QueryException;
-import org.hibernate.cache.access.EntityRegionAccessStrategy;
-import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
-import org.hibernate.engine.Mapping;
-import org.hibernate.engine.SessionFactoryImplementor;
-import org.hibernate.engine.Versioning;
-import org.hibernate.mapping.Column;
-import org.hibernate.mapping.KeyValue;
-import org.hibernate.mapping.PersistentClass;
-import org.hibernate.mapping.Property;
-import org.hibernate.mapping.Selectable;
-import org.hibernate.mapping.Subclass;
-import org.hibernate.mapping.Table;
-import org.hibernate.sql.CaseFragment;
-import org.hibernate.sql.SelectFragment;
-import org.hibernate.type.AbstractType;
-import org.hibernate.type.Type;
-import org.hibernate.util.ArrayHelper;
-
-/**
- * An <tt>EntityPersister</tt> implementing the normalized
"table-per-subclass"
- * mapping strategy
- *
- * @author Gavin King
- */
-public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
-
- // the class hierarchy structure
- private final int tableSpan;
- private final String[] tableNames;
- private final String[] naturalOrderTableNames;
- private final String[][] tableKeyColumns;
- private final String[][] tableKeyColumnReaders;
- private final String[][] tableKeyColumnReaderTemplates;
- private final String[][] naturalOrderTableKeyColumns;
- private final String[][] naturalOrderTableKeyColumnReaders;
- private final String[][] naturalOrderTableKeyColumnReaderTemplates;
- private final boolean[] naturalOrderCascadeDeleteEnabled;
-
- private final String[] spaces;
-
- private final String[] subclassClosure;
-
- private final String[] subclassTableNameClosure;
- private final String[][] subclassTableKeyColumnClosure;
- private final boolean[] isClassOrSuperclassTable;
-
- // properties of this class, including inherited properties
- private final int[] naturalOrderPropertyTableNumbers;
- private final int[] propertyTableNumbers;
-
- // the closure of all properties in the entire hierarchy including
- // subclasses and superclasses of this class
- private final int[] subclassPropertyTableNumberClosure;
-
- // the closure of all columns used by the entire hierarchy including
- // subclasses and superclasses of this class
- private final int[] subclassColumnTableNumberClosure;
- private final int[] subclassFormulaTableNumberClosure;
-
- // subclass discrimination works by assigning particular
- // values to certain combinations of null primary key
- // values in the outer join using an SQL CASE
- private final Map subclassesByDiscriminatorValue = new HashMap();
- private final String[] discriminatorValues;
- private final String[] notNullColumnNames;
- private final int[] notNullColumnTableNumbers;
-
- private final String[] constraintOrderedTableNames;
- private final String[][] constraintOrderedKeyColumnNames;
-
- private final String discriminatorSQLString;
-
- //INITIALIZATION:
-
- public JoinedSubclassEntityPersister(
- final PersistentClass persistentClass,
- final EntityRegionAccessStrategy cacheAccessStrategy,
- final SessionFactoryImplementor factory,
- final Mapping mapping) throws HibernateException {
-
- super( persistentClass, cacheAccessStrategy, 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 keyColumnReaders = new ArrayList();
- ArrayList keyColumnReaderTemplates = 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];
- String[] keyColReaders = new String[idColumnSpan];
- String[] keyColReaderTemplates = new String[idColumnSpan];
- Iterator citer = key.getColumnIterator();
- for ( int k=0; k<idColumnSpan; k++ ) {
- Column column = (Column) citer.next();
- keyCols[k] = column.getQuotedName( factory.getDialect() );
- keyColReaders[k] = column.getReadExpr( factory.getDialect() );
- keyColReaderTemplates[k] = column.getTemplate( factory.getDialect(),
factory.getSqlFunctionRegistry() );
- }
- keyColumns.add(keyCols);
- keyColumnReaders.add(keyColReaders);
- keyColumnReaderTemplates.add(keyColReaderTemplates);
- cascadeDeletes.add( new Boolean( key.isCascadeDeleteEnabled() &&
factory.getDialect().supportsCascadeDelete() ) );
- }
- naturalOrderTableNames = ArrayHelper.toStringArray(tables);
- naturalOrderTableKeyColumns = ArrayHelper.to2DStringArray(keyColumns);
- naturalOrderTableKeyColumnReaders = ArrayHelper.to2DStringArray(keyColumnReaders);
- naturalOrderTableKeyColumnReaderTemplates =
ArrayHelper.to2DStringArray(keyColumnReaderTemplates);
- 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);
- tableKeyColumnReaders = reverse(naturalOrderTableKeyColumnReaders);
- tableKeyColumnReaderTemplates = reverse(naturalOrderTableKeyColumnReaderTemplates);
- 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);
-
- }
-
- /*public void postInstantiate() throws MappingException {
- super.postInstantiate();
- //TODO: other lock modes?
- loader = createEntityLoader(LockMode.NONE, CollectionHelper.EMPTY_MAP);
- }*/
-
- public String getSubclassPropertyTableName(int i) {
- return subclassTableNameClosure[ subclassPropertyTableNumberClosure[i] ];
- }
-
- public Type getDiscriminatorType() {
- return Hibernate.INTEGER;
- }
-
- public String getDiscriminatorSQLValue() {
- return discriminatorSQLString;
- }
-
-
- public String getSubclassForDiscriminatorValue(Object value) {
- return (String) subclassesByDiscriminatorValue.get(value);
- }
-
- public Serializable[] getPropertySpaces() {
- return spaces; // don't need subclass tables, because they can't appear in
conditions
- }
-
-
- protected String getTableName(int j) {
- return naturalOrderTableNames[j];
- }
-
- protected String[] getKeyColumns(int j) {
- return naturalOrderTableKeyColumns[j];
- }
-
- protected boolean isTableCascadeDeleteEnabled(int j) {
- return naturalOrderCascadeDeleteEnabled[j];
- }
-
- protected boolean isPropertyOfTable(int property, int j) {
- return naturalOrderPropertyTableNumbers[property]==j;
- }
-
- /**
- * Load an instance using either the <tt>forUpdateLoader</tt> or the outer
joining <tt>loader</tt>,
- * depending upon the value of the <tt>lock</tt> parameter
- */
- /*public Object load(Serializable id, Object optionalObject, LockMode lockMode,
SessionImplementor session)
- throws HibernateException {
-
- if ( log.isTraceEnabled() ) log.trace( "Materializing entity: " +
MessageHelper.infoString(this, id) );
-
- final UniqueEntityLoader loader = hasQueryLoader() ?
- getQueryLoader() :
- this.loader;
- try {
-
- final Object result = loader.load(id, optionalObject, session);
-
- if (result!=null) lock(id, getVersion(result), result, lockMode, session);
-
- return result;
-
- }
- catch (SQLException sqle) {
- throw new JDBCException( "could not load by id: " +
MessageHelper.infoString(this, id), sqle );
- }
- }*/
-
- private static final void reverse(Object[] objects, int len) {
- 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 String[] reverse(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 String[][] reverse(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 String fromTableFragment(String alias) {
- return getTableName() + ' ' + alias;
- }
-
- public String getTableName() {
- return tableNames[0];
- }
-
- private static int getTableId(String tableName, 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 void addDiscriminatorToSelect(SelectFragment select, String name, String suffix)
{
- if ( hasSubclasses() ) {
- select.setExtraSelectList( discriminatorFragment(name), getDiscriminatorAlias() );
- }
- }
-
- private CaseFragment discriminatorFragment(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 String filterFragment(String alias) {
- return hasWhere() ?
- " and " + getSQLWhereString( generateFilterConditionAlias( alias ) ) :
- "";
- }
-
- public String generateFilterConditionAlias(String rootAlias) {
- return generateTableAlias( rootAlias, tableSpan-1 );
- }
-
- public String[] getIdentifierColumnNames() {
- return tableKeyColumns[0];
- }
-
- public String[] getIdentifierColumnReaderTemplates() {
- return tableKeyColumnReaderTemplates[0];
- }
-
- public String[] getIdentifierColumnReaders() {
- return tableKeyColumnReaders[0];
- }
-
- public String[] toColumns(String alias, String propertyName) throws QueryException {
-
- 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);
- }
-
- }
-
- protected int[] getPropertyTableNumbersInSelect() {
- return propertyTableNumbers;
- }
-
- protected int getSubclassPropertyTableNumber(int i) {
- return subclassPropertyTableNumberClosure[i];
- }
-
- public int getTableSpan() {
- return tableSpan;
- }
-
- public boolean isMultiTable() {
- return true;
- }
-
- protected int[] getSubclassColumnTableNumberClosure() {
- return subclassColumnTableNumberClosure;
- }
-
- protected int[] getSubclassFormulaTableNumberClosure() {
- return subclassFormulaTableNumberClosure;
- }
-
- protected int[] getPropertyTableNumbers() {
- return naturalOrderPropertyTableNumbers;
- }
-
- protected String[] getSubclassTableKeyColumns(int j) {
- return subclassTableKeyColumnClosure[j];
- }
-
- public String getSubclassTableName(int j) {
- return subclassTableNameClosure[j];
- }
-
- public int getSubclassTableSpan() {
- return subclassTableNameClosure.length;
- }
-
- protected boolean isClassOrSuperclassTable(int j) {
- return isClassOrSuperclassTable[j];
- }
-
- public String getPropertyTableName(String propertyName) {
- Integer index = getEntityMetamodel().getPropertyIndexOrNull(propertyName);
- if ( index == null ) {
- return null;
- }
- return tableNames[ propertyTableNumbers[ index.intValue() ] ];
- }
-
- public String[] getConstraintOrderedTableNameClosure() {
- return constraintOrderedTableNames;
- }
-
- public String[][] getContraintOrderedTableKeyColumnClosure() {
- return constraintOrderedKeyColumnNames;
- }
-
- public String getRootTableName() {
- return naturalOrderTableNames[0];
- }
-
- public String getRootTableAlias(String drivingAlias) {
- return generateTableAlias( drivingAlias, getTableId( getRootTableName(), tableNames )
);
- }
-
- public Declarer getSubclassPropertyDeclarer(String propertyPath) {
- if ( "class".equals( propertyPath ) ) {
- // special case where we need to force incloude all subclass joins
- return Declarer.SUBCLASS;
- }
- return super.getSubclassPropertyDeclarer( propertyPath );
- }
-}
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ *
+ */
+package org.hibernate.persister.entity;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.MappingException;
+import org.hibernate.QueryException;
+import org.hibernate.cache.access.EntityRegionAccessStrategy;
+import org.hibernate.engine.ExecuteUpdateResultCheckStyle;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.Versioning;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.Join;
+import org.hibernate.mapping.KeyValue;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.Selectable;
+import org.hibernate.mapping.Subclass;
+import org.hibernate.mapping.Table;
+import org.hibernate.sql.CaseFragment;
+import org.hibernate.sql.SelectFragment;
+import org.hibernate.type.AbstractType;
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * An <tt>EntityPersister</tt> implementing the normalized
"table-per-subclass"
+ * mapping strategy
+ *
+ * @author Gavin King
+ */
+public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
+
+ // the class hierarchy structure
+ private final int tableSpan;
+ private final String[] tableNames;
+ private final String[] naturalOrderTableNames;
+ private final String[][] tableKeyColumns;
+ private final String[][] tableKeyColumnReaders;
+ private final String[][] tableKeyColumnReaderTemplates;
+ private final String[][] naturalOrderTableKeyColumns;
+ private final String[][] naturalOrderTableKeyColumnReaders;
+ private final String[][] naturalOrderTableKeyColumnReaderTemplates;
+ private final boolean[] naturalOrderCascadeDeleteEnabled;
+
+ private final String[] spaces;
+
+ private final String[] subclassClosure;
+
+ private final String[] subclassTableNameClosure;
+ private final String[][] subclassTableKeyColumnClosure;
+ private final boolean[] isClassOrSuperclassTable;
+
+ // properties of this class, including inherited properties
+ private final int[] naturalOrderPropertyTableNumbers;
+ private final int[] propertyTableNumbers;
+
+ // the closure of all properties in the entire hierarchy including
+ // subclasses and superclasses of this class
+ private final int[] subclassPropertyTableNumberClosure;
+
+ // the closure of all columns used by the entire hierarchy including
+ // subclasses and superclasses of this class
+ private final int[] subclassColumnTableNumberClosure;
+ private final int[] subclassFormulaTableNumberClosure;
+
+ private final boolean[] subclassTableSequentialSelect;
+ private final boolean[] subclassTableIsLazyClosure;
+
+ // subclass discrimination works by assigning particular
+ // values to certain combinations of null primary key
+ // values in the outer join using an SQL CASE
+ private final Map subclassesByDiscriminatorValue = new HashMap();
+ private final String[] discriminatorValues;
+ private final String[] notNullColumnNames;
+ private final int[] notNullColumnTableNumbers;
+
+ private final String[] constraintOrderedTableNames;
+ private final String[][] constraintOrderedKeyColumnNames;
+
+ private final String discriminatorSQLString;
+
+ //INITIALIZATION:
+
+ public JoinedSubclassEntityPersister(
+ final PersistentClass persistentClass,
+ final EntityRegionAccessStrategy cacheAccessStrategy,
+ final SessionFactoryImplementor factory,
+ final Mapping mapping) throws HibernateException {
+
+ super( persistentClass, cacheAccessStrategy, 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 keyColumnReaders = new ArrayList();
+ ArrayList keyColumnReaderTemplates = 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];
+ String[] keyColReaders = new String[idColumnSpan];
+ String[] keyColReaderTemplates = new String[idColumnSpan];
+ Iterator citer = key.getColumnIterator();
+ for ( int k=0; k<idColumnSpan; k++ ) {
+ Column column = (Column) citer.next();
+ keyCols[k] = column.getQuotedName( factory.getDialect() );
+ keyColReaders[k] = column.getReadExpr( factory.getDialect() );
+ keyColReaderTemplates[k] = column.getTemplate( factory.getDialect(),
factory.getSqlFunctionRegistry() );
+ }
+ keyColumns.add(keyCols);
+ keyColumnReaders.add(keyColReaders);
+ keyColumnReaderTemplates.add(keyColReaderTemplates);
+ cascadeDeletes.add( new Boolean( key.isCascadeDeleteEnabled() &&
factory.getDialect().supportsCascadeDelete() ) );
+ }
+
+ //Span of the tables directly mapped by this entity and super-classes, if any
+ int coreTableSpan = tables.size();
+
+ Iterator joinIter = persistentClass.getJoinClosureIterator();
+ while ( joinIter.hasNext() ) {
+ Join join = (Join) joinIter.next();
+
+ Table tab = join.getTable();
+
+ String tabname = tab.getQualifiedName(
+ factory.getDialect(),
+ factory.getSettings().getDefaultCatalogName(),
+ factory.getSettings().getDefaultSchemaName()
+ );
+ tables.add(tabname);
+
+ KeyValue key = join.getKey();
+ int joinIdColumnSpan = key.getColumnSpan();
+
+ String[] keyCols = new String[joinIdColumnSpan];
+ String[] keyColReaders = new String[joinIdColumnSpan];
+ String[] keyColReaderTemplates = new String[joinIdColumnSpan];
+
+ Iterator citer = key.getColumnIterator();
+
+ for ( int k=0; k<joinIdColumnSpan; k++ ) {
+ Column column = (Column) citer.next();
+ keyCols[k] = column.getQuotedName( factory.getDialect() );
+ keyColReaders[k] = column.getReadExpr( factory.getDialect() );
+ keyColReaderTemplates[k] = column.getTemplate( factory.getDialect(),
factory.getSqlFunctionRegistry() );
+ }
+ keyColumns.add(keyCols);
+ keyColumnReaders.add(keyColReaders);
+ keyColumnReaderTemplates.add(keyColReaderTemplates);
+ cascadeDeletes.add( new Boolean( key.isCascadeDeleteEnabled() &&
factory.getDialect().supportsCascadeDelete() ) );
+ }
+
+ naturalOrderTableNames = ArrayHelper.toStringArray(tables);
+ naturalOrderTableKeyColumns = ArrayHelper.to2DStringArray(keyColumns);
+ naturalOrderTableKeyColumnReaders = ArrayHelper.to2DStringArray(keyColumnReaders);
+ naturalOrderTableKeyColumnReaderTemplates =
ArrayHelper.to2DStringArray(keyColumnReaderTemplates);
+ naturalOrderCascadeDeleteEnabled = ArrayHelper.toBooleanArray(cascadeDeletes);
+
+ ArrayList subtables = new ArrayList();
+ ArrayList isConcretes = new ArrayList();
+ ArrayList isDeferreds = new ArrayList();
+ ArrayList isLazies = new ArrayList();
+
+ keyColumns = new ArrayList();
+ titer = persistentClass.getSubclassTableClosureIterator();
+ while ( titer.hasNext() ) {
+ Table tab = (Table) titer.next();
+ isConcretes.add( new Boolean( persistentClass.isClassOrSuperclassTable(tab) ) );
+ isDeferreds.add(Boolean.FALSE);
+ isLazies.add(Boolean.FALSE);
+ 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);
+ }
+
+ //Add joins
+ joinIter = persistentClass.getSubclassJoinClosureIterator();
+ while ( joinIter.hasNext() ) {
+ Join join = (Join) joinIter.next();
+
+ Table tab = join.getTable();
+
+ isConcretes.add( new Boolean( persistentClass.isClassOrSuperclassTable(tab) ) );
+ isDeferreds.add( new Boolean( join.isSequentialSelect() ) );
+ isLazies.add(new Boolean(join.isLazy()));
+
+ 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);
+
+ }
+
+ String [] naturalOrderSubclassTableNameClosure = ArrayHelper.toStringArray(subtables);
+ String[][] naturalOrderSubclassTableKeyColumnClosure =
ArrayHelper.to2DStringArray(keyColumns);
+ isClassOrSuperclassTable = ArrayHelper.toBooleanArray(isConcretes);
+ subclassTableSequentialSelect = ArrayHelper.toBooleanArray(isDeferreds);
+ subclassTableIsLazyClosure = ArrayHelper.toBooleanArray(isLazies);
+
+ constraintOrderedTableNames = new String[naturalOrderSubclassTableNameClosure.length];
+ constraintOrderedKeyColumnNames = new
String[naturalOrderSubclassTableNameClosure.length][];
+ int currentPosition = 0;
+ for ( int i = naturalOrderSubclassTableNameClosure.length - 1; i >= 0 ; i--,
currentPosition++ ) {
+ constraintOrderedTableNames[currentPosition] =
naturalOrderSubclassTableNameClosure[i];
+ constraintOrderedKeyColumnNames[currentPosition] =
naturalOrderSubclassTableKeyColumnClosure[i];
+ }
+
+ /**
+ * Suppose an entity Client extends Person, mapped to the tables CLIENT and PERSON
respectively.
+ * For the Client entity:
+ * naturalOrderTableNames -> PERSON, CLIENT; this reflects the sequence in which the
tables are
+ * added to the meta-data when the annotated entities are processed.
+ * However, in some instances, for example when generating joins, the CLIENT table
needs to be
+ * the first table as it will the driving table.
+ * tableNames -> CLIENT, PERSON
+ */
+
+ tableSpan = naturalOrderTableNames.length;
+ tableNames = reverse(naturalOrderTableNames, coreTableSpan);
+ tableKeyColumns = reverse(naturalOrderTableKeyColumns, coreTableSpan);
+ tableKeyColumnReaders = reverse(naturalOrderTableKeyColumnReaders, coreTableSpan);
+ tableKeyColumnReaderTemplates = reverse(naturalOrderTableKeyColumnReaderTemplates,
coreTableSpan);
+ subclassTableNameClosure = reverse(naturalOrderSubclassTableNameClosure,
coreTableSpan);
+ subclassTableKeyColumnClosure = reverse(naturalOrderSubclassTableKeyColumnClosure,
coreTableSpan);
+
+ 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 = coreTableSpan-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." );
+ }
+
+ joinIter = persistentClass.getJoinClosureIterator();
+ int j = coreTableSpan;
+ while ( joinIter.hasNext() ) {
+ Join join = (Join) joinIter.next();
+
+ customSQLInsert[j] = join.getCustomSQLInsert();
+ insertCallable[j] = customSQLInsert[j] != null &&
join.isCustomInsertCallable();
+ insertResultCheckStyles[j] = join.getCustomSQLInsertCheckStyle() == null
+ ? ExecuteUpdateResultCheckStyle.determineDefault(
customSQLInsert[j], insertCallable[j] )
+ : join.getCustomSQLInsertCheckStyle();
+ customSQLUpdate[j] = join.getCustomSQLUpdate();
+ updateCallable[j] = customSQLUpdate[j] != null &&
join.isCustomUpdateCallable();
+ updateResultCheckStyles[j] = join.getCustomSQLUpdateCheckStyle() == null
+ ? ExecuteUpdateResultCheckStyle.determineDefault(
customSQLUpdate[j], updateCallable[j] )
+ : join.getCustomSQLUpdateCheckStyle();
+ customSQLDelete[j] = join.getCustomSQLDelete();
+ deleteCallable[j] = customSQLDelete[j] != null &&
join.isCustomDeleteCallable();
+ deleteResultCheckStyles[j] = join.getCustomSQLDeleteCheckStyle() == null
+ ? ExecuteUpdateResultCheckStyle.determineDefault(
customSQLDelete[j], deleteCallable[j] )
+ : join.getCustomSQLDeleteCheckStyle();
+ j++;
+ }
+
+ // 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);
+
+ }
+
+ protected boolean isSubclassTableSequentialSelect(int j) {
+ return subclassTableSequentialSelect[j] && !isClassOrSuperclassTable[j];
+ }
+
+
+ /*public void postInstantiate() throws MappingException {
+ super.postInstantiate();
+ //TODO: other lock modes?
+ loader = createEntityLoader(LockMode.NONE, CollectionHelper.EMPTY_MAP);
+ }*/
+
+ public String getSubclassPropertyTableName(int i) {
+ return subclassTableNameClosure[ subclassPropertyTableNumberClosure[i] ];
+ }
+
+ public Type getDiscriminatorType() {
+ return Hibernate.INTEGER;
+ }
+
+ public String getDiscriminatorSQLValue() {
+ return discriminatorSQLString;
+ }
+
+
+ public String getSubclassForDiscriminatorValue(Object value) {
+ return (String) subclassesByDiscriminatorValue.get(value);
+ }
+
+ public Serializable[] getPropertySpaces() {
+ return spaces; // don't need subclass tables, because they can't appear in
conditions
+ }
+
+
+ protected String getTableName(int j) {
+ return naturalOrderTableNames[j];
+ }
+
+ protected String[] getKeyColumns(int j) {
+ return naturalOrderTableKeyColumns[j];
+ }
+
+ protected boolean isTableCascadeDeleteEnabled(int j) {
+ return naturalOrderCascadeDeleteEnabled[j];
+ }
+
+ protected boolean isPropertyOfTable(int property, int j) {
+ return naturalOrderPropertyTableNumbers[property]==j;
+ }
+
+ /**
+ * Load an instance using either the <tt>forUpdateLoader</tt> or the outer
joining <tt>loader</tt>,
+ * depending upon the value of the <tt>lock</tt> parameter
+ */
+ /*public Object load(Serializable id, Object optionalObject, LockMode lockMode,
SessionImplementor session)
+ throws HibernateException {
+
+ if ( log.isTraceEnabled() ) log.trace( "Materializing entity: " +
MessageHelper.infoString(this, id) );
+
+ final UniqueEntityLoader loader = hasQueryLoader() ?
+ getQueryLoader() :
+ this.loader;
+ try {
+
+ final Object result = loader.load(id, optionalObject, session);
+
+ if (result!=null) lock(id, getVersion(result), result, lockMode, session);
+
+ return result;
+
+ }
+ catch (SQLException sqle) {
+ throw new JDBCException( "could not load by id: " +
MessageHelper.infoString(this, id), sqle );
+ }
+ }*/
+
+ private static final void reverse(Object[] objects, int len) {
+ 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];
+ }
+ }
+
+
+ /**
+ * Reverse the first n elements of the incoming array
+ * @param objects
+ * @param n
+ * @return New array with the first n elements in reversed order
+ */
+ private static final String[] reverse(String [] objects, int n) {
+
+ int size = objects.length;
+ String[] temp = new String[size];
+
+ for (int i=0; i<n; i++) {
+ temp[i] = objects[n-i-1];
+ }
+
+ for (int i=n; i < size; i++) {
+ temp[i] = objects[i];
+ }
+
+ return temp;
+ }
+
+ /**
+ * Reverse the first n elements of the incoming array
+ * @param objects
+ * @param n
+ * @return New array with the first n elements in reversed order
+ */
+ private static final String[][] reverse(String[][] objects, int n) {
+ int size = objects.length;
+ String[][] temp = new String[size][];
+ for (int i=0; i<n; i++) {
+ temp[i] = objects[n-i-1];
+ }
+
+ for (int i=n; i<size; i++) {
+ temp[i] = objects[i];
+ }
+
+ return temp;
+ }
+
+
+
+ public String fromTableFragment(String alias) {
+ return getTableName() + ' ' + alias;
+ }
+
+ public String getTableName() {
+ return tableNames[0];
+ }
+
+ private static int getTableId(String tableName, 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 void addDiscriminatorToSelect(SelectFragment select, String name, String suffix)
{
+ if ( hasSubclasses() ) {
+ select.setExtraSelectList( discriminatorFragment(name), getDiscriminatorAlias() );
+ }
+ }
+
+ private CaseFragment discriminatorFragment(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 String filterFragment(String alias) {
+ return hasWhere() ?
+ " and " + getSQLWhereString( generateFilterConditionAlias( alias ) ) :
+ "";
+ }
+
+ public String generateFilterConditionAlias(String rootAlias) {
+ return generateTableAlias( rootAlias, tableSpan-1 );
+ }
+
+ public String[] getIdentifierColumnNames() {
+ return tableKeyColumns[0];
+ }
+
+ public String[] getIdentifierColumnReaderTemplates() {
+ return tableKeyColumnReaderTemplates[0];
+ }
+
+ public String[] getIdentifierColumnReaders() {
+ return tableKeyColumnReaders[0];
+ }
+
+ public String[] toColumns(String alias, String propertyName) throws QueryException {
+
+ 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);
+ }
+
+ }
+
+ protected int[] getPropertyTableNumbersInSelect() {
+ return propertyTableNumbers;
+ }
+
+ protected int getSubclassPropertyTableNumber(int i) {
+ return subclassPropertyTableNumberClosure[i];
+ }
+
+ public int getTableSpan() {
+ return tableSpan;
+ }
+
+ public boolean isMultiTable() {
+ return true;
+ }
+
+ protected int[] getSubclassColumnTableNumberClosure() {
+ return subclassColumnTableNumberClosure;
+ }
+
+ protected int[] getSubclassFormulaTableNumberClosure() {
+ return subclassFormulaTableNumberClosure;
+ }
+
+ protected int[] getPropertyTableNumbers() {
+ return naturalOrderPropertyTableNumbers;
+ }
+
+ protected String[] getSubclassTableKeyColumns(int j) {
+ return subclassTableKeyColumnClosure[j];
+ }
+
+ public String getSubclassTableName(int j) {
+ return subclassTableNameClosure[j];
+ }
+
+ public int getSubclassTableSpan() {
+ return subclassTableNameClosure.length;
+ }
+
+ protected boolean isSubclassTableLazy(int j) {
+ return subclassTableIsLazyClosure[j];
+ }
+
+
+ protected boolean isClassOrSuperclassTable(int j) {
+ return isClassOrSuperclassTable[j];
+ }
+
+ public String getPropertyTableName(String propertyName) {
+ Integer index = getEntityMetamodel().getPropertyIndexOrNull(propertyName);
+ if ( index == null ) {
+ return null;
+ }
+ return tableNames[ propertyTableNumbers[ index.intValue() ] ];
+ }
+
+ public String[] getConstraintOrderedTableNameClosure() {
+ return constraintOrderedTableNames;
+ }
+
+ public String[][] getContraintOrderedTableKeyColumnClosure() {
+ return constraintOrderedKeyColumnNames;
+ }
+
+ public String getRootTableName() {
+ return naturalOrderTableNames[0];
+ }
+
+ public String getRootTableAlias(String drivingAlias) {
+ return generateTableAlias( drivingAlias, getTableId( getRootTableName(), tableNames )
);
+ }
+
+ public Declarer getSubclassPropertyDeclarer(String propertyPath) {
+ if ( "class".equals( propertyPath ) ) {
+ // special case where we need to force incloude all subclass joins
+ return Declarer.SUBCLASS;
+ }
+ return super.getSubclassPropertyDeclarer( propertyPath );
+ }
+}