📄 joinedsubclassentitypersister.cs
字号:
using System;
using System.Collections.Generic;
using NHibernate.Cache;
using NHibernate.Engine;
using NHibernate.Mapping;
using NHibernate.SqlCommand;
using NHibernate.Type;
using NHibernate.Util;
namespace NHibernate.Persister.Entity
{
/// <summary>
/// A <c>IEntityPersister</c> implementing the normalized "table-per-subclass" mapping strategy
/// </summary>
public class JoinedSubclassEntityPersister : AbstractEntityPersister
{
// the class hierarchy structure
private readonly int tableSpan;
// all of the table names that this Persister uses for just its data
// it is indexed as tableNames[tableIndex]
// for both the superclass and subclass the index of 0=basetable
// for the base class there is only 1 table
private readonly string[] tableNames;
private readonly string[] naturalOrderTableNames;
// two dimensional array that is indexed as tableKeyColumns[tableIndex][columnIndex]
private readonly string[][] tableKeyColumns;
private readonly string[][] naturalOrderTableKeyColumns;
private readonly bool[] naturalOrderCascadeDeleteEnabled;
private readonly string[] spaces;
// the Type of objects for the subclass
// the array is indexed as subclassClosure[subclassIndex].
// The length of the array is the number of subclasses + 1 for the Base Class.
// The last index of the array contains the Type for the Base Class.
// in the example of JoinedSubclassBase/One the values are :
// subclassClosure[0] = JoinedSubclassOne
// subclassClosure[1] = JoinedSubclassBase
private readonly string[] subclassClosure;
// the names of the tables for the subclasses
// the array is indexed as subclassTableNameColsure[tableIndex] = "tableName"
// for the RootClass the index 0 is the base table
// for the subclass the index 0 is also the base table
private readonly string[] subclassTableNameClosure;
// the names of the columns that are the Keys for the table - I don't know why they would
// be different - I thought the subclasses didn't have their own PK, but used the one generated
// by the base class??
// the array is indexed as subclassTableKeyColumnClosure[tableIndex][columnIndex] = "columnName"
private readonly string[][] subclassTableKeyColumnClosure;
// TODO: figure out what this is being used for - when initializing the base class the values
// are isClassOrSuperclassTable[0] = true, isClassOrSuperclassTable[1] = false
// when initialized the subclass the values are [0]=true, [1]=true. I believe this is telling
// us that the table is used to populate this class or the superclass.
// I would guess this is telling us specifically which tables this Persister will write information to.
private readonly bool[] isClassOrSuperclassTable;
// properties of this class, including inherited properties
private readonly int[] naturalOrderPropertyTableNumbers;
// the index of the table that the property is coming from
// the array is indexed as propertyTables[propertyIndex] = tableIndex
private readonly int[] propertyTableNumbers;
// the closure of all properties in the entire hierarchy including
// subclasses and superclasses of this class
private readonly int[] subclassPropertyTableNumberClosure;
// the closure of all columns used by the entire hierarchy including
// subclasses and superclasses of this class
private readonly int[] subclassColumnTableNumberClosure;
private readonly 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 readonly Dictionary<object, string> subclassesByDiscriminatorValue = new Dictionary<object, string>();
private readonly string[] discriminatorValues;
private readonly string[] notNullColumnNames;
private readonly int[] notNullColumnTableNumbers;
private readonly string[] constraintOrderedTableNames;
private readonly string[][] constraintOrderedKeyColumnNames;
private readonly string discriminatorSQLString;
private readonly object discriminatorValue;
/// <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>();
IEnumerator<IKeyValue> kiter = persistentClass.KeyClosureIterator.GetEnumerator();
foreach (Table tab in persistentClass.TableClosureIterator)
{
kiter.MoveNext();
IKeyValue key = kiter.Current;
string tabname = tab.GetQualifiedName(factory.Dialect, factory.Settings.DefaultCatalogName, factory.Settings.DefaultSchemaName);
tables.Add(tabname);
List<string> keyCols = new List<string>(idColumnSpan);
IEnumerable<Column> enumerableKCols = new SafetyEnumerable<Column>(key.ColumnIterator);
foreach (Column 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, ArrayHelper.ToStringArray(persistentClass.SynchronizedTables));
// 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -