所谓一实体一具体表就是每个实体对应一张数据表,并且,每个数据表冗余包含其父类的所有属性字段,并且,子类和父类共享相同的主键值。一实一具体表方案适合需要较高查询性能,继承体系层次不太复杂,并且基类包含较少的属性而子类扩展较多属性,并且能够承受一定的数据库空间浪费的情况。
一实体一具体表方案的优点主要就是查询性能好,读操作只需操作一张表,和实体数据的对应结构清晰,数据库表迁移和维护会比较方便;主要的缺点是数据冗余较大,因为每次插入一条子类数据时,同时要插入一份子类包含的父类字段的数据到所有父类层次表中。
请看示例代码:
这种方案下,每个实体对应一张表,每张表包含冗余的父类的数据。也就是说,如上面的例子,Child类对应的ChildTable包含父类Parent和AnotherParent对应的表的所有字段。同时,当插入或更新一条Child数据时,必须同时保存对应的Parent和AnotherParent类的记录。也就是说,当使用Gateway来插入或更新Child记录时,必须显式如下更新Parent和AnotherParent:[Table("ParentTable")] public interface Parent : IEntity { [PrimaryKey] int ID { get; } string Name { get; set; } } [Table("AnotherParentTable")] public interface AnotherParent : IEntity { [PrimaryKey] int ID { get; } int Age { get; set; } } [Table("ChildTable")] public interface Child : Parent, AnotherParent { [PrimaryKey] new int ID { get; set; } DateTime Birthday { get; set; } }
//obj is a Child instance Gateway.Save<Child>(obj, tran); Gateway.Save<Parent>(obj, tran); Gateway.Save<AnotherParent>(obj, tran);
一实体一扩展表
所谓一实体一扩展表是指继承体系中的每个实体对应一张数据表,但是,每个子类不冗余包含父类的所有属性,而只是包含扩展的属性和共享的主键值。一实体一扩展表方案适合继承体系非常复杂,结构易变,并希望最大程度减少数据冗余的情形。
一实体一扩展表方案的优点是结构灵活,新增子类或插入中间的继承类都很方便,冗余数据最少;但是缺点是,无论读还是写操作都会涉及到子类和所有的父类。读操作时,必须自然链接查询所有的父类对应的数据表,而插入或更新数据时,也需要写所有的父类表。
我们还是以代码来说明:
[Table("ParentTable")] public interface Parent : IEntity { [PrimaryKey] int ID { get; } string Name { get; set; } } [Table("AnotherParentTable")] public interface AnotherParent : IEntity { [PrimaryKey] int ID { get; } int Age { get; set; } } [Table("ChildTable")] public interface Child : IEntity { [PrimaryKey] int ID { get; set; } DateTime Birthday { get; set; } } [Table("select ChildTable.ID, ChildTable.Birthday, ParentTable.Name, AnotherParentTable.Age from ChildTable inner join ParentTable on ChildTable.ID = ParentTable.ID inner join AnotherParentTable on ChildTable.ID = AnotherParentTable.ID", IsView=true)] public interface ChildView : Child, Parent, AnotherParent { [PrimaryKey] new int ID { get; set; } }
首先,请注意以上代码中的Child的定义,此时,Child没有从Parent和AnotherParent继承,因为,实际上,对应于ChildTable的字段的只有现在的Child实体包含的ID和Birthday属性。
接着,注意这个新增的ChildView类,ChildView同时从Child,Parent和AnotherParent继承,因此,包含所有的这些实体的属性。因此ChildView实际上代表了我们逻辑上的Child对象,包含Child及其所有父类的属性。注意,ChildView是一个内联视图,对应了Child,Parent和AnotherParent对应的数据表的自然链接查询。因此,读数据时,只需通过Gateway.Select<ChildView>就能读取数据,但是,请注意,实际的查询是一个关联查询,所以性能肯定没有前面的单表继承体系和一实体一具体表好,但是,理论上,当数据量不是特别大时,查询操作的性能差异并不明显。
写操作时,和一实体一具体表类似,需要同时写Child,Parent和AnotherParent:
//obj is a ChildView instance Gateway.Save<Child>(obj, tran); Gateway.Save<Parent>(obj, tran); Gateway.Save<AnotherParent>(obj, tran);