第十一章 Caché 使用集合

第十一章 Caché 使用集合

Caché支持集合,集合提供了使用一组相同类型的元素的方式。元素可以是文字值,也可以是对象。
以在任何对象类中定义集合属性。还可以将独立集合定义为其他用途,例如用作方法参数或返回值。本章介绍集合,尤其是集合属性。

简介集合

集合包含一组单独的元素,所有元素都具有相同的类型。集合有两种:列表(list)和数组(array)。

集合中的每个项目称为元素,其在集合中的位置称为键。对于列表集合,Caché生成顺序整数键。对于数组,键可以具有任意值,可以为每个元素指定它们。

Caché使用一组集合类作为集合属性的接口;这些是%Collection包中的类。Caché提供了一组不同的集合类,供在需要独立集合时使用,例如,将其作为参数传递给方法。这些是%Library包中的类。

每组类都提供了可用于添加集合项,删除集合项,对集合项计数的方法和属性。本章重点介绍%Collection类,但细节与%Library类相似。

请注意,集合类是对象类。因此,集合是一个对象。

定义集合属性

要定义列表属性,请添加一个属性,如下所示:

Property MyProp as List of Type;

其中MyProp是属性名称,而Type是数据类型类或对象类。

同样,要定义数组属性,请添加一个属性,如下所示:

Property MyProp as Array of Type;

例如,以下属性定义是%String值的列表:

Property Colors As List Of %String;

对于另一个示例,以下属性定义是Doctor值的数组,其中Doctor是对象类的名称。

Property Doctors As Array Of Doctor;

在内部,Caché使用%Collection包中的类来表示此类属性,如下所示:

  • %Collection.ListOfDT (如果list元素是数据类型类)
  • %Collection.ListOfObj (如果list元素是对象类型类)
  • %Collection.ArrayOfDT (如果array元素是数据类型类)
  • %Collection.ArrayOfObj (如果array元素是对象类型类)

这意味着可以使用这些类的方法来添加集合项,删除集合项等。本章后面的部分将说明如何完成此操作。

不要将%Collection类直接用作属性的类型。例如,不要创建这样的属性定义:

Property MyProp as %Collection.ArrayOfDT;

而是使用本节前面显示的语法。

给List属性添加子项

给定一个列表属性(如上一节所述),请使用以下过程为该属性指定一个值:

  • 如果列表项是对象,请根据需要创建那些对象。
  • 根据需要将列表项添加到列表中。若要添加一个列表项,请调用list属性的Insert()实例方法。该方法如下:
method Insert(listitem) as %Status

或使用list属性的其他方法,例如InsertAt()。

例如,假设obj是OREF,而Colors是关联对象的list属性。在这种情况下,我们可以添加列表项,如下所示:

 Do obj.Colors.Insert("Red")
 Do obj.Colors.Insert("Green")
 Do obj.Colors.Insert("Blue")

对于另一个示例,假设pat是OREF,而Diagnoses是关联对象的list属性。此属性定义如下,其中PatientDiagnosis是类的名称:

Property Diagnoses as list of PatientDiagnosis;

在这种情况下,我们可以添加一个列表项,如下所示:

 Set patdiag=##class(PatientDiagnosis).%New()
 Set patdiag.DiagnosisCode=code
 Set patdiag.DiagnosedBy=diagdoc
 Set status=pat.Diagnoses.Insert(patdiag)

完整示例

Property Colors As list Of %String;

Property objectList As list Of PHA.OP.MOB.TestTwo;
ClassMethod TestList()
{
	s test=##class(PHA.OP.MOB.Test).%New()
	d test.Colors.Insert("Red")
	d test.Colors.Insert("white")
	d test.Colors.Insert("black")
	
	s one=##class(PHA.OP.MOB.TestTwo).%New(1)
	s two=##class(PHA.OP.MOB.TestTwo).%New(2)
	s status= test.objectList.Insert(one)
	s status= test.objectList.Insert(two)
}

给Array属性添加子项

给定一个数组属性(如本章前面所述),请使用以下过程为该属性指定一个值:

  1. 如果数组项是对象,请根据需要创建那些对象。
  2. 根据需要将数组项添加到数组。若要添加一个列表项,请调用array属性的SetAt()实例方法。该方法如下:
