多维存储的SQL和对象使用(一)

2022-04-12 00:00:00 数据 节点 对象 定义 属性

本章介绍InterSystems IRIS®对象和SQL引擎如何利用多维存储(全局变量)来存储持久对象、关系表和索引。

尽管InterSystems IRIS对象和SQL引擎会自动提供和管理数据存储结构,但了解其工作原理的详细信息还是很有用的。

数据的对象视图和关系视图使用的存储结构是相同的。为简单起见,本章仅从对象角度介绍存储。

数据

每个使用%Storage.Persistent
存储类(默认)的持久化类都可以使用多维存储(全局变量)的一个或多个节点在InterSystems IRIS数据库中存储其自身的实例。

每个持久化类都有一个存储定义,用于定义其属性如何存储在全局变量节点中。这个存储定义(称为“默认结构”)由类编译器自动管理。

默认结构

用于存储持久对象的默认结构非常简单:

  • 数据存储在名称以完整类名(包括包名)开头的全局变量中。附加“D”
    以形成全局数据的名称,而附加“I”
    作为全局索引。

  • 每个实例的数据都存储在全局数据的单个节点中,所有非瞬态属性都放在$list
    结构中。

  • 数据全局变量中的每个节点都以对象ID
    值作为下标。默认情况下,对象ID
    值是通过调用存储在全局变量数据根(没有下标)的计数器节点上的$Increment
    函数提供的整数。

例如,假设我们定义了一个简单的持久化类MyApp.Person
,它有两个文本属性:

Class MyApp.Person Extends %Persistent
{
Property Name As %String;
Property Age As %Integer;
}

如果我们创建并保存此类的两个实例,得到的全局变量结果将类似于:

 ^MyApp.PersonD = 2  // counter node
^MyApp.PersonD(1) = $LB("",530,"Abraham")
^MyApp.PersonD(2) = $LB("",680,"Philip")

注意,存储在每个节点中的$List
结构的部分是空的; 这是为类名保留的。如果定义Person
类的子类,则此槽包含子类名。当多个对象存储在同一个区段内时,%OpenId
方法(由%Persistent
类提供)使用此信息多态地打开正确的对象类型。此槽在类存储定义中显示为名为“%%CLASSNAME”
的属性。

IDKEY

IDKEY
机制允许显式定义用作对象ID
的值。为此,只需将IDKEY
索引定义添加到类中,并指定将提供ID
值的一个或多个属性。请注意,一旦保存对象,其对象ID
值就不能更改。这意味着在保存使用IDKEY
机制的对象后,不能再修改该对象ID
所基于的任何特性。

Class MyApp.Person Extends %Persistent
{
Index IDKEY On Name [ Idkey ];

Property Name As %String;
Property Age As %Integer;
}

如果我们创建并保存Person
类的两个实例,得到的全局变量结果现在类似于:

 ^MyApp.PersonD("Abraham") = $LB("",530,"Abraham")
^MyApp.PersonD("Philip") = $LB("",680,"Philip")

请注意,不再定义任何计数器节点。还要注意,通过将对象ID
基于Name
属性,我们已经暗示了Name
的值对于每个对象必须是的。

如果IDKEY
索引基于多个属性,则主数据节点具有多个下标。例如:

Class MyApp.Person Extends %Persistent
{
Index IDKEY On (Name,Age) [ Idkey ];

Property Name As %String;
Property Age As %Integer;
}

在这种情况下,生成的全局变量现在类似于:

 ^MyApp.PersonD("Abraham",530) = $LB("",530,"Abraham")
^MyApp.PersonD("Philip",680) = $LB("",680,"Philip")

重要提示:IDKEY
索引使用的任何属性的值中都不能有连续的一对竖线(||
),除非该属性是对持久类实例的有效引用。这种限制是由InterSystems SQL机制的工作方式强加的。在IDKey
属性中使用||
会导致不可预知的行为。

Subclasses

