Fluent NHibernate中的继承映射策略






4.76/5 (20投票s)
本文通过一个实际示例,解释和说明了 Entity Developer 如何在 Fluent NHibernate 中实现对不同继承映射策略的支持。
引言
本文通过一个实际示例,解释和说明了 Entity Developer 如何在 Fluent NHibernate 中实现对不同继承映射策略的支持。
支持的映射策略如下:
- 每个层次表(TPH);
- 每个类型表(TPT);
- 每个具体类表(TPC)。
每个层次表(TPH)
在 TPH 中,继承树只通过一个表创建。TPH 继承依赖于条件映射,该映射由一个条件定义,例如数据库中的鉴别器字段。该条件用于将记录定义为不同的类型。
例如,数据库包含 TPH_Animal 表:
ClassType 字段是鉴别器,用于确定动物的种类:鳄鱼、蛇、狗或马。
我们执行以下操作序列:创建 NHibernate 模型,创建类结构和继承关系,添加额外的 Fluent NHibernate 模板来生成 Fluent 映射。最终,我们得到以下模型:
下面是该模型生成的 Fluent 映射:
// mapping of the Dog class
public class DogMap : SubclassMap<Dog>
{
public DogMap()
{
// Here we define the discriminator value "Dog"
// to differentiate records of the class Dog
DiscriminatorValue(@"Dog");
// using the Map function, we define the mapping
// of the Breed property, it is possible to define
// the type of the property, its access, the name
// of the filed in the table and its server type,
// facets and other mapping settings
Map(x => x.Breed)
.Column("Breed")
.CustomType("String")
.Access.Property()
.Generated.Never()
.CustomSqlType("nvarchar")
.Length(128);
}
}
// mapping of the Shake class
public class SnakeMap : SubclassMap<Snake>
{
public SnakeMap()
{
// Here we define the discriminator value "Snake"
// to differentiate records of the class Snake
DiscriminatorValue(@"Snake");
// Using the Map functions, we define mapping
// of the Length и IsAdder properties; it is possible
// to specify the type of the property, its access,
// the name of the field in the table
// and its server type, facets and other mapping settings
Map(x => x.Length)
.Column("Length")
.CustomType("Decimal")
.Access.Property()
.Generated.Never()
.CustomSqlType("decimal")
.Precision(5)
.Scale(2);
Map(x => x.IsAdder)
.Column("IsAdder")
.CustomType("Boolean")
.Access.Property()
.Generated.Never()
.CustomSqlType("bit");
}
}
// mapping of the Horse class
public class HorseMap : SubclassMap<Horse>
{
public HorseMap()
{
// Here we define the discriminator value "Horse"
// to differentiate records of the class Horse
DiscriminatorValue(@"Horse");
// Using the Map functions, we define mapping
// of the MaximumSpeed property, it is possible to
// specify the type of the property, its access,
// the name of the field in the table and its
// server type, facets and other mapping settings
Map(x => x.MaximumSpeed)
.Column("MaximumSpeed")
.CustomType("Decimal")
.Access.Property()
.Generated.Never()
.CustomSqlType("decimal")
.Precision(4)
.Scale(2);
}
}
// mapping of the Crocodile class
public class CrocodileMap : SubclassMap<Crocodile>
{
public CrocodileMap()
{
// Here we define the discriminator value
// "Crocodile" to differentiate records of
// the class Crocodile
DiscriminatorValue(@"Crocodile");
// Using the Map functions, we define mapping
// of the Weight и Length properties, it is possible
// to specify the type of the property,
// its access, the name of the field in the table
// and its server type, facets and other mapping settings
Map(x => x.Weight)
.Column("Weight")
.CustomType("Decimal")
.Access.Property()
.Generated.Never()
.CustomSqlType("decimal")
.Not.Nullable()
.Precision(4)
.Scale(2);
Map(x => x.Length)
.Column("Length")
.CustomType("Decimal")
.Access.Property()
.Generated.Never()
.CustomSqlType("decimal")
.Precision(5)
.Scale(2);
}
}
// mapping of the base class TPHAnimal, which contains
// properties that are common for all classes
public class TPHAnimalMap : ClassMap<TPHAnimal>
{
public TPHAnimalMap()
{
// the name of the schema that contains the table
Schema("dbo");
// the name of the table for TPT inheritance
Table("TPH_Animal");
// the Id function is used for identity key mapping,
// it is possible to specify the type of
// the property, its access, the name
// of the field in the table and its server type,
// facets and other mapping settings,
// as well as to specify the class name to be used to
// generate the primary key for a new record while saving a new record
Id(x => x.ID)
.Column("ID")
.CustomType("Int32")
.Access.Property()
.CustomSqlType("int")
.Not.Nullable()
.Precision(10)
.GeneratedBy.Identity();
// here we specify the name of the column
// that will define the type of the animal
DiscriminateSubClassesOnColumn("ClassType").Not.Nullable();
// Using the Map function, we define mapping
// of common properties; it is possible to specify
// the type of the property, its access,
// the name of the field in the table and its server
// type, facets and other mapping settings
Map(x => x.FoodClassification)
.Column("FoodClassification")
.CustomType("String")
.Access.Property()
.Generated.Never()
.CustomSqlType("nvarchar")
.Not.Nullable()
.Length(128);
Map(x => x.BirthDate)
.Column("BirthDate")
.CustomType("DateTime")
.Access.Property()
.Generated.Never()
.CustomSqlType("datetime");
Map(x => x.Family)
.Column("Family")
.CustomType("String")
.Access.Property()
.Generated.Never()
.CustomSqlType("nvarchar")
.Length(128);
Map(x => x.Genus)
.Column("Genus")
.CustomType("String")
.Access.Property()
.Generated.Never()
.CustomSqlType("nvarchar")
.Length(128);
}
}
上面的代码表示生成的模型映射类。派生类 CrocodileMap
、DogMap
和 HorseMap
的映射实现了仅包含在这些类中的属性的映射,而 DiscriminatorValue
函数指定了用于确定记录是否属于该类型的那个值。
TPHAnimalMap
类确定了基类的映射,并为所有派生类描述了通用映射;此映射包含数据库中表名称、表所属模式的名称、标识键映射和通用属性的描述,而 DisciriminateSubClassesOnColumn
指定了用于确定动物种类的数据库鉴别器字段。
每个类型表(TPT)
TPT 是一种在数据库中用单独的表描述的继承。每个表提供附加的详细信息,描述一个基于另一个表(即其父表)的新类型。
例如,数据库包含以下表:TPT_Animal, TPT_Mammal, TPT_Reptile, TPT_Horse, TPT_Snake, TPT_Crocodile 和 TPT_Dog。
我们执行以下操作序列:首先,我们创建 NHibernate 模型,创建类结构和继承关系,添加可选的 Fluent NHibernate 模板来生成 Fluent 映射。最终,我们得到以下模型:
该模型生成的 Fluent 映射如下:
// mapping of the TPTHorse class
public class TPTHorseMap : SubclassMap<TPTHorse>
{
public TPTHorseMap()
{
// the name of the schema that stores the table corresponding to the type
Schema("dbo");
// the name of the table corresponding to the type
Table("TPT_Horse");
// the name of the column, against which
// the foreign key is created in the database together
// with the table TPTMammal
KeyColumn("ID");
// Using the Map function, we define mapping
// of the MaximumSpeed property, it is possible
// to specify the type of the property,
// its access, the name of the field in the table
// and its server type, facets and other mapping settings
Map(x => x.MaximumSpeed)
.Column("MaximumSpeed")
.CustomType("Decimal")
.Access.Property()
.Generated.Never()
.CustomSqlType("decimal")
.Precision(4)
.Scale(2);
}
}
// mapping of the TPTDog class
public class TPTDogMap : SubclassMap<TPTDog>
{
public TPTDogMap()
{
// the name of the schema that stores the table corresponding to the type
Schema("dbo");
// the name of the table corresponding to the type
Table("TPT_Dog");
// the name of the column, against which
// the foreign key is created in the database together
// with the table TPTMammal
KeyColumn("ID");
// Using the Map function, we define mapping
// of the Breed property, it is possible to
// specify the type of the property, its access,
// the name of the field in the table and its
// server type, facets and other mapping settings
Map(x => x.Breed)
.Column("Breed")
.CustomType("String")
.Access.Property()
.Generated.Never()
.CustomSqlType("nvarchar")
.Not.Nullable()
.Length(128);
}
}
// mapping of the TPTMammal class, which is the base class
// for the TPTDog and TPTHorse classes
public class TPTMammalMap : SubclassMap<TPTMammal>
{
public TPTMammalMap()
{
// the name of the schema that stores the table corresponding to the type
Schema("dbo");
// the name of the table corresponding to the type
Table("TPT_Mammal");
// the name of the column, against which
// the foreign key is created in the database together
// with the table TPTAnimal
KeyColumn("ID");
// Using the Map function, we define mapping
// of the Mammals property BirthDate, it is possible
// to specify the type of the property, its access,
// the name of the field in the table
// and its server type, facets and other mapping settings
Map(x => x.BirthDate)
.Column("BirthDate")
.CustomType("DateTime")
.Access.Property()
.Generated.Never()
.CustomSqlType("datetime")
.Not.Nullable();
}
}
// mapping of the TPTCrocodile class
public class TPTCrocodileMap : SubclassMap<TPTCrocodile>
{
public TPTCrocodileMap()
{
// the name of the schema that stores the table corresponding to the type
Schema("dbo");
// the name of the table corresponding to the type
Table("TPT_Crocodile");
// the name of the column, against which
// the foreign key is created in the database together
// with the table TPTReptile
KeyColumn("ID");
// Using the Map function, we define mapping
// of the Family и Genus properties, it is possible
// to specify the type of the property, its access,
// the name of the field in the table and
// its server type, facets and other mapping settings
Map(x => x.Family)
.Column("Family")
.CustomType("String")
.Access.Property()
.Generated.Never()
.CustomSqlType("nvarchar")
.Length(128);
Map(x => x.Genus)
.Column("Genus")
.CustomType("String")
.Access.Property()
.Generated.Never()
.CustomSqlType("nvarchar")
.Length(128);
}
}
// mapping of the TPTSnake class
public class TPTSnakeMap : SubclassMap<TPTSnake>
{
public TPTSnakeMap()
{
// the name of the schema that stores the table corresponding to the type
Schema("dbo");
// the name of the table corresponding to the type
Table("TPT_Snake");
// the name of the column, against which
// the foreign key is created in the database together
// with the table TPTReptile
KeyColumn("ID");
// Using the Map function, we define mapping of the IsAdder property, it is possible to
// specify the type of the property, its access,
// the name of the field in the table and
// its server type, facets and other mapping settings
Map(x => x.IsAdder)
.Column("IsAdder")
.CustomType("Boolean")
.Access.Property()
.Generated.Never()
.CustomSqlType("bit")
.Not.Nullable();
}
}
// mapping of the TPTReptile class, which is the base
// one for the TPTSnake and TPTCrocodile classes
public class TPTReptileMap : SubclassMap<TPTReptile>
{
public TPTReptileMap()
{
// the name of the schema that stores the table corresponding to the type
Schema("dbo");
// the name of the table corresponding to the type
Table("TPT_Reptile");
// the name of the column, against which
// the foreign key is created in the database together
// with the table TPTAnimal
KeyColumn("ID");
// Using the Map function, we define mapping
// for the reptile-common property Length, it is
// possible to specify the type of the property,
// its access, the name of the field in the
// table and its server type, facets and other mapping settings
Map(x => x.Length)
.Column("Length")
.CustomType("Decimal")
.Access.Property()
.Generated.Never()
.CustomSqlType("decimal")
.Not.Nullable()
.Precision(5)
.Scale(2);
}
}
// mapping of the TPTAnimal base class
public class TPTAnimalMap : ClassMap<TPTAnimal>
{
public TPTAnimalMap()
{
// the name of the schema that stores the table corresponding to the type
Schema("dbo");
// the name of the table corresponding to the type
Table("TPT_Animal");
// the Id function is used for identity key mapping,
// it is possible to specify the type of
// the property, its access, the name of the field
// in the table and its server type, facets
// and other mapping settings, as well as to specify
// the class name to be used to generate
// the primary key for a new record while saving a new record
Id(x => x.ID)
.Column("ID")
.CustomType("Int32")
.Access.Property()
.CustomSqlType("int")
.Not.Nullable()
.Precision(10)
.GeneratedBy.Identity();
// Using the Map function, we define mapping
// of the common properties; it is possible to
// specify the type of the property, its access,
// the name of the field in the table and its
// server type, facets and other mapping settings
Map(x => x.Weight)
.Column("Weight")
.CustomType("Decimal")
.Access.Property()
.Generated.Never()
.CustomSqlType("decimal")
.Not.Nullable()
.Precision(4)
.Scale(2);
Map(x => x.FoodClassification)
.Column("FoodClassification")
.CustomType("String")
.Access.Property()
.Generated.Never()
.CustomSqlType("nvarchar")
.Not.Nullable()
.Length(128);
}
}
上面的代码表示生成的模型映射类。TPTAnimalMap
类确定了基类本身的映射,并为所有派生类描述了通用映射;通用映射包含标识键映射和通用属性的描述,以及数据库中表名称和表所属模式的定义。所有派生类 TPTMammalMap
、TPTReptileMap
、TPTCrocodileMap
、TPTSnakeMap
、TPTDogMap
和 TPTHorseMap
的映射确定了这些类特有的属性的映射、与该类型对应的数据库表名称、表所属的模式名称,而 KeyColumn
函数则指定了数据库中用于创建外键的列名,以及与基类对应的表。
每个具体类表(TPC)
在 TPC 继承中,继承层次结构中的每个类都将有自己的表。继承层次结构掩盖了存在多个独立底层表来表示每个子类型的事实。
在这种情况下,通用属性可以重复出现在每个表中,而不是放在单独的表中。在这种情况下,基类将是抽象的。我们将考虑这种情况。
数据库包含 TPC_Horse 和 TPC_Dog 表,它们具有一些相同的字段。
我们创建 NHibernate 模型,创建类结构和继承关系,并添加可选的 Fluent NHibernate 模板来生成 Fluent 映射。最终,我们得到以下模型:
该模型生成的 Fluent 映射如下:
// mapping of the TPCDog class
public class TPCDogMap : SubclassMap<TPCDog>
{
public TPCDogMap()
{
// the name of the schema that stores
// the table corresponding to the type
Schema("dbo");
// the name of the table corresponding to the type
Table("TPC_Dog");
// indicates that the base class is abstract
Abstract();
// Using the Map function, we define mapping
// of the Breed property, it is possible to specify
// the type of the property, its access,
// the name of the field in the table and its server
// type, facets and other mapping settings
Map(x => x.Breed)
.Column("Breed")
.CustomType("String")
.Access.Property()
.Generated.Never()
.CustomSqlType("nvarchar")
.Length(128);
}
}
// mapping of the TPCHorse class
public class TPCHorseMap : SubclassMap<TPCHorse>
{
public TPCHorseMap()
{
// the name of the schema that stores
// the table corresponding to the type
Schema("dbo");
// the name of the table corresponding to the type
Table("TPC_Horse");
// indicates that the base class is abstract
Abstract();
// Using the Map function, we define mapping
// of the MaximumSpeed property, it is possible
// to specify the type of the property,
// its access, the name of the field in the table
// and its server type, facets and other mapping settings
Map(x => x.MaximumSpeed)
.Column("MaximumSpeed")
.CustomType("Decimal")
.Access.Property()
.Generated.Never()
.CustomSqlType("decimal")
.Precision(4)
.Scale(2);
}
}
// mapping of the TPCBaseEntity base class
public class TPCBaseEntityMap : ClassMap<TPCBaseEntity>
{
public TPCBaseEntityMap()
{
// indicates that this class is the base
// one for the TPC inheritance strategy and that
// the values of its properties should
// be united with the values of derived classes
UseUnionSubclassForInheritanceMapping();
// the Id function is used for identity
// key mapping, it is possible to specify the type
// of the property, its access, the name
// of the field in the table and its server type,
// facets and other mapping settings,
// as well as to specify the class name to be used to
// generate the primary key for a new
// record while saving a new record
Id(x => x.ID)
.Column("ID")
.CustomType("Int32")
.Access.Property()
.CustomSqlType("int")
.Not.Nullable()
.Precision(10)
.GeneratedBy.Assigned();
// Using the Map function, we define mapping
// for common properties, it is possible to specify
// the type of the property, its access,
// the name of the field in the table and its server type,
// facets and other mapping settings
Map(x => x.BirthDate)
.Column("BirthDate")
.CustomType("DateTime")
.Access.Property()
.Generated.Never()
.CustomSqlType("datetime");
Map(x => x.Genus)
.Column("Genus")
.CustomType("String")
.Access.Property()
.Generated.Never()
.CustomSqlType("nvarchar")
.Length(128);
}
}
上面的代码表示生成的模型映射类。派生类 TPCDogMap
和 TPCHorseMap
的映射实现了这些类特有的属性的映射,并定义了与该类型对应的数据库表名称、表所属的模式名称,而 Abstract
函数则表示基类是抽象的。
TPСBaseEntityMap
类确定了基类的映射,并描述了所有派生类的通用映射,其中包含标识键和通用属性的映射描述;UseUnionSubclassForInheritanceMapping
函数表示该类是 TPC 继承策略的基类,并且其属性值应该与派生类的值合并。当通用属性放在数据库中的一个单独的表中时,基类应该通过在映射中指定数据库中的表名称以及表所属的模式名称来映射到该表。
为了演示如何实现和使用继承以及其他多种 NHibernate 映射类型,我们发布了 NHibernate Mapping Samples 应用程序,该应用程序演示了 50 种不同的映射案例,它们是如何通过 Fluent 和 XML 映射实现的,以及它们的用法。请访问我们的 NHibernate Mapping Samples 博客文章 阅读有关此应用程序的更多信息并下载它。