/// <summary> /// Constructs the NormalizedEntityPerister for the PersistentClass. /// </summary> /// <param name="persistentClass">The PersistentClass to create the EntityPersister for.</param> /// <param name="cache">The configured <see cref="ICacheConcurrencyStrategy" />.</param> /// <param name="factory">The SessionFactory that this EntityPersister will be stored in.</param> /// <param name="mapping">The mapping used to retrieve type information.</param> public JoinedSubclassEntityPersister(PersistentClass persistentClass, ICacheConcurrencyStrategy cache, ISessionFactoryImplementor factory, IMapping mapping) : base(persistentClass, cache, factory) { #region DISCRIMINATOR if (persistentClass.IsPolymorphic) { try { discriminatorValue = persistentClass.SubclassId; 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.OptimisticLock.Version) { throw new MappingException(string.Format("optimistic-lock=all|dirty not supported for joined-subclass mappings [{0}]", EntityName)); } #endregion #region MULTITABLES int idColumnSpan = IdentifierColumnSpan; List <string> tables = new List <string>(); List <string[]> keyColumns = new List <string[]>(); List <bool> cascadeDeletes = new List <bool>(); using (var kiter = persistentClass.KeyClosureIterator.GetEnumerator()) { foreach (var tab in persistentClass.TableClosureIterator) { kiter.MoveNext(); var key = kiter.Current; var tabname = tab.GetQualifiedName(factory.Dialect, factory.Settings.DefaultCatalogName, factory.Settings.DefaultSchemaName); tables.Add(tabname); var keyCols = new List <string>(idColumnSpan); var enumerableKCols = new SafetyEnumerable <Column>(key.ColumnIterator); foreach (var kcol in enumerableKCols) { keyCols.Add(kcol.GetQuotedName(factory.Dialect)); } keyColumns.Add(keyCols.ToArray()); cascadeDeletes.Add(key.IsCascadeDeleteEnabled && factory.Dialect.SupportsCascadeDelete); } } naturalOrderTableNames = tables.ToArray(); naturalOrderTableKeyColumns = keyColumns.ToArray(); naturalOrderCascadeDeleteEnabled = cascadeDeletes.ToArray(); List <string> subtables = new List <string>(); List <bool> isConcretes = new List <bool>(); keyColumns = new List <string[]>(); foreach (Table tab in persistentClass.SubclassTableClosureIterator) { isConcretes.Add(persistentClass.IsClassOrSuperclassTable(tab)); string tabname = tab.GetQualifiedName(factory.Dialect, factory.Settings.DefaultCatalogName, factory.Settings.DefaultSchemaName); subtables.Add(tabname); List <string> key = new List <string>(idColumnSpan); foreach (Column column in tab.PrimaryKey.ColumnIterator) { key.Add(column.GetQuotedName(factory.Dialect)); } keyColumns.Add(key.ToArray()); } subclassTableNameClosure = subtables.ToArray(); subclassTableKeyColumnClosure = keyColumns.ToArray(); isClassOrSuperclassTable = isConcretes.ToArray(); 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, persistentClass.SynchronizedTables.ToArray()); // Custom sql customSQLInsert = new SqlString[tableSpan]; customSQLUpdate = new SqlString[tableSpan]; customSQLDelete = new SqlString[tableSpan]; insertCallable = new bool[tableSpan]; updateCallable = new bool[tableSpan]; deleteCallable = new bool[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.CustomSQLInsert; insertCallable[jk] = customSQLInsert[jk] != null && pc.IsCustomInsertCallable; insertResultCheckStyles[jk] = pc.CustomSQLInsertCheckStyle ?? ExecuteUpdateResultCheckStyle.DetermineDefault(customSQLInsert[jk], insertCallable[jk]); customSQLUpdate[jk] = pc.CustomSQLUpdate; updateCallable[jk] = customSQLUpdate[jk] != null && pc.IsCustomUpdateCallable; updateResultCheckStyles[jk] = pc.CustomSQLUpdateCheckStyle ?? ExecuteUpdateResultCheckStyle.DetermineDefault(customSQLUpdate[jk], updateCallable[jk]); customSQLDelete[jk] = pc.CustomSQLDelete; deleteCallable[jk] = customSQLDelete[jk] != null && pc.IsCustomDeleteCallable; deleteResultCheckStyles[jk] = pc.CustomSQLDeleteCheckStyle ?? ExecuteUpdateResultCheckStyle.DetermineDefault(customSQLDelete[jk], deleteCallable[jk]); jk--; pc = pc.Superclass; } if (jk != -1) { throw new AssertionFailure("Tablespan does not match height of joined-subclass hierarchy."); } #endregion #region PROPERTIES int hydrateSpan = PropertySpan; naturalOrderPropertyTableNumbers = new int[hydrateSpan]; propertyTableNumbers = new int[hydrateSpan]; int i2 = 0; foreach (Property prop in persistentClass.PropertyClosureIterator) { string tabname = prop.Value.Table.GetQualifiedName(factory.Dialect, factory.Settings.DefaultCatalogName, factory.Settings.DefaultSchemaName); propertyTableNumbers[i2] = GetTableId(tabname, tableNames); naturalOrderPropertyTableNumbers[i2] = GetTableId(tabname, naturalOrderTableNames); i2++; } // subclass closure properties List <int> columnTableNumbers = new List <int>(); List <int> formulaTableNumbers = new List <int>(); List <int> propTableNumbers = new List <int>(); foreach (Property prop in persistentClass.SubclassPropertyClosureIterator) { Table tab = prop.Value.Table; string tabname = tab.GetQualifiedName(factory.Dialect, factory.Settings.DefaultCatalogName, factory.Settings.DefaultSchemaName); int tabnum = GetTableId(tabname, subclassTableNameClosure); propTableNumbers.Add(tabnum); foreach (ISelectable thing in prop.ColumnIterator) { if (thing.IsFormula) { formulaTableNumbers.Add(tabnum); } else { columnTableNumbers.Add(tabnum); } } } subclassColumnTableNumberClosure = columnTableNumbers.ToArray(); subclassPropertyTableNumberClosure = propTableNumbers.ToArray(); subclassFormulaTableNumberClosure = formulaTableNumbers.ToArray(); #endregion #region SUBCLASSES int subclassSpan = persistentClass.SubclassSpan + 1; subclassClosure = new string[subclassSpan]; subclassClosure[subclassSpan - 1] = EntityName; if (persistentClass.IsPolymorphic) { subclassesByDiscriminatorValue[discriminatorValue] = EntityName; discriminatorValues = new string[subclassSpan]; discriminatorValues[subclassSpan - 1] = discriminatorSQLString; notNullColumnTableNumbers = new int[subclassSpan]; int id = GetTableId( persistentClass.Table.GetQualifiedName(factory.Dialect, factory.Settings.DefaultCatalogName, factory.Settings.DefaultSchemaName), 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; } int k2 = 0; foreach (Subclass sc in persistentClass.SubclassIterator) { subclassClosure[k2] = sc.EntityName; 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 int subclassId = sc.SubclassId; //new Integer(k+1); subclassesByDiscriminatorValue[subclassId] = sc.EntityName; discriminatorValues[k2] = subclassId.ToString(); int id = GetTableId( sc.Table.GetQualifiedName(factory.Dialect, factory.Settings.DefaultCatalogName, factory.Settings.DefaultSchemaName), subclassTableNameClosure); notNullColumnTableNumbers[k2] = id; notNullColumnNames[k2] = subclassTableKeyColumnClosure[id][0]; //( (Column) sc.getTable().getPrimaryKey().getColumnIterator().next() ).getName(); } } catch (Exception e) { throw new MappingException("Error parsing discriminator value", e); } k2++; } #endregion InitLockers(); InitSubclassPropertyAliasesMap(persistentClass); PostConstruct(mapping); }