具体实施方式
本发明涉及用于存储数据并用于创建相应的数据库结构的方法和***。本发明的各实施例可包括具有各种计算机硬件的专用或通用计算机,如下进行更详细的描述。
如在此所描述的,本发明各实施例包括基于对数据的计划使用而用属性装饰数据类型的方法,这种方法不需要任何有关将如何创建存储数据的数据库结构的专门知识。然后根据由经装饰数据类型确定的需要来创建适当的数据库存储表格。然后表格可用相应数据来填充,并通过适当的应用程序来查询。可以理解,因为数据库结构的创建基于数据的计划使用,且排除了对数据库知识的任何要求,数据库结构被创建成最优化数据库的性能,而不管编程人员对有关数据库和数据库结构的知识有任何缺乏。还可以理解,这表示对创建数据库结构的现有技术的模式转换和改进。
在本发明范围内的实施例还包括用于执行或具有存储其上的计算机可执行指令或数据结构的计算机可读介质。这种计算机可读介质可以是可由通用或专用计算机访问的任何可用介质。作为示例,而非限制,这种计算机可读介质可包括RAM、ROM、EEPROM、CD-ROM、或任何其它介质,这些介质可用来执行或存储计算机可执行指令或数据结构形式的所需程序代码方法,并可由通用或专用计算机访问。当信息经网络或另一通信连接(硬接线、无线、或硬接线或无线的组合)传送或提供到计算机时,计算机适当地将该连接视为计算机可读介质。因而,任何这种连接被适当地称为计算机宽度介质。以上的组合应当包括在计算机可读介质的范围内。计算机可执行指令包括,例如使通用计算机、专用计算机、或专用处理装置执行某功能或一组功能的指令和数据。
.NET数据类型和实例的持久存储
根据本发明一实施例,对.NET数据类型和实例的持久存储具备相应模块,以使诸如但不限于数据库表格的数据库结构响应置于数据类型上的在编程期间定义的装饰而创建。
置于数据类型上的装饰可根据任何需要或愿望而改变。现在将提供某些实例来示出装饰可如何附在由编程人员编写的代码分段中的数据类型上,且用这种方法使它们在后来可用来控制数据库结构的创建,即使编程人员并不具有任何有关要随后创建的数据库结构的特定知识。
在第一个示例中,专辑(album)被描述为一个类的类型,包括标题、专辑图片、艺术家列表和歌曲列表。每首歌也被标识为具有标题、持续时间和专辑的一个类的类型。最后,艺术家被标识为包括名字、图片、以及歌迷网页的URL的一个类的类型。
class Album{
string title;
byte[]albumArt;
//以下未经装饰的集合字段表示给定艺术家可通过许多专辑引用,
//且给定专辑可引用许多艺术家。
//用数据库术语来说,这就是多对多关系。
Artist[]artists;
//以下字段上的CLSIBackPointer属性表示歌曲集合由专辑实例所有,
//且歌曲(Song)类型中的该专辑字段用作指向所拥有专辑的逆向指针。
//用数据库术语来说,这就是一对多关系。
[CLSIBackPointer(album)]
Song[]songs;
}
class Song{
//以下字段上的CLSIIndex属性表示应为了查询效率对它进行索引。
[CLSIIndex]
string title;
int duration;
//以下字段用作由以上Album(专辑)类型的歌曲集合引用的逆向指针
//用数据库术语来说,这就是外来关键字。
Album album;
}
class Artist{
//以下字段上的CLSIIndex属性表示应为了查询效率对它进行索引。
[CLSIIndex]
string Name;
byte[]picture;
Uri fanPage;
}
如由前面示例进一步反映的,部分对应于类的类型的数据对象用特定属性来装饰。例如,[CLSIBackPointer(album)]属性表示专辑字段或对象包括指向可与同一专辑相关联的许多不同歌曲的逆向指针。这将参照图1在下面进行更详细的描述。
所示的另一装饰包括与艺术家类相关联的[CLSIndex]属性,它表示应当能够进行查询。该装饰的一个原因是要提高数据库的性能。特别地,如果知道数据库将允许查询某些词,则指示要查询哪些词是有用的。这样,当创建数据结构时,它们可用最优化已经知道要查询的词的搜索的方法来创建,如下进行更详细地描述。
现在注意图1A-1D,其中示出包括可根据本发明的各方法并基于向持久存储模块提供的代码中定义的数据类型和装饰来创建的数据库表格的某些数据库结构。
如图所示创建了4个表格。第一个表格Album Table(专辑表格)100a对应于在以上示例中标识的专辑类型,并包括标题字段110a和图片字段120a,以及记录号列130a和艺术家列140a,引用如下。这些不同字段对应于在上述代码中提供的各个对象。
独立的Song Table(歌曲表格)100b分别包括标题、持续时间和专辑的相应字段(110b,120b,130b),如代码所定义。持续时间字段120b可包括与它所属的歌曲相对应的任何持续时间数据。
艺术家表格100c包括与在以上提供的示例中与艺术家类的类型的描述相对应的名字字段110c、图片字段120c、以及URI字段130c。图片字段120c可包括指向对应于例如艺术家的图形图像的指针,且URI字段130c可包括与对应于艺术家的文档或网页的链接,诸如歌迷网页。
最后,提供了集合表格100d,该表格包括都定型为保存唯一身份的两个列。第一列110d表示该集合的身份,并由引用它的实例字段指向,在此情形中为来自专辑表格100a的专辑。第二列120d表示是该集合一部分的实例的身份,在此情形中是艺术家。因而,单个集合将在集合表格中具有与它所包含的实例数量相同的行数,但所有这些行都具有标识给定集合的同一个唯一身份。因此,指向集合的指针字段可通过放置表示该集合实例的行的唯一身份来表示。这暗示,由专辑对象指向的每个独特的艺术家集合将在集合表格中被表示为一个或多个行,同时每个行的第二列指向艺术家表格中的一个行。例如,在本示例中,集合ID 1(与专辑1相关联)与艺术家元素21和艺术家元素23相链接。集合ID还在专辑表格100a中标识,用于交叉引用。
当艺术家类型的名字字段使用上述的适当装饰表征为唯一标识符时,艺术家名字可表示可指向艺术家表格中各行的集合表格中的唯一身份。然而,如果艺术家类型的名字字段未被表征为唯一标识符,则指针将相反指回相应艺术家表格的标识符,而不指向艺术家名字。在本示例中,这使用记录号来完成。
可以理解,由于歌曲类的标题字段和艺术家类类型的名字字段用[CLSIIndex]属性来装饰,对应于歌曲和艺术家类的数据库结构(例如,表格)用可分别根据标题和名字列来排序并快速查询的方法来建立,且不需要数据库查询存储在相应数据库表格中的所有行。
例如,与给定类相关联的数据库表格可包括隐藏的记录号130a、140b、140c,这些记录号对表格中的每个行提供唯一标识符。
在搜索词或对象期间,数据库可基于记录号130a顺序搜索所有记录。然而,如果类字段先前已用诸如[CLSIIndex]属性的属性来标识并装饰,其中该属性指示它想要被查询,则可在对应于属性字段的列上创建相应的索引数据结构,所述列诸如歌曲表格的标题列和艺术家表格的名字列,从而它们可进行排序(按字母顺序,或以任何其它方式)以优化搜索。因此,查询可被优化,且编程人员不必通过有关数据库的任何专门知识来建立数据结构。
类似地,与专辑类中的歌曲字段相关联的逆向指针装饰表示,歌曲集合由专辑实例所拥有,且歌曲类型中的专辑字段用作指向所拥有专辑的逆向指针。因此,属于给定专辑的歌曲可通过查询带有标识指定专辑的专辑列的歌曲表格来标识。相反,专辑列可用来标识给定歌曲所属的专辑。此后,所标识的专辑可使用其它表格(例如100c)用来标识与专辑的艺术家相对应的其它信息。
这样,歌曲表格100b可独立于专辑表格100a被查询,使得专辑表格100a不必存储有关在歌曲表格100b中列出的每首歌曲的持续时间信息。可以理解,这可改善查询歌曲表格100a时的性能。类似地,查询歌曲表格100b还通过不必存储与所属专辑相关联的所有图片和艺术家名字信息来最优化。这是因为对各表格的交叉引用可简便地提供所有必要的信息。
现在将参照购买订单的客户来提供另一个示例,以示出另一类装饰。然而,可以理解,本示例以及前面的示例仅仅是说明性的,且因此不应解释为将本发明限制为与可创建的数据库结构类型或可使用的装饰属性的类型相关。
在以下示例中,创建客户和购买订单类。
class Customer{
//以下字段上的CLSIIndex属性表示为了查询效率应对它进行索引。
[CLSIIndex]
string name;
//以下字段上的CLSIIdentity属性表示它用作该类型的身份字段。
//用数据库术语来说,本字段是该类型的唯一关键字。
[CLSIIdentity]
string phoneNumber;
//以下字段上的CLSISingleRef属性表示只有它才能引用地址实例。
//用数据库术语来说,它暗示客户和地址实例之间的一一对应关系。
[CLSISingleRef]
Address address;
//以下字段上的CLSIBackPointer属性表示订购集合由客户实例所有,
//且PurchaseOrder类型的客户字段用作指向所有拥有客户实例的逆向指针。
//用数据库术语来说,这是一对多关系。
[CLSIBackPointer(customer)]
PurchaseOrder[]orders;
}
class PurchaseoOrder{
//以下字段用作由以上客户类型的订单集合字段引用的逆向指针。
//用数据库术语来说,这是受保护的外部关键字。
Customer customer;
string itemDescription;
int skuID;
}
abstract class Address {
string street;
string city;
}
class USAddress:Address {
string state;
int zip;
}
在前面示例中,呈现了某些新的装饰。例如,提供了装饰客户类的电话号码字段的[CLSIIdentity]属性,以指示该电话号码应表示客户类型的唯一关键字。
还向客户类的地址字段提供[CLAISingleRef]属性,以表示一对一关系在客户和客户地址之间存在。
另一包括[CLSIBackPointer(customer)]的属性对客户类的购买订单字段进行装饰。该属性表示购买订单计划用作指向下订单的客户的逆向指针。这根据图2中提供的各个示例得到更清晰的理解。
如图2A和2B所示,客户表格(Customer Table)200a和购买订单表格(PurchaseOrder Table)200b可分别与其相应字段(210a、220a、230a)和(220a、220b、230b)一起创建,这些字段在以上说明性代码片断中定义。这些表格200a和200b还像表格100a、100b和100c一样根据在代码片断中提供的定义和装饰来创建,且无需有关如何建立数据库结构的任何专门知识。相反,定义和装饰仅需提供存储数据将用于什么或将需要如何访问或查询的暗示。
在前面示例中,提供了[CSIIdentity]属性的装饰,以表示相应的字段、电话号码应表示客户类型的唯一关键字。这可用来例如最小化存储表格中每一行的特定记录号130a(图1)的要求。特别地,用[CLSIIdentity]属性装饰的字段可用作每一相应行的唯一标识符。这在图2中示出,其中电话号码220a被用来表示购买订单表格200b中的客户。
在其它实施例中,字段的组合可用[CSIIdentity]属性来装饰,从而该字段的组合可表示行的唯一标识符。
如上标识的[CSISingleRef]属性向客户类的地址字段提供,以表示需要在客户和客户地址之间存在一对一的关系。这样,客户地址字段230a可用对应于客户的实际地址串来填充,或者可指向存储在另一表格中的地址数据。
通常如上所述,逆向指针属性[CLSIBackPointer(customer)]在上个示例中对客户类的购买订单字段装饰。因此,如参照图1所述,逆向指针使得在购买订单表格中列出的客户能指回客户表格200a。因此,单个表格不必保持对应于客户以及对应于客户的购买订单的每个数据项。相反,所购买项及相应SKU代码的详细描述可与客户表格200a分开保持,并在需要时通过查寻对应于购买订单的客户来访问。可以理解,这可根据数据库的计划需要来改进数据库的性能,如上所述。更重要的是,编程人员不必知道或理解数据库表格将如何创建。相反,编程人员仅需要知道数据的计划使用是什么。
前面的示例还示出各个类的类型如何安排到不同分层结构中。特别地,前面的示例标识地址基类和对应于地址基类的US地址类。这可用来例如创建可存储对应于不同分层结构分类的相关数据的独立表格或其它数据库结构。
尽管当前未示出地址类的表格,可以想像如何创建地址的一般基类表格,该表格将包括用于存储一般地址信息的不同列,该一般地址信息对许多不同类的地址都相同,诸如州、街道名、地址号码等。然后可在专门表格或其它结构中存储子类,这些表格或其它结构包括可能不是与所有地址都相关的更多具体地址数据。例如,在全世界的目录应用中,并不是所有的国家都包括邮编。类似地,美国的地址也可不包括地址代码以及与外国地址相关联的其它信息。在此情形中,每个不同区域的地址表格可包括不相关于所有地址对象的专用信息,且同时仍然使能对来自基类表格的一般地址信息的快速访问。
如在前面有关交叉引用的示例中,可以理解,基类表格(例如基本地址类表格)可用子类表格(例如区域类表格)来交叉引用。该技术可被用来创建在类和子类表格之间各种深度的分级结构层次。
现在注意图3中示出的流程图300,它示出用于实现本发明的一种方法。如图所示,该方法包括标识要存储的数据(310)。这样,术语数据指要存储的数据类型。当标识数据类型的代码被提交给保持在计算机可读介质上的持久存储模块(未示出)时,可进行该标识。
然后,确定有关应使用什么数据基本模式(例如数据库结构)来存储数据(320)。该确定响应于标识与数据类型相关联的装饰而自动作出,如上所述。特别地,编程人员不必具有有关应创建什么数据结构或应如何使用数据库存储工具来选择或创建它们的任何专门理解。相反,本发明基于与在代码中定义的数据类型相关联的装饰来提供数据结构的自动创建,其中装饰反映对数据的计划使用或访问。
然后,确定适当的数据库结构是否存在(330)。如果不存在,则创建它们(340)。例如,图1和2示出可创建的表格的某些实施例。然而,这些表格不应被解释为限制本发明的范围。数据库结构的创建(340)可包括创建一数据库表格,并用通过代码中相应字段标识的至少某些列来填充表格,如参照图1和2如上所述并显示。
创建数据库结构(340)还可包括创建对应于该数据类型的多个表格。可创建的表格例如可包括定义在代码中标识的类型并标识每个相应数据库表格的类型表格。该表格和其它类似表格在下面进行更详细的描述。任何数量的基类和子类表格也可在确定不存在存储数据类型的适当数据库结构之后创建。
在创建适当的数据库结构(例如表格)之后,图3的所示方法包括获取要存储的数据对象(350)。这可在例如单个实例期间或在一段时间上实现,并从任何数量的相连或独立的数据库和应用实现。一旦获得了数据对象(350),就确定是否已存储了数据对象(360)。这可通过例如检查适当的表格来实现,特别是当相应字段之一已用[CLSIIdentity]属性装饰时,其表示没有两个条目将相同,除非条目具有表征字段的相同值。因此,如果另一条目在相应经装饰字段中具有相同值,则可确定已经接收该条目,且新数据是已接收数据的副本或更新。通过减少数据对象的重复输入,存储能力和查询处理要求可最小化。
在一实施例中,身份属性应用在字段上,以表示该字段是否是主要关键字的一部分。根据本实施例,身份属性应用于形成持久类的分层结构的根的至少一个字段,从而该至少一个字段用来如上所述地标识可使用的主要关键字,以防止重复存储。
所示方法的下一元素包括如果未写数据对象则写数据对象(370)。这可包括如上所述或以任何其它方式将数据对象的任一部分写入一个或多个数据库表格。这还可包括将数据对象的派生物写入数据库表格。这还可包括将指向存储在另一位置的实际数据的指针写入数据库表格。
在其它实施例中,本发明的各方法还可包括更改已发现存储在数据库结构中的数据(380)。例如,如果自存储的最后时间起已对数据作了更新,则可用来更新数据。为了避免已更新数据与旧数据分开并独立存储,并因此使用可贵的存储空间,可使用主要关键字来确定数据相同且应进行更改或重写。否则,记录在后来检索回对象时会产生过时信息。防止重复存储的唯一关键字的使用还用来避免响应于对数据的请求产生对同一实体的不同信息集。
尽管前面的示例已参照包括数据库表格的数据库结构来提供,可以理解,本发明的范围可扩展到其它实施例,诸如其中数据库结构包括XML文件或其它存储结构的实施例。
本发明的范围还扩展到数据存储是数组或字典的实施例。例如,数组可类似地被配置成所述集合,且在此通过提供对应于数组中维的数量和秩的不同列的两个附加数据元素或集。
总之,可存储并从一个或多个具有用来实现上述方法的计算机可执行指令的计算机可读介质中实现的本发明,有时称为.NET数据类型和实例的持久存储。如所述,持久存储提供将数据结构串行化到数据库中的一般机制,从而提供对众多服务的方案,这些服务需要一般方法来存储任意的数据结构并按需检索它们。
尽管持久存储基于CLR(公共语言运行时间)所提供的功能,在存储器下***不同数据源应当是可能的。此外,本发明使用的基于.NET数据类型的数据模型不应视为限制本发明的范围,而是也可支持可选数据模型,诸如那些基于Java的。
将在以下描述中提供某些感兴趣的变化和其它细节。
开始时,持久存储框架可被描述为包括以下元素:提供串行化或解串行化数据结构为数据源的基本功能的ObjectStore。该ObjectStore可接受要在其中操作的环境。ObjectStore的导出形式提供特定实现,其中与ObjectStore相关联的数据源是SQL数据库或XML文件。还定义了与记录id、存储上下文等的特定Store实现相关的类。还应用了串行化属性。如在此所述,定义了几个定制属性,从而服务开发者可在各字段上指定某些附加信息。StoreService是向客户机提供单个接触点以将对象保存在底层数据库中ServiceBus服务。
该框架确保对图表中或集合中同一对象的多个引用仅存储一次。在多个图表上对同一对象的引用还应解析为存储中的同一记录。为了确保这一点,类需要指定组成其唯一id的字段。
当解串行化发生,且对象图表或集合从存储记录建立时,每个对象只应创建一次。但对象引用不需在多个解串行化调用上解析为同一对象,除非它们被指定为属于同一环境。
现在注意ObjectStore,它是处理存储和检索请求的组件。现在将提供与ObjectStore相对应的示例性方法和代码。
Constructor:ObjectStore是存储机制的起点。可任选的StoreContext参数被用来设置检索和存储的阶段。如果不提供环境,则用缺省值来初始化StoreContext。
Write:该方法允许调用者存储对象o。对象o可以是元类型、结构、类或集合。如果存储了对象,则该Write方法返回唯一的RecordID。否则它返回空或产生一StoreException例外。
Read:该方法建立给予RecordID的对象以及该对象的类型。过载版本基于指定标准来返回对象的集合。
Delete:该方法删除给予RecordID的对象以及该对象的类型。过载版本基于指定标准删除对象。参数表示是否应递归地跟踪对其它对象的引用。如果作为删除外部对象的结果,内部对象的引用ref计数变成0,则删除操作的层叠会发生。
类型描述:
abstract class ObjectStore
{
ObjectStore();
ObjectStore(StoreContext context);
RecordID Write(object o);
object Read(Type t,RecordID id);
object[] Read(Type t,Query criteria);
bool Delete(Type t,RecordID id,bool recurse);
bool Delete(Type t,Query criteria,bool recurse);
}
abstract class RecordID
{
}
abstract class StoreContext
{
}
class StoreException:Exception
{
}
现在注意定制属性的某些示例,包括串行化和引用动作属性。现在将提供对应于某些属性的示例性代码。
串行化属性
SerializableAttribute(可串行化属性)和NonSerializableAttribute(不可串行化属性)表示是否类型的实例可进行串行化。Storeability(可存储性)和serializability(可串行性)被视为相关概念。可存储性被视为串行化到文件或数据存储。
CLSIIdentity属性
如上所述,CLSIIdentity属性是一定制属性,它应用在字段中以表示该字段是否是主要关键字的一部分。CLSIIdentity属性必需应用于形成持久类的分层结构的根的类中的至少一个字段。
[AttributeUsage(AttributeTargets.Field)]
public sealed class CLSIIdentityAttribute:Attribute
{
}
ReferentialAction属性
ReferentialActionAttribute(参考动作属性)是一定制属性,它应用于参考字段上以表示如果参考对象被删除应采取的动作。
[AttributeUsage(AttributeTargets.Field)]
public sealed class ReferentialActionAttribute:Attribute
{
ReferentialActionAttribute(ReferentialAction a);
}
enum ReferentialAction
{
NoAction,//在删除该对象之前不能删除引用对象
Cascade,//引用对象将被删除且该对象也被删除
SetNull//引用对象被删除且引用被设置为空
}
其它属性参照图1和2以及相应示例来描述。
现在注意SqlStore,它是处理对SQL数据库的存储和检索请求的组件。SqlStore建立数据库连接、将读写调用转化为DataAdaptor等所理解的。现在将提供对应于SqlStore的示例性方法和代码。
Constructor:SqlStore是SQL存储机制的起点。可任选的StoreContext参数被用来设置检索
和存储的阶段。如果不提供环境,则用缺省值来初始化StoreContext。
a)_database名字:设置为“ServiceBus”
b)_varMaxLength:设置为1024
Write:该方法允许调用者存储对象o。对象o可以是元类型、结构、类或集合。
如果存储了对象或者对象已经出现,则该Write方法返回唯一的SqlRecordID。否则它返回空或产生一StoreException例外。
Read:该方法建立给予RecordID的对象。
VarMaxLength:本属性给出要使用的最大长度,同时定义varchar和varbinary的SQL类型。
缺省值为1024。
类型描述:
class SqlStore:ObjectStore
{
SqlStore();
SqlStore(SqlStoreContext context);
SqlDataAdapter DataAdapter {get;}
SqlConnection DbConnection {get;}
uint VarMaxLength {get;set;}
}
class SqlRecordID:RecordID
{
int64_ID;
}
class SqlStoreContext:StoreContext
{
string_databaseName;
IDataAdapter_dataAdapter;
uint_varMaxLength;
}
class StoreException:Exception
{
}
尽管本发明可在许多不同环境中实践并可用不同的关系数据库来实践,以下示例示出将对象存储到SQL数据库中所涉及的部分动作。特别地,存储对象在SQL数据库中涉及以下动作:
a)创建表格:如果必要创建与对象类型等效的SQL表格。如果不必要,则标识要存入对象的SQL表格。
b)一旦对应于对象的SQL表格可用,则构成主要关键字的字段被用来检查对象是否已经存储在表格中。如果该行已经出现,则用当前对象更新,否则创建一新记录。
c)存储可串行化的对象实例的字段。基于对象成员的类型及成员的递归式成员,如果必要创建更多的SQL表格。
现在呈现可创建的不同表格的示例。
ObjectTypeTable
ObjectTypeTable是一SQL表格,它将CLR类/结构的XmlTypeName映射成数据源表格的名字。对于每个在存储过程期间创建的SQL表格,在ObiectTypeTable中创建一个条目。
OBJECTTYPETABLE
(
objectTypeID SqlRecordID,
objectTypeName varchar(n),
sqlTableName varchar(128),
rootTypeID SqlRecordID,
baseTypeID SqlRecordID,
lastObjectID SqlRecordID
)
objectTypeID:给予遇到的每个类型的唯一id。
objectTypeName:可用于查询的完全受限的类型名字。可使用XmlTypeName而非Assembly
QualifiedName,从而各版本上无字段变化的类型可解析为同一数据类型名。
sqlTableName:给出对应于CLR类型的表格的名字。
rootTypeID:给出持久类的分层结构的根。该根类必须具有由用户指定的主要关键字。唯一id对该根表格中的每一新行生成,并在沿类的分层结构链的所有导出类表格中再次使用。
baseTypeID:给出基类id。对于持久类的分层结构的根,将为空。
lastObjectID:给出最后加入SQL表格的对应于该CLR类型的最后一行的SqlRecordID。
ObjectBaseTypeTable
ObjectBaseTypeTable是一个SQL表格,它维护各类的基本类关系。对于在存储过程期间创建的每个SQL表格,一个或多个条目在ObjectBaseTypeTable(对象基本类表格)中创建。如果类型A是B的基类,则该条目包含以A为源的一个行。
该表格在查询期间有用。
enum Relationship
{
Root=1,//源是dest的根类
Ancestor=2,//源是dest的非紧邻基类
Parent=3,//源是dest的紧邻基类
}
EXEC sp_addtype Relationship,tinyint,′NOT NULL′
OBJECTBASETYPETABLE
(
srcObjectTypeID SqlRecordID,
destObjectTypeID SqlRecordID,
relationship Relationship,
)
srcObjectTypeID:是该关系的源的类型。
destObjectTypeID:是该关系的目标的类型。
relationship:源和目标之间的关系。
ObjectArrayTable
ObjectArrayTable是一SQL表格,它包含组成任何数组的元素。所有数组实例的元素都存储在此。
OBJECTARRAYTABLE
(
objCollectionID SqlRecordID,
elemIndex int,
elemObjID SqlRecordID,
elemTypeID SqlRecordID
)
objCollectionID:给予遇到的每个数组实例的唯一id。
elemIndex:数组内元素的索引。
elemObjID:元素对象的记录id。
elemTypeID:给出元素对象的类型。这将有助于定位元素存储在其中的表格。
ObjectDictionaryTable
ObjectDictionaryTable是一SQL表格,它包含组成任何字典的元素。所有字典实例的元素都存储在此。注意,实际关键值和元素对象存储在对应于其CLR类型的SQL表格中。
OBJECTDICTIONARYTABLE
(
objCollectionID SqlRecordID,
elemKeyID SqlRecordID,
elemObjID SqlRecordID,
elemTypeID SqlRecordID
)
objCollectionID:给予遇到的每个字典实例的唯一id。
elemKeyID:关键对象的记录id。
elemObjID:元素对象的记录id。
elemTypeID:给出元素对象的类型。这将有助于定位元素存储在其中的表格。
现在注意CLR类型的映射。特别地,每个类的类型被存储为单独的SQL表格。类的实例中的每个字段的值被存储为SQL表格中的列值。
对象表格等同于类或结构类型,并在遇到该类的第一对象时快速创建。只要可用,就使用对象的实例类型,否则使用该对象的声明类型。例如,如果该对象为空,则使用声明类型。
类的子类型被转化成包含导出类的字段的SQL表格,该表格具有额外的会引用对应于根类实例值的SQL记录的SqlRecordId列。持久类的链在ObiectTypeTable中存储。
根表格包含给出实际实例类型的额外的列。该根对象表格也将包含引用(ref)计数的两个列。这些在删除期间有用,并对处理周期性图表也有用。一个引用计数跟踪对象是否被传递进来,作为对象图表的根(该计数只具有两个值0或1),另一引用计数保持引用该对象的对象总量。仅当两个引用计数都变成零时,才删除一行。
表格的主要关键字类型从对象类型的定制属性来确定。指定主要关键字的该定制属性可对该类型的一个或多个字段设置。该根持久类必须包含至少一个具有该属性的字段。SqlStore还把SqlRecordId列加入表格定义中,该表格定义将包含自动生成的唯一id。该id是在SQL表格内唯一的一个64-比特号码,并将定义为可选关键字。SqlRecordId将一直被用作引用该对象的内部对象中的外部关键字,甚至在主要关键字已经明确指定的情形中。
组成主要关键字的所有字段必须是根持久类的可串行化成员。否则,为了检查是否已存储了一对象,应当首先获取主要关键字内的引用字段的SqlRecordId。注意这是一个递归过程,因为引用字段本身可在其主要关键字内包含引用字段。以下段落提供这样的一个示例。
例如class Media
{
[uniqueKey]
MediaId id;
[uniqueKey]
string location;
string artist;
}
class MediaId
{
string libId;
int objId;
}
Media a;
在此为了发现对象“a”是否已得到持续,我们需要在MediaId表格中查询对应于a.id的SqlRecordId。一旦我们有了这个,我们就可以在Media表格中查询由SqlRecordId和位置值给出的主要关键字。
不具字段的类被转化成一SQL表格,该表格具有是SqlRecordId的单个列。
现在将提供各个示例。
<ROOT TABLENAME>
(
objID SqlRecordID UNIQUE,
objectTypeID SqlRecordID REFERENCES OBJECTTYPETABLE(objectTypeID),
refCount int,
rootRefCount bit,
<field><type>,
<field><type>,
<field>SqlRecordID REFERENCES<REF ROOT TABLENAME>(objID),
...
CONSTRAINT PK_<ROOT TABLENAME>PRIMARY KEY (<field>,<field>...)
)
<DERIVED TABLENAME>
(
objID SqlRecordID REFERENCES<ROOT TABLENAME>(objID)
<field><type>,
<field><type>,
<field>SqlRecordID REFERENCES<REF ROOT TABLENAME>(objID),
...
)
例如
class A
{
...
}
[Serializable]
class B:A
{
[UniqueKey]
int fb1;
[UniqueKey]
int fb2;
int fb3;
}
[Serializable]
class C:B
{
int fc1;
}
创建如下的SQL表格:
BTABLE
(
objID SqlRecordId UNIQUE,
objectTypeID SqlRecordID REFERENCES OBJECTTYPETABLE(objectTypeID),
refCount int,
rootRefCOunt bit,
fb1 int,
fb2 int,
fb3 int,
CONSTRAINT PK_BTABLE PRIMARY KEY(fb1,fb2)
)
CTABLE
(
objID SqlRecordId REFERENCES BTABLE(objID),
fc1 int
)
给出
B b=new B;
C c=new C;
假设b的objID为1,而c的objID为2。则具有id 1的行将出现在BTABLE中,同时具有id 2的行将出现在BTABLE和CTABLE中。
被定型为一类类型的类的成员将转化成为所包含的类创建的SQL表格中的SqlRecordId。外部关键字约束被强加在该列上。即使内部类类型的子类型的实例也在所包含的类的实例中出现,这也是可能的,因为所有id在根类层上都是唯一的。所引用的该行的refCount和rootRefCount列被适当维护。
如果类的成员是一类类型且不是主要关键字的一部分,则类成员可为空。是元类型和结构的成员是不可为空的。在SQL表格定义中,可为空的字段被指定为可为空的。
当类实例A和B在对象图表中包含对彼此的引用时,A和B仅存储一次。A和B将包含彼此的SqlRecordId。示例如下:
[Serializable]
class A
{
[UniqueKey]
int fa1;
int fa2;
X fa3;
}
[Serializable]
class X
{
[UniqueKey]
int fx1;
}
创建如下的SQL表格:
ATABLE
(
objID SqlRecordId UNIQUE,
objectTypeID SqlRecordID REFERENCES OBJECTTYPETABLE(objectTypeID),
fa1 int NOT NULL,
fa2 int NOT NULL,
fa3 SqlRecordId REFERENCES XTABLE(objID)NULL,
CONSTRAINT PK_BTABLE PRIMARY KEY(fal)
)
XTABLE
(
objID SqlRecordId UNIQUE,
fx1 int
CONSTRAINT PK_BTABLE PRIMARY KEY(fx1)
)
在一实施例中,结构类型被转化为具有字段的单独表格。尽管CLR结构是一值类型,创建独立的表格以最优化对加框结构的多次引用情形。结构被准确地视为不具有继承链的类。
结构实例中每个字段的值被存储为表格中的列值。具有结构类型的类或结构成员将转化成SQL表格中为该类创建的SqlRecordId列。
在第二实施例中,被定型为结构类型的类成员的成员被存储在为所包含类创建的同一表格中,该类具有与结构类型的成员相对应的列名,该列名以定型为所包含类的结构类型的类成员名作为前缀。
在第三实施例中,被定型为结构类型的类成员的成员被存储为为所包含类创建的表格中的用户定义类型(UDT)。
具有元类型的对象被转化为元类型表格。元类型表格包含两列-SqlRecordId列和元类型的列。根据一实施例,不支持选择性检索以及元类型对象的删除。
具有元类型的类或结构成员被转化为如下SQL表格中的列。该列中的值是元类型的值。
CLR Type SQL Type
System.Data.SqlTypes.SqlBinary varbinary(1024)
如果指定,则使用VarMaxLength而不是1024。
System.Data.SqlTypes.SqlDateTime Datetime
System.Data.SqlTypes.SqlDecimal Decimal
System.Data.SqlTypes.SqlDouble Float
System.Data.SqlTypes.SqlGuid Uniqueidentifier
System.Data.SqlTypes.SqlInt16 Smallint
System.Data.SqlTypes.SqlInt32 Int
System.Data.SqlTypes.SqlInt64 Bigint
System.Data.SqlTypes.SqlMoney Money
System.Data.SqlTypes.SqlSingle Real
System.Data.SqlTypes.SqlString varbinary(1024)
如果指定,则使用VarMaxLength而不是1024。
System.Char Char
System.Double Float
System.Guid Uniqueidentifier
System.Int16 Smallint
System.Int32 Int
System.Int64 BigInt
System.Single Real
System,String varbinary(1024)
如果指定,则使用VarMaxLength而不是1024。
System.UInt16 Int
System.UInt32 BigInt
System.UInt64 binary(8)
发给数组唯一的SqlRecordId,并将其存储在ObjectArrayTable中作为与其元素相对应的多个行。该表格中的每个行将包含数组实例和元素索引的SqlRecordId。实际元素被存储在与其实例类型相对应的表格中。如果数组是一个类的成员,则该数组的SqlRecordId被存储在所包含的类中。
数组列表、队列被视为与数组相同,并存储在ObjectArrayTable中。
散列表格:被视为与数组相似,但存储在ObjectDictionaryTable中。该表格中的每个行都将包含元素实例的SqlRecordId和元素关键字。
对象检索和查询
对象可用两种方法检索:(1)使用其SqlRecordId和类型;以及(2)使用选择标准和类型。该类型可以是持久类分层结构中从根到对象的实际类型的任何类型。如果SqlRecordId指定且该类型不是一集合,则可执行以下步骤来检索它:
a)查寻ObjectTypeTable并获取该类型的根表格名字。
b)获取该对象的实际类型。
c)从ObjectTypeTable中获取该实例的表格名字。
d)从SQL表格获取该行。
e)从表格名字中获取CLR类型的全限定名字。
f)使用缺省构造器创建所获得CLR类型的对象(期望可持续对象具有缺省构造器)。
g)如果CLR类型是元类型,则直接分配值。
h)如果CLR类型是类或结构,则向对象的字段分配行的字段。对于元字段直接分配;对于字段迭代地跟随记录id并检索所引用对象。注意所引用对象本身可以是类、结构或集合。
i)跟踪对象仅创建一次,同时解串行化对象图表。
如果SqlRecordId指定且类型是一集合,则可实现以下步骤来检索它:
a)获取相关集合表格中所指定id为集合id的行数。在集合有行处中创建许多元素。
b)对每个元素迭代地遵从对象检索规则。
假设一查询来检索对象可更加复杂,并可包括:
a)遵从查询转化规则并将Query转化成SQL。
b)执行查询并在类型的SQL表格中获取匹配行。
c)对每个行遵从对象检索规则。
对象可用两种方法删除:(1)使用其SqlRecordId和类型;以及(2)使用选择标准和类型。该类型可以是持久类分层结构中从根到对象的实际类型的任何类型。如果SqlRecordId指定且该类型不是一集合,则可执行以下步骤来删除它:
a)从对象类型中获取根表格id。
b)获取根表格中的行。减小根引用计数。
c)获取实际对象类型。跟随分层结构链并遍历对应于对象的每一行的字段,并获取非元字段的记录id。
d)对于每个非元字段,获取所引用的表格id。减小所引用行的可及参考计数。迭代地,遍历内部的非元字段,并应用相同规则。如果参考计数都降为零,则删除所引用的行。
e)如果根引用计数和可及引用计数都为零,则删除原始行。
仅当recurse参数被设为真时,才应执行步骤c、d。如果SqlRecordId指定且该类型是一集合,则可执行以下步骤来删除它:
a)获取相关集合表格中所指定id为集合id的各行。
b)对每个元素迭代地遵从对象删除规则。
假设一查询来删除对象可更加复杂,并可包括:
a)遵从查询转化规则并将Query转化成SQL。
b)执行查询并在类型的SQL表格中获取匹配行。
c)对每个行遵从对象删除规则。
基于前述内容,可以理解可对存储在如上所述的数据库结构和表格中的数据执行各种类型的查询。将提供可对参照图1和2所述的示例执行的查询的某些非限制示例。例如,为查询居住在邮编98074的客户的购买订单,XPATH查询看起来如下:/Customers[typeof(address)==USAddress && address.zip==98074]/orders。类似地,为了检索至少有一个购买订单未偿的客户的地址,XPATH查询看起来如下:/Customers[Count(orders)>0]/address。为了检索具有至少一首歌曲的“Sting”的专辑,XPATH查询看起来如下:/Albums[Exists(artists,name==“Sting”)&&count(songs)>0]。为了检索标题为Groovy的专辑的歌曲,XPATH查询看起来如下:/Albums[title==“Groovy”]/songs。最后,为了检索包含题为“Whenever”的歌曲的专辑,XPATH查询看起来如下:/Songs[title==“Whenever”]/album。
尽管已在此提供了前面实现的细节和示例,可以理解本发明的范围广泛地扩展到其中表格或其它数据结构响应于属性和其它装饰而创建的任何方法、***或计算机程序产品。这种用于创建存储数据的数据库机制的方法不依赖于编程人员对数据库的经验,相反仅依赖于数据的计划使用。这表现了在诸如Access和其它数据库工具的现有技术***上的进步,这些数据库工具需要编程人员理解他们需要数据库如何构建。
本发明还使数据库机制能够用各种不同数据格式和关系数据库***扩展和互操作。因此,可以理解,本发明可在各种计算环境中实践。以下是本发明可在其中实践的适当计算环境的一个示例。
计算环境
上述方法可使用带有各种配置的任何数量的计算模块和***以及网络计算环境来执行,这些配置包括,个人计算机、手持式装置、多处理器***、基于微处理器或可编程的消费电子产品、网络PC、小型计算机、大型计算机等等。本发明还可在分布式计算环境中实践,其中本地和远程处理装置通过通信网络实施任务并链接(硬接线链接、无线链接、或硬接线或无线链接的组合)。在分布式计算环境中,程序模块可置于本地和远程存储器存储装置中。
参照图4,实现本发明的示例性***具有常规计算机420形式的通用计算装置,包括处理单元421、***存储器422以及把包括***存储器422在内的各种***组件耦合到处理单元421的***总线423。***总线423可以是若干总线结构类型中的任何一种,包括存储器总线或存储器控制器、***总线、以及使用多种总线架构的任一种的本地总线。***存储器包括只读存储器(ROM)424和随机存储器(RAM)425。含有帮助在个人计算机420中元件之间,如启动期间的信息交换的基本例程的基本输入/输出***(BIOS)426存储在ROM 424中。
计算机420还包括读取和写入磁性硬盘439的磁性硬盘驱动器427、读取或写入可移动磁盘429的磁盘驱动器428、和读取或写入诸如CD-ROM、DVD-ROM或其它光学介质等可移动光盘431的光盘驱动器430。磁性硬盘驱动器427、磁盘驱动器428、光盘驱动器430分别通过硬盘驱动器接口432、磁盘驱动器接口433、光盘驱动器接口434连接至***总线423。诸驱动器及其相关联计算机可读介质为计算机420提供计算机可实施指令、数据结构、程序模块和其它数据的非易失性储存。尽管在此所述示例性环境采用了磁性硬盘439、可移动磁盘429和可移动光盘431,但可使用其它类型的用于存储数据的计算机可读介质,包括磁盒、闪存卡、数字通用盘、Bernoulli卡、RAM、ROM等等。
包括操作***435、一个或多个应用程序436、其它程序模块437和程序数据438的一个或多个程序模块的程序代码装置,可以存储在硬盘439、磁盘429、光盘431、ROM 424或RAM 425中。用户可通过诸如键盘440、定位装置442或诸如话筒、操纵杆、游戏垫、卫星天线、扫描仪等等的其它输入装置(未示出)向计算机420输入命令和信息。这些和其它输入装置常常通过与***总线423耦合的串行端口接口446连接到处理单元421。或者,输入装置可通过诸如并行端口、游戏端口、或通用串行总线(USB)连接。监视器447或其它显示装置也通过诸如视频适配器44的接口和***总线423相连。除了监视器,个人计算机还可包括其它***输出装置(未示出),诸如音箱和打印机。
计算机420可以在使用与一台或多台远程计算机,诸如远程计算机449a和449b的逻辑连接的网络化环境中运行。远程计算机449a和449b可以是另一个人计算机、服务器、路由器、网络PC、对等装置或其它公共网络节点,而且通常包括上述与个人计算机420相关的许多或全部组件,尽管在图4中仅示出了存储器存储装置450a和450b及其相关联应用程序436a和436b。图4中所描绘的逻辑连接包括局域网(LAN)451和广域网(WAN)452,在此作为示例呈现而非限制。这样的网络化环境在办公室、企业范围计算机网络、内联网和因特网上是常见的。
当用于LAN网络化环境中时,计算机420通过网络接口或适配器453与局域网451连接。当用于WAN网络化环境中时,计算机420通常包括调制解调器454、无线链接、或其它用于在广域网452,诸如因特网中建立通信的装置。可以是内置式或外置式的调制解调器454与***总线423通过串行端口接口446连接。在网络化环境中,与计算机420相关的程序模块或其一部分可存储在远程存储器存储装置中。应当理解,所示网络连接是示例性的,且其它用于在广域网452上建立通信的装置也可以使用。
本发明可体现为其它特定形式,而不背离其精神或本质特征。所述诸实施例在所有方面都应仅仅被视为是说明性的,而不是限制性的。因此,本发明的范围由所附权利要求书而不是前面的说明书来指出。在权利要求书的等效技术方案含义和范围内的所有变化被包含在其范围内。