/// <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);
        }