method SetAt(element, key As %String) as %Status

其中element是要添加的元素,key是要与该元素关联的数组键。

注意:请勿在用作数组键的值内包含连续的一对垂直条(||)。此限制是由CachéSQL机制的工作方式施加的。

有关此方法的详细信息,请参见%Collection.ArrayOfDT和%Collection.ArrayOfObj的类参考。

例如,要将新颜色添加到通过Palette对象中的颜色名称访问的RGB值数组中,请使用以下代码:

Do palette.Colors.SetAt("255,0,0","red")

其中Palette是包含数组的OREF,Colors是数组属性的名称,“ red”是访问值“ 255,0,0”的键。

注意:Array其实就是HashMap。

完整示例:

Property Doctors As array Of %String;

Property testTwo As array Of PHA.OP.MOB.TestTwo;

ClassMethod Array()
{
	s test=##class(PHA.OP.MOB.Test).%New()
	d test.Doctors.SetAt("小王","护士")
	d test.Doctors.SetAt("小李","医生")
	d test.Doctors.SetAt("小张","院长")
	
	s one=##class(PHA.OP.MOB.TestTwo).%New(1)
	s two=##class(PHA.OP.MOB.TestTwo).%New(2)
	s status= test.objectList.SetAt(one,"first")
	s status= test.objectList.SetAt(one,"second")
}


使用List属性

如前所述创建List属性时,该属性本身是一个对象,它提供以下类之一的实例方法,具体取决于属性定义:

  • %Collection.ListOfDT (list元素是数据类型)
  • %Collection.ListOfObj list元素是对象类型)

这些类提供实例方法,如GetAt()、Find()、GetPrevious()、GetNext()和Remove()。以下示例显示如何使用这些方法:

Property Colors As list Of %String;

Property objectList As list Of PHA.OP.MOB.TestTwo;

/// d ##class(PHA.OP.MOB.Test).TestList()
ClassMethod TestList()
{
	s test=##class(PHA.OP.MOB.Test).%New()
	d test.Colors.Insert("Red")
	d test.Colors.Insert("white")
	d test.Colors.Insert("black")
	for i=1:1:test.Colors.Count() d
	.write test.Colors.GetAt(i),!
}

DHC-APP>d ##class(PHA.OP.MOB.Test).TestList()
Red
white
black

列表是信息的有序集合。每个列表元素由其在列表中的位置(插槽)标识。可以设置插槽的值或在插槽中插入数据。如果为插槽设置新值,该值将存储在列表中。如果设置现有插槽的值,则新数据将覆盖以前的数据,并且插槽分配不会修改。如果在现有插槽中插入数据,则新列表项将递增所有后续插槽的插槽编号。(在第二个插槽中插入新项目将当前在第二个插槽中的数据滑动到第三个插槽,将当前在第三个插槽中的对象滑动到第四个插槽,依此类推。)

可以使用以下语法修改插槽n处的数据:

 Do oref.PropertyName.SetAt(data,n)

其中OREF是OREF,PropertyName是该对象的列表属性的名称,DATA是实际数据。

例如,假设Person.FavoriteColors是一个最喜欢的颜色列表,并且假设该列表最初是“红色”、“蓝色”和“绿色”。要更改列表中的第二种颜色(使列表为“红色”、“黄色”和“绿色”),我们可以使用以下代码:

Do person.FavoriteColors.SetAt("yellow",2)
Property Colors As list Of %String;

Property objectList As list Of PHA.OP.MOB.TestTwo;

