Caché包含一个实用程序,用于为持久性类创建伪随机测试数据。此类数据的创建称为数据填充。用于执行此操作的实用程序称为Caché填充实用程序,可用于在将持久性类部署到实际应用程序之前对其进行测试。在测试处理大量数据时应用程序的各个部分将如何工作时,这特别有用。
populate实用程序的名称来自其主要元素–%Populate类,它是Caché类库的一部分。从%Populate继承的类包含一个称为Populate()的方法,该方法使可以生成和保存包含有效数据的类实例。还可以自定义%Populate类的行为以提供满足需要的数据。
与%Populate类一起,填充工具使用%PopulateUtils。%Populate提供实用程序的接口,而%PopulateUtils是帮助程序类。
要使用填充工具,请执行以下操作:
Class MyApp.MyClass Extends (%Persistent,%Populate) {}
不要将%Populate用作主父类;也就是说,不要将其列为父类列表中的第一类。
或在CachéStudio中使用“ New Class Wizard”时,在最后一个屏幕上检查“数据填充”。这等效于将%Populate类添加到超类列表。
在这些类中,可以选择指定每个属性的POPSPEC和POPORDER参数,以控制填充实用程序为这些属性生成数据的方式(如果要生成自定义数据而不是默认数据,将在下一节中进行描述)。
重新编译类。
要生成数据,请调用每个持久化类的Populate()方法。默认情况下,此方法为该类生成10条记录(包括其引用的任何串行对象):
Do ##class(MyApp.MyClass).Populate()
可以指定要创建的对象数
Do ##class(MyApp.MyClass).Populate(num)
num是要创建的对象数量
为类手动添加记录相同的顺序执行此操作。也就是说,如果类A具有引用类B的属性,请使用下表确定首先填充哪个类:
如果A类中的属性具有此格式... | B类继承自... | 首先填充此类... |
---|---|---|
Property PropertyName as ClassB; | %SerialObject | ClassA(自动填充ClassB) |
Property PropertyName as List of ClassB; | ||
Property PropertyName as Array of ClassB; | ||
Property PropertyName as Array of ClassB; | %Persistent | ClassB |
Property PropertyName as List of ClassB; | ||
Property PropertyName as Array of ClassB; | ||
Relationship PropertyName as ClassB [ Cardinality = one ...]; | either | |
Relationship PropertyName as ClassB [ Cardinality = parent ...]; | ||
Relationship PropertyName as ClassB [ Cardinality = many...]; | either | ClassA |
Relationship PropertyName as ClassB [ Cardinality = child ...]; |
要删除生成的数据,请使用持久性接口的%DeleteExtent()方法(安全)或%KillExtent()方法。
提示:实际上,在更改代码时,经常有必要重复填充类。因此,编写一种方法或例程以正确的顺序填充类以及删除生成的数据非常有用。
classmethod Populate(count As %Integer = 10,
verbose As %Integer = 0,
DeferIndices As %Integer = 1,
ByRef objects As %Integer = 0,
tune As %Integer = 1) as %Integer
Populate()返回实际填充的对象数:
Set objs = ##class(MyApp.MyClass).Populate(100)
在具有定义的约束(例如最小或最大长度)的情况下,某些生成的数据可能无法通过验证,因此不会保存单个对象。在这些情况下,Populate()可能创建的对象少于指定数量的对象。
如果错误导致对象无法保存,并且顺序发生1000次却没有成功保存,则Populate()退出。
Populate()方法将忽略流属性。
本部分描述默认情况下Populate()方法如何为表单属性生成数据:
Property PropertyName as Type;
Property PropertyName;
其中Type是数据类型类。
对于这些属性,Populate()方法首先查看名称。一些属性名称经过特殊处理,如下所示:
如果属性名称是以下任何一种情况的变量 | Populate()调用以下方法为其生成数据 |
---|---|
NAME | Name() |
SSN | SSN() |
COMPANY | Company() |
TITLE | Title() |
PHONE | USPhone() |
CITY | City() |
STREET | Street() |
ZIP | USZip() |
MISSION | Mission() |
STATE | USState() |
COLOR | Color() |
PRODUCT | Product() |
如果属性没有上述名称之一,则Populate()方法将查看属性类型并生成合适的值。例如,如果属性类型为%String,则Populate()方法将生成随机字符串(使用属性的MAXLEN参数)。对于另一个示例,如果属性类型为%Integer,则Populate()方法将生成随机整数(使用属性的MINVAL和MAXVAL参数)。
如果属性没有类型,则Caché假定它是字符串。这意味着Populate()方法为其值生成随机字符串。
如果属性是私有的、多维的、经过计算的或具有初始表达式,则Populate()方法不会为该属性生成数据。
本部分描述默认情况下Populate()方法如何为表单属性生成数据:
Property PropertyName as List of Classname;
Property PropertyName as Array of Classname;
对于此类属性:
Property PropertyName as SerialObject;
其中SerialObject是从%SerialObject继承的类。
对于此类属性:
如果引用的类从%Populate继承,则Populate()方法将创建该类的实例并生成上一节中所述的属性值。
如果引用的类不继承自%Populate,则Populate()方法不会为该属性生成任何值。
本部分描述默认情况下Populate()方法如何为以下形式的属性生成数据:
Property PropertyName as PersistentObject;
其中PersistentObject是从%Persistent继承的类。
对于此类属性:
请注意,这意味着必须首先为所引用的类生成数据。或以其他任何方式为该类创建数据。
本部分描述默认情况下Populate()方法如何为以下形式的属性生成数据:
Relationship PropertyName as PersistentObject;
其中PersistentObject是从%Persistent继承的类。
对于此类属性:
如果引用的类继承自%Populate:
请注意,这意味着必须首先为所引用的类生成数据。或以其他任何方式为该类创建数据。
对于扩展%Populate的类中的给定属性,可以自定义Populate()方法如何为该属性生成数据。为此,请执行以下操作:
POPSPEC参数为列表和数组属性提供了其他选项,将在后面的小节中讨论。
对于文字集合属性,另一种技术是识别SQL表列,该列包含用于此属性的值。然后指定POPSPEC参数以引用此属性;请参阅最后一个小节。
注意:在类级别还定义了一个POPSPEC参数,该参数控制整个类的数据填充。这是一种较旧的机制(出于兼容性考虑,已包含在内)已由特定于属性的POPSPEC参数代替。本附录不再进一步讨论。
对于不是集合的文字属性,请使用以下变体之一:
POPSPEC=“MethodName()” 在这种情况下,Populate()调用%PopulateUtils类的方法MethodName ()。
POPSPEC=".MethodName()" 在这种情况下,Populate()调用正在生成的实例的实例方法MethodName()。
POPSPEC="##class(ClassName).MethodName()" 在这种情况下,Populate()调用ClassName类的方法MethodName()。
例如:
Property HomeCity As %String(POPSPEC = "City()");
如果需要将字符串值作为参数传递给给定方法,请将该字符串前后的引号和结束引号引起来加倍。例如:
Property PName As %String(POPSPEC = "Name(""F"")");
另外,可以将字符串附加到指定方法返回的值。例如:
Property JrName As %String(POPSPEC = "Name()_"" jr."" ");
请注意,必须在该字符串周围将双引号和双引号引起来。不可能在字符串前加上前缀,因为POPSPEC以方法开头。
对于包含文字或对象列表的属性,可以使用以下变体:
POPSPEC="basicspec:MaxNo"
basicspec是上一节中显示的基本变体之一。如果属性是对象列表,则将basicspec保留为空。
MaxNo是列表中的最大项目数。默认值为10。
例如
Property MyListProp As list Of %String(POPSPEC = ".MyInstanceMethod():15");
可以省略basicspec。例如:
Property Names As list of Name(POPSPEC=":3");
在以下示例中,列出了几种类型的数据。Colors是字符串列表,Kids是持久对象引用列表,Addresses 是嵌入式对象列表。例如:
Property Colors As list of %String(POPSPEC="ValueList("",Red,Green,Blue"")");
Property Kids As list of Person(POPSPEC=":5");
Property Addresses As list of Address(POPSPEC=":3");
若要为Colors属性生成数据,Populate()方法将调用PopulateUtils类的ValueList()方法。请注意,此示例将逗号分隔的列表作为此方法的参数传递。对于Kids属性,没有指定的方法,该方法会导致自动生成引用。对于Addresses属性,串行Address类从%Populate继承,并自动为该类的实例填充数据。
对于由文字或对象组成的数组的属性,可以使用以下变体:
POPSPEC="basicspec:MaxNo:KeySpecMethod"
以下示例显示了几种数据类型和不同种类的键的数组:
Property Tix As array of %Integer(POPSPEC="Integer():20:Date()");
Property Reviews As array of Review(POPSPEC=":3:Date()");
Property Actors As array of Actor(POPSPEC=":15:Name()");
Tix属性的数据是使用PopulateUtils类的Integer()方法生成的;其键是使用PopulateUtils类的Date()方法生成的。Reviews属性没有指定的方法,该方法将导致自动生成引用,并且其键也使用Date()方法生成。Actors属性没有指定的方法,该方法会导致自动生成引用,并且其键是使用PopulateUtils类的Name()方法生成的。
对于POPSPEC,可以指定要使用的SQL表名称和SQL列名称,而不是指定返回随机值的方法。如果这样做,则Populate()方法将构造一个动态查询以返回该表的该列的不同列值。对于POPSPEC的此变体,请使用以下语法:
POPSPEC=":MaxNo:KeySpecMethod:SampleCount:Schema_Table:ColumnName"
MaxNo和KeySpecMethod是可选的,仅适用于集合属性(请参见前面有关列表和数组的小节)。
SampleCount是要从给定列检索的不同值的数量,以用作起点。如果该值大于该列中现有的不同值的数量,则可能使用所有值。
Schema_Table是表的名称。
ColumnName是列的名称。
例如
Property P1 As %String(POPSPEC=":::100:Wasabi_Data.Outlet:Phone");
在此示例中,属性P1从Wasabi_Data.Outlet表中检索的100个电话号码列表中接收随机值。
在某些情况下,一个属性(A)的一组适当值可能取决于另一属性(B)的现有值。在这种情况下:
Method MyMethod() As %String
{
if (i%MyBooleanProperty) {
quit "abc"
} else {
quit "def"
}
}
Property Name As %String(POPORDER = 2, POPSPEC = ".MyNameMethod()");
Property Gender As %String(POPORDER = 1, VALUELIST = ",1,2");
本节介绍%Populate在内部如何工作。%Populate类包含两个方法生成器:Populate()和PopulateSerial()。从%Populate继承的每个持久性或序列类都包含这两种方法中的一种或另一种(视情况而定)。
我们将在这里仅描述Populate方法。 Populate()方法是一个循环,对每个请求数量的对象重复执行此循环。
在循环内部,代码:
没有覆盖POPSPEC参数的简单属性具有使用以下形式的代码生成的值:
Set obj.Description = ##class(%PopulateUtils).String(50)
通过%PopulateUtils通过“ Name:Name()”规范使用库方法时,将生成:
Set obj.Name = ##class(%PopulateUtils).Name()
嵌入式Home属性可能会创建如下代码:
Do obj.HomeSetObject(obj.Home.PopulateSerial())
生成器遍历该类的所有属性,并为某些属性创建代码,如下所示:
它检查属性是私有的,已计算的,是多维的还是具有初始表达式。如果其中任何一个为真,则生成器退出。
如果该属性具有POPSPEC替代,则生成器将使用该替代,然后退出。
如果该属性是引用,则在循环中的第一次生成器会生成一个随机ID列表,从列表中获取一个,然后退出。对于随后的遍,生成器仅从列表中获取一个ID,然后退出。
如果属性名称是特殊处理的名称之一,则生成器将使用相应的库方法,然后退出。
如果生成器可以基于属性类型生成代码,则生成器然后将其退出。
否则,生成器将属性设置为空字符串。有关可用方法的列表,请参考%PopulateUtils类。
为了对生成的数据进行其他控制,可以定义OnPopulate()方法。如果定义了OnPopulate()方法,则Populate()方法为它生成的每个对象调用它。在为属性分配值之后但在将对象保存到磁盘之前调用该方法。每次调用Populate()方法都会导致检查OnPopulate()方法是否存在,并对其生成的每个对象都调用OnPopulate()。
在将值分配给属性之后但将对象保存到磁盘之前,Populate方法将调用此实例方法。此方法提供了对生成数据的附加控制。如果存在OnPopulate()方法,则Populate方法为它生成的每个对象调用它。
Method OnPopulate() As %Status
{
// body of method here...
}
注意:这不是私有方法。
该方法返回%Status代码,其中错误状态导致正在填充的实例被丢弃。
例如,如果具有流属性Memo,并希望在填充时为其分配一个值,则可以提供OnPopulate()方法:
Method OnPopulate() As %Status
{
Do ..Memo.Write("Default value")
QUIT $$$OK
}
可以在%Library.Populate的子类中重写此方法。
还有另一种使用%Populate和%PopulateUtils类的方法的方法。而不是使用%Populate作为父类,而是编写一个实用程序方法来为类生成数据。
在此代码中,对于每个类,迭代所需的次数。在每次迭代中:
要为属性生成数据,请调用%Populate或%PopulateUtils方法或使用自己的方法。
3. 保存对象
与标准方法一样,必须为独立类生成数据,然后再为相关类生成数据。
在某些情况下,可能只希望在某些情况下包括某些值。可以使用$ RANDOM函数执行此操作。
在DeepSee.Populate中,示例方法RandomTrue()随机返回true或false,具体取决于作为参数提供的截止百分比。因此,例如,它可以在10%的时间或75%的时间返回true。 (内部,此方法使用$ RANDOM。)
在为属性生成数据时,可以使用此方法确定是否分配值:
If ##class(DeepSee.Populate).RandomTrue(15) {
set object.property="something"
}
在此处显示的示例中,大约15%的记录将具有此属性的给定值。
在其他情况下,可能需要模拟分布。为此,请设置并使用 lottery系统。例如,假设值的1/4应该是A,值的1/4应该是B,值的1/2应该是C。lottery的逻辑可以像这样: