第十三章 Caché 定义和使用对象值属性

第十三章 Caché 定义和使用对象值属性

文中描述序列类和串行类是一种类。

定义和使用对象值属性

短语对象值属性通常指的是如下定义的属性:

Property PropName as Classname;

其中Classname是除集合或流之外的对象类的名称.(集合属性和流属性是前面几章中讨论的特殊情况。)通常,Classname是注册的对象类,持久性类或序列类(请参见下一节)。

要定义这样的属性,请定义该属性所引用的类,然后添加该属性。

变量:CLASSNAME参数

如果属性基于持久性类,则需要执行额外的步骤。在这种情况下,必须将该属性的CLASSNAME属性参数指定为1。此步骤影响Caché存储此属性的方式,并使Caché能够检索其指向的对象。

例如,假设MyApp.Payment指定了NoExtent,而MyApp.CreditCard是MyApp.Payment的子类。假设MyApp.CurrencyOrder包含MyApp.CreditCard类型的属性。该属性应将CLASSNAME指定为1:

Class MyApp.CurrencyOrder [ NoExtent ] 
{
Property Payment as MyApp.CreditCard (CLASSNAME=1);

//other class members
}

请注意,SQL箭头语法在这种情况下不起作用。 (可以改用合适的JOIN。)

重要说明:请勿为类型为序列化类的属性指定CLASSNAME = 1。不支持此用法。

简介序列化对象

序列化类扩展%SerialObject。这种类的目的是用作另一个对象类的属性。序列化对象中的值被序列化到父对象中。序列化对象也称为嵌入式(或可嵌入)对象。Caché处理序列化对象属性的方式与处理非串行对象属性的方式不同。

两者的区别如下:

  • 在将值分配给其中的属性之前,无需调用%New()创建序列化对象。
  • 如果序列化对象属性包含在持久性类中,则序列化对象的属性存储在持久性类的范围内。

要定义一个串行类,只需定义一个扩展%SerialObject的类,并根据需要添加属性和其他类成员。下面显示了一个示例:

Class Sample.Address Extends %SerialObject
{

/// The street address.
Property Street As %String(MAXLEN = 80);

/// The city name.
Property City As %String(MAXLEN = 80);

/// The 2-letter state abbreviation.
Property State As %String(MAXLEN = 2);

/// The 5-digit U.S. Zone Improvement Plan (ZIP) code.
Property Zip As %String(MAXLEN = 5);

}

可能的对象组合

下表显示了父类和该类中的对象值属性的可能组合:

/ 属性是注册的对象类 属性是持久类对象 属性是序列化类
父类是注册对象类 支持 支持但不常见 支持
父类是持久类 支持但不常见 支持 支持
父类是序列化类 不支持 不支持 支持

对象值属性的术语

在持久类中,有两个术语表示对象值属性:

  • 引用属性(基于其他持久对象的属性)
  • 嵌入式对象属性(基于序列化对象的属性)

指定对象属性的值

要设置对象值属性,请将该属性设置为等于适当类的实例的OREF。

请考虑以下情况:ClassA包含基于ClassB的属性PropB,其中ClassB是对象类:

Class MyApp.ClassA
{

Property PropB as MyApp.ClassB;

//additional class members
}

并且ClassB具有一个非串行类,它具有自己的一组属性Prop1,Prop2和Prop3。

假设MyClassAInstance是ClassA实例的OREF。若要为此实例设置PropB属性的值,请执行以下操作:

  1. 如果ClassB不是串行类,请首先:
  • 获取ClassB实例的OREF。
  • (可选)设置此实例的属性。可以稍后进行设置。
  • 将MyClassAInstance.PropB设置为等于该OREF。
  • 如果ClassB是串行类,则可以跳过此步骤。
  1. (可选)使用级联点语法设置属性的属性(即,设置MyClassAInstance.PropB的属性)。

例如:

 set myclassBInstance=##class(MyApp.ClassB).%New()
 set myClassBInstance.Prop1="abc"
 set myClassBInstance.Prop2="def"
 set myClassAInstance.PropB=myclassBInstance
 set myClassAInstance.PropB.Prop3="ghi"

请注意,此示例在创建实例后立即直接设置ClassB实例的属性,之后通过级联点语法更间接地设置。

以下步骤可以实现相同的目标:

 set myClassAInstance.PropB=##class(MyApp.ClassB).%New()
 set myClassAInstance.PropB.Prop1="abc"
 set myClassAInstance.PropB.Prop2="def"
 set myClassAInstance.PropB.Prop3="ghi"

相反,如果ClassB是串行类,则可以执行以下操作,而无需为ClassB调用%New():

 set myClassAInstance.PropB.Prop1="abc"
 set myClassAInstance.PropB.Prop2="def"
 set myClassAInstance.PropB.Prop3="ghi"

完整示例:

Class PHA.DEC.MOB.MyClassB Extends %SerialObject
{
    Property Street As %String(MAXLEN = 80);
    Property City As %String(MAXLEN = 80);
    Property State As %String(MAXLEN = 2);
    Property Zip As %String(MAXLEN = 5);
    }