/// d ##class(PHA.OP.MOB.Test).TestList()
ClassMethod TestList()
{
	s test=##class(PHA.OP.MOB.Test).%New()
	d test.Colors.Insert("Red")
	d test.Colors.Insert("white")
	d test.Colors.Insert("black")
	
	for i=1:1:test.Colors.Count() d
	.write test.Colors.GetAt(i),!
	
	d test.Colors.SetAt("yellow",2)
	
	for i=1:1:test.Colors.Count() d
	.write test.Colors.GetAt(i),!
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestList()
Red
white
black
Red
yellow
black

注意: SetAt可以修改位置的值

使用Array属性

如前所述创建数组属性时,该属性本身就是一个提供以下类之一的实例方法的对象,具体取决于属性定义:

  • %Collection.ArrayOfDT (数组元素为数据类型)
  • %Collection.ArrayOfObj (数据元素为对象类型)

这些类提供实例方法,如GetAt()、Find()、GetPrevious()、GetNext()和Remove()。

Property Doctors As array Of %String;

Property testTwo As array Of PHA.OP.MOB.TestTwo;

/// d ##class(PHA.OP.MOB.Test).TestArray()
ClassMethod TestArray()
{
	s test=##class(PHA.OP.MOB.Test).%New()
	d test.Doctors.SetAt("小王","护士")
	d test.Doctors.SetAt("小李","医生")
	d test.Doctors.SetAt("小张","院长")
	
	w test.Doctors.Count(),!
	write test.Doctors.GetAt("护士"),!
	write test.Doctors.GetAt("医生"),!
	write test.Doctors.GetAt("院长"),!
}

DHC-APP>d ##class(PHA.OP.MOB.Test).TestArray()
3
小王
小李
小张

复制数据集合

若要将一个集合中的项复制到另一个集合中,请将接收集合设置为与源集合相等。这会将源的内容复制到接收方(而不是集合本身的OREF)。

此类命令的一些示例包括:

 Set person2.Colors = person1.Colors
 Set dealer7.Inventory = owner3.cars

其中Person2、Person1、Designer7和owner3都是Class的实例,而Colors、Inventory和Cars都是集合属性。

第一行代码可能用于在单个类的两个实例之间复制数据,第二行代码可能用于将数据从一个类的实例复制到另一个类的实例。

如果接收集合是列表,而源集合是数组,则Caché只复制数组的数据(不复制其键值)。

如果接收集合是数组,而源集合是列表,则Caché将为接收数组生成键值;这些键值是基于项在源列表中的位置的整数。

注意:无法将OREF从一个集合复制到另一个集合。只能复制数据。

控制集合属性的SQL映射

正如本书前面所述,持久类被映射为SQL表。本节介绍默认情况下如何映射列表和数组属性,以及如何修改这些SQL映射。

列表属性的默认映射(list)

默认情况下,列表属性以序列化形式作为 L I S T S Q L 使 LIST映射到SQL。这意味着当获得这样的值时,应该使用适用于 LIST的函数才能使用它。

下面的示例通过嵌入式SQL获取List属性的值,然后使用合适的函数处理该值:

/// d ##class(PHA.OP.MOB.Test).TestDefaultProjectionList()
ClassMethod TestDefaultProjectionList()
{
	&sql(SELECT favoritecolors INTO :FavCol FROM Sample.Person WHERE id=13)
	write !, $LISTVALID(FavCol)
	for i=1:1:$LISTLENGTH(FavCol) {
		write !, $LIST(FavCol,i)
	}
}

DHC-APP>d ##class(PHA.OP.MOB.Test).TestDefaultProjectionList()
 
1
Red
Orange
Yellow
Green
Property FavoriteColors As list Of %String(JAVATYPE = "java.util.List", POPSPEC = "ValueList("",Red,Orange,Yellow,Green,Blue,Purple,Black,White""):2");

/// w ##class(PHA.OP.MOB.Test).TestSaveObject()
ClassMethod TestSaveObject()
{
	Set obj = ##class(Sample.Person).%New()
	Set obj.Name = "姚鑫"
	Set obj.SSN="111-11-1115"
	d obj.FavoriteColors.Insert("Red")
	d obj.FavoriteColors.Insert("Orange")
	d obj.FavoriteColors.Insert("Yellow")
	d obj.FavoriteColors.Insert("Green")
	Set sc = obj.%Save()
	q sc
}

如果特定实例的列表不包含任何元素,则会将其映射为空字符串(而不是SQL null值)。

数组属性的默认映射(array)

默认情况下,数组属性被映射为子表,子表与父表在同一个包中。该子表的名称如下:

tablename_fieldname
  • tablename是父类的SqlTableName(如果指定)或父类的短名称(如果未指定SqlTableName)。
  • fieldname是数组属性的SqlFieldName(如果指定)或数组属性的名称(如果未指定SqlFieldName)。

例如,具有名为siblings的数组属性的Person类有一个名为“Person_Siblings”的子表映射。

子表包含以下三列:

  • 一个包含父类的相应实例的ID;此列的名称是包含数组的类的名称(在本例中为Person)。
  • 一个包含每个数组成员的标识符;在本例中,其名称始终为element_key.rson)。
  • 一个包含类的所有实例的数组成员;它的名称是数组属性的名称(在本例中为siblings)。

