多维存储的SQL和对象使用(一)
本章介绍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
相关文章