默认情况下,持久性对象的子类引入的任何字段都存储在附加节点中。子类的名称用作附加的下标值。

例如,假设我们定义了一个具有两个文本属性的简单持久MyApp.Person
类:

Class MyApp.Person Extends %Persistent
{
Property Name As %String;

Property Age As %Integer;
}

现在,我们定义了一个持久子类MyApp.Students
,它引入了两个额外的文本属性:

Class MyApp.Student Extends Person
{
Property Major As %String;

Property GPA As %Double;
}

如果我们创建并保存此MyApp.Student
类的两个实例,得到的全局结果将类似于:

^MyApp.PersonD = 2  // counter node
^MyApp.PersonD(1) = $LB("Student",19,"Jack")
^MyApp.PersonD(1,"Student") = $LB(3.2,"Physics")

^MyApp.PersonD(2) = $LB("Student",20,"Jill")
^MyApp.PersonD(2,"Student") = $LB(3.8,"Chemistry")

Person
类继承的属性存储在主节点中,而由Student
类引入的属性存储在另一个子节点中。这种结构确保了学生数据可以作为人员数据互换使用。例如,列出所有Person
对象名称的SQL查询正确地获取Person
Student
数据。当属性被添加到超类或子类时,这种结构还使类编译器更容易维护数据兼容性。

请注意,主节点的部分包含字符串“Student”
-它标识包含学生数据的节点。

父子关系

在父子关系中,子对象的实例存储为它们所属的父对象的子节点。这种结构确保子实例数据与父数据在物理上是集群的。

/// An Invoice class
Class MyApp.Invoice Extends %Persistent
{
Property CustomerName As %String;

/// an Invoice has CHILDREN that are LineItems
Relationship Items As LineItem [inverse = TheInvoice, cardinality = CHILDREN];
}

LineItem

/// A LineItem class
Class MyApp.LineItem Extends %Persistent
{
Property Product As %String;
Property Quantity As %Integer;

/// a LineItem has a PARENT that is an Invoice
Relationship TheInvoice As Invoice [inverse = Items, cardinality = PARENT];
}

如果我们存储多个Invoice
对象的实例,每个实例都有关联的LineItem
对象,则得到的全局变量结果将类似于:

^MyApp.InvoiceD = 2  // invoice counter node
^MyApp.InvoiceD(1) = $LB("","Wiley Coyote")
^MyApp.InvoiceD(1,"Items",1) = $LB("","Rocket Roller Skates",2)
^MyApp.InvoiceD(1,"Items",2) = $LB("","Acme Magnet",1)

^MyApp.InvoiceD(2) = $LB("","Road Runner")
^MyApp.InvoiceD(2,"Items",1) = $LB("","Birdseed",30)

嵌入对象

存储嵌入对象的方法是先将它们转换为序列化状态(默认情况下是包含对象属性的$List
结构),然后以与任何其他属性相同的方式存储此串行状态。

例如,假设我们定义了一个具有两个文字属性的简单串行(可嵌入)类:

Class MyApp.MyAddress Extends %SerialObject
{
Property City As %String;
Property State As %String;
}

现在,我们修改前面的示例以添加嵌入的Home Address
属性:

Class MyApp.MyClass Extends %Persistent
{
Property Name As %String;
Property Age As %Integer;
Property Home As MyAddress;
}

如果我们创建并保存此类的两个实例,则生成的全局变量相当于:

 ^MyApp.MyClassD = 2  // counter node
^MyApp.MyClassD(1) = $LB(530,"Abraham",$LB("UR","Mesopotamia"))
^MyApp.MyClassD(2) = $LB(680,"Philip",$LB("Bethsaida","Israel"))

通过将全局流的数据拆分成一系列块(每个块小于32K
字节)并将这些块写入一系列顺序节点,全局流被存储在全局流中。文件流存储在外部文件中。

来源 https://www.modb.pro/db/96087

相关文章