继续使用名为siblings的数组属性的Person类的示例,Person的映射包括具有以下条目的Person_Siblings子表:

Array属性的映射示

Person (ID) element_key Siblings
10 C Claudia
10 T Tom
12 B Bobby
12 C Cindy
12 G Greg
12 M Marsha
12 P Peter

如果父类的实例包含空集合(不包含元素的集合),则该实例的ID不会出现在子表中,例如上面ID等于11的实例。

请注意,父表中没有Siblings列。

对于包含数组成员的列,列的数量和内容取决于数组的类型:

  • 数据类型属性数组的映射是单列数据。
  • 引用属性数组的映射是对象引用的单列。
  • 嵌入对象数组的映射是子表中的多列。

每个实例的ID和每个数组成员的标识符一起构成子表的唯一索引。此外,如果父实例没有关联的数组,则它在子表中没有关联的条目。

注意:默认情况下,序列对象属性以相同的方式映射到SQL。

重要提示:当集合属性被映射为数组时,对于可能添加到该属性的任何索引都有特定的要求

代替集合的映射

本节讨论影响集合属性存储和映射到SQL的方式的STORAGEDEFAULT、SQLTABLENAME和SQLPROJECTION属性参数。

STORAGEDEFAULT参数

可以将列表属性存储为子表,也可以将数组属性存储为$LIST。在这两种情况下,都需要指定属性的STORAGEDEFAULT参数:

  • 对于列表特性,默认情况下STORAGEDEFAULT为“LIST”。如果将STORAGEDEFAULT指定为“ARRAY”,则数量将存储并映射为子表。例如:
Property MyList as list of %String (STORAGEDEFAULT="array");
  • 对于数组属性,默认情况下STORAGEDEFAULT为“Array”。如果将STORAGEDEFAULT指定为“ list”,则该属性将存储并映射为$ LIST。例如:
Property MyArray as array of %String (STORAGEDEFAULT="list");

STORAGEDEFAULT属性参数影响编译器如何为该类生成存储。如果类定义已经包含给定属性的存储定义,则编译器将忽略此属性参数。

SQLTABLENAME参数

如果将集合属性映射为子表,则可以控制该表的名称。为此,请指定属性的SQLTABLENAME参数。例如:

Property MyArray As array Of %String(SQLTABLENAME = "MyArrayTable");

Property MyList As list Of %Integer(SQLTABLENAME = "MyListTable", STORAGEDEFAULT = "array");

除非将该属性映射为子表,否则SQLTABLENAME参数无效。

SQLPROJECTION参数

默认情况下,如果将集合属性存储为子表,则它也将映射为子表,但在父表中不可用。要使该属性在父表中也可用,请将属性的SQLPROJECTION参数指定为“ table / column”

例如,考虑以下类定义:

Class Sample.Sample Extends %Persistent
{

Property Property1 As %String;

Property Property2 As array Of %String(SQLPROJECTION = "table/column");

}

Caché为此类生成两个表:Sample.Sample和Sample.Sample_Property2

与默认方案一样,表Sample.Sample_Property2存储数组属性Property2的数据。和Sample.Sample_Property2.但是,与默认方案不同,查询可以引用Sample.Sample表中的Property2字段。例如:

SAMPLES>>SELECT Property2 FROM Sample.Sample where ID=7
13.     SELECT Property2 FROM Sample.Sample where ID=7
 
Property2
"1     value 12       value 23       value 3"

SELECT *查询,但是,不返回Property2字段:

SAMPLES>>SELECT * FROM Sample.Sample where ID=7
14.     SELECT * FROM Sample.Sample where ID=7
 