Class PHA.OP.MOB.TestThree Extends %RegisteredObject
{
    Property age;
}
/// d ##class(PHA.OP.MOB.Test).TestSpecifyingValue()
ClassMethod TestSpecifyingValue()
{
    //序列类属性可以直接用点语法执行
    s mTwo=##class(PHA.OP.MOB.TestTwo).%New("1")
    s mTwo.mClassB.City="北京"
    s mTwo.mClassB.Street="长安街"
    //注册类必须先%New 赋值 才可以 否则报错<INVALID OREF>
    s mThree=##class(PHA.OP.MOB.TestThree).%New()
    s mTwo.address = mThree
    s mTwo.address.age = 20
    
    w mTwo.mClassB.City,!
    w mTwo.mClassB.Street,!
    w mTwo.address.age,!
}

保存更改

在使用持久类的情况下,保存包含的对象(即,包含object属性的实例)。无需直接保存对象属性,因为在保存包含对象时会自动保存该属性。

以下示例说明了这些原理。考虑以下持久性类:

Class MyApp.Customers Extends %Persistent
{

    Property Name As %String;
    
    Property HomeStreet As %String(MAXLEN = 80);

    Property HomeCity As MyApp.Cities;

}
Class MyApp.Cities Extends %Persistent
{

    Property City As %String(MAXLEN = 80);

    Property State As %String;

    Property ZIP As %String;

}

在这种情况下,我们可以创建MyApp.Customers的实例并按如下所示设置其属性:

/// d ##class(PHA.OP.MOB.Test).TestSaveChange()
ClassMethod TestSaveChange()
{
    set customer=##class(MyApp.Customers).%New()
    set customer.Name="O'Greavy,N."
    set customer.HomeStreet="1234 Main Street"
    set customer.HomeCity=##class(MyApp.Cities).%New()
    set customer.HomeCity.City="Overton"
    set customer.HomeCity.State="Any State"
    set customer.HomeCity.ZIP="啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊"
    set status=customer.%Save()
    if $$$ISERR(status) {
        d $system.Status.DisplayError(status)
    }
}
错误 #7201: 数据类型值'啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊'                                   的长度超过2允许的MAXLEN
  > 错误 #5802: 属性'MyApp.Cities:ZIP'上的数据类型验证失败,值等于"啊啊啊啊啊啊啊                        啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊"

这些步骤将一条新记录添加到MyApp.Customers,并将一条新记录添加到MyApp.Cities。

不用为MyApp.Cities调用%New(),我们可以打开一个现有记录:

/// d ##class(PHA.OP.MOB.Test).TestSaveOther()
ClassMethod TestSaveOther()
{
    set customer=##class(MyApp.Customers).%New()
    set customer.Name="Burton,J.K."
    set customer.HomeStreet="17 Milk Street"
    set customer.HomeCity=##class(MyApp.Cities).%OpenId(3)
    set status=customer.%Save()
    if $$$ISERR(status) {
        do $system.Status.DisplayError(status)
    }
}

在以下变体中,在添加新客户的过程中打开并修改了现有城市:

/// d ##class(PHA.OP.MOB.Test).TestSaveOtherModify()
ClassMethod TestSaveOtherModify()
{
    set customer=##class(MyApp.Customers).%New()
    set customer.Name="Emerson,S."
    set customer.HomeStreet="295 School Lane"
    set customer.HomeCity=##class(MyApp.Cities).%OpenId(2)
    set customer.HomeCity.ZIP="11111"
    set status=customer.%Save()
    if $$$ISERR(status) {
        do $system.Status.DisplayError(status)
    }
}

当然,此更改将对该本地城市的任何其他客户可见。


image.png
image.png

对象值属性的SQL映射

如前面所述,一个持久化类被映射为一个SQL表。本节介绍如何将此类的引用属性和嵌入式对象属性映射到SQL。

引用属性

引用属性被映射为包含引用对象的OID的ID部分的字段。例如,假设一个客户对象具有一个引用SalesRep对象的Rep属性。如果特定客户的销售代表的ID为12,则该客户的“代表”列中的条目也是12。由于此值与引用对象的ID列的特定行的值匹配,因此在执行任何联接或其他处理时可以使用此值。

请注意,在CachéSQL中,可以使用特殊的引用语法轻松地使用此类引用,以代替使用JOIN。例如:

SELECT Company->Name FROM Sample.Employee ORDER BY Company->Name

嵌入式对象属性

嵌入式对象属性被映射为父类表中的多列。映射中的一列包含序列化形式的整个对象(包括所有定界符和控制字符)。其余各列分别用于对象的一个属性。

对象属性的列名与对象属性本身的列名相同。对象的一种属性。其他列名称由对象属性的名称,下划线和嵌入对象内的属性组成。

例如,假设一个类具有一个Home属性,其中包含一个Address类型的嵌入式对象;房屋本身的属性包括Street 和 Country.。然后,嵌入式对象的映射包括名为“Home_Street”和“Home_Country”的列。

注意:(请注意,列名称是从属性Home而不是地址Address派生的。)

例如,示例类Sample.Person包含Home属性,该属性是Sample.Address类型的嵌入式对象。可以通过SQL使用Home的组件字段,如下所示:

SELECT Name, Home_City, Home_State FROM Sample.Person 
WHERE Home_City %STARTSWITH 'B'
ORDER BY Home_City

嵌入式对象还可以包括其他复杂的数据形式:

  • 引用属性的映射包括一个只读字段,该字段包含对象引用。
  • 数组的映射是作为表一部分的单个不可编辑的列。
  • 列表的映射作为列表字段作为其映射字段之一;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352