ID      Property1
7       abc

SQLPROJECTION属性参数还有其他可能的值,但是这些值仅在启用MV的类中有效。

创建和使用独立集合

以下类旨在用作不是类属性的集合:

  • %ListOfDataTypes (如果list元素是数据类型类)
  • %ListOfObjects (如果list元素是一个对象类)
  • %ArrayOfDataTypes (如果数组元素是数据类型类)
  • %ArrayOfObjects (如果数组元素是对象类)

若要创建独立集合,请调用适当类的%New()方法以获得该类的实例。

然后使用该实例的方法添加元素,依此类推。例如:

/// d ##class(PHA.OP.MOB.Test).TestUseCollect()
ClassMethod TestUseCollect()
{
	set list=##class(%ListOfDataTypes).%New()
	do list.Insert("red")
	do list.Insert("green")
	do list.Insert("blue")
	write list.Count(),!
	
	set array=##class(%ArrayOfDataTypes).%New()
	do array.SetAt("red","红色")
	do array.SetAt("green","绿色")
	write array.Count(),!
	
	s one=##class(PHA.OP.MOB.TestTwo).%New(1)
	s two=##class(PHA.OP.MOB.TestTwo).%New(2)

	set listObject=##class(%ListOfObjects).%New()
	s status= listObject.Insert(one)
	s status= listObject.Insert(two)
	write listObject.Count(),!
	
	set arrayObject=##class(%ArrayOfObjects).%New()
	s status= arrayObject.SetAt(one,"first")
	write arrayObject.Count(),!
}

DHC-APP>d ##class(PHA.OP.MOB.Test).TestUseCollect()
3
2
2
1

重点:%Populate随机生成数据

/// w ##class(PHA.OP.MOB.Test).TestSaveObject()
ClassMethod TestSaveObject()
{
	Set obj = ##class(Sample.Person).%New()
	Set obj.Name = ##class(%PopulateUtils).Name()
	Set obj.SSN=##class(%PopulateUtils).SSN()
	d obj.FavoriteColors.Insert("Red")
	d obj.FavoriteColors.Insert("Orange")
	d obj.FavoriteColors.Insert("Yellow")
	d obj.FavoriteColors.Insert("Green")
	d obj.FavoriteColors.Insert(##class(%PopulateUtils).Color())
	
	Set sc = obj.%Save()
	Do ##class(Sample.Person).Populate(100)
	q sc
}
##class(%PopulateUtils).Name() 随机生成数据

查询数据存在与否 Find 和Data

/// d ##class(PHA.OP.MOB.Test).TestUseCollect()
ClassMethod TestUseCollect()
{
	set list=##class(%ListOfDataTypes).%New()
	do list.Insert("red")
	do list.Insert("green")
	do list.Insert("blue")
	write list.Count(),!
	
	w "aaaa"_list.Find("agreen"),!
	
	set array=##class(%ArrayOfDataTypes).%New()
	do array.SetAt("red","红色")
	do array.SetAt("green","绿色")
	write array.Count(),!
	
	w "aaab"_array.Find("green"),!
	//存在不报错
	w "aaac"_array.Data("绿色")
	
	w "aaac"_array.Data("a绿色")
	s one=##class(PHA.OP.MOB.TestTwo).%New(1)
	s two=##class(PHA.OP.MOB.TestTwo).%New(2)

	set listObject=##class(%ListOfObjects).%New()
	s status= listObject.Insert(one)
	s status= listObject.Insert(two)
	write listObject.Count(),!
	
	set arrayObject=##class(%ArrayOfObjects).%New()
	s status= arrayObject.SetAt(one,"first")
	write arrayObject.Count(),!
}

DHC-APP>d ##class(PHA.OP.MOB.Test).TestUseCollect()
3
aaaa
2
aaab绿色
 
 w "aaac"_array.Data("a绿色")
 ^
<UNDEFINED>zTestUseCollect+12^PHA.OP.MOB.Test.1 *.Data("a绿色")
发布了13 篇原创文章 · 获赞 1 · 访问量 355

猜你喜欢

转载自blog.csdn.net/yaoxin521123/article/details/105044417