清除过滤器
问题
water huang · 四月 22, 2021
m 里面如何获取cpu的序列号? 可以调用操作系统的命令来获取CPU序列号。例如在Cache' for Windows上,可以执行:SAMPLES>s args=3SAMPLES>s args(1)="CPU"SAMPLES>s args(2)="get"SAMPLES>s args(3)="ProcessorID"SAMPLES>d $ZF(-100,"","wmic",.args)ProcessorId0FABFBFF000506EX0FABFBFF000006EX0FABFBFF000006EX0FABFBFF000006EX 乔工,请问 $zf函数的使用,在哪里可以查询到它的所有使用说明 InterSystems Cache'InterSystems IRIS 刚才试了一下,这个不行呢 感谢你的回答 但是我用的是ensemble2016 是Windows吗?在Windows命令行,执行wmic CPU get ProcessorID,能得到CPU序列号吗? Ensemble 2016有点久,没有$ZF(-100)。用$ZF(-1):
https://cedocs.intersystems.com/ens20161/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_fzf-1#RCOS_B80417 是Windows,在Windows命令行,执行wmic CPU get ProcessorID,能得到CPU序列号 刚才试了 还是不行 用$ZF(-1), 可以将OS命令输出保存到文件里。例如:w $ZF(-1,"wmic CPU get ProcessorID > c:\temp\cpuinfo.txt") 系统是windows 10 试了,不行,返回的值是1 返回值是1,说明报错了。确认一下是否OS命令写正确了。另外,输出不是看返回值,是看输出的文件 我直接复制的你写的这个命令。
文章
Michael Lei · 五月 24, 2021
作为一个软件架构师,如果要设计一个企业级的架构来满足当前的业务需求时,你需要达到5级的水平,这是一个巨大的挑战。有了InterSystems IRIS。这是有可能的。通过1个产品,你可以得到SQL + NoSQL + ESB + BI + Open Analytics + Real Time Virtual cubes + NLP + AutoML + ML(使用Python)和高级云支持 + Sharding支持。
以Oracle为例,你需要Oracle数据库+ Oracle NoSQL+ Oracle BI+ Oracle SOA套件+一些附加组件,如R、Partitioning和RAC以及一些Oracle云产品的NLP和AI功能。对于开源架构、IBM或者其他友商来说,也是类似的,你需要的组件只可能更多。
医疗机构在数字化转型在技术上最大的挑战之一,就是技术栈过于丰富、快速变化以及对人的技术要求很高(这也是为什么全栈工程师最值钱的原因);一个统一、高效、稳定、现代化、专为医疗定制的一体化数据平台能帮助客户很好地解决这个问题。IRIS for Health 是您的不二选择。
文章
Michael Lei · 六月 1, 2022
InterSystems Production 监控是管理门户中的一个页面,用于显示当前运行的Production监控信息。我喜欢这个页面的样子,但这并不适合所有人。
2022年5月13日,我在开发者社区上看到这个帖子。
https://community.intersystems.com/post/creating-custom-monitoring-page
我同意马克的观点,Production监控很复杂。我想创建一个更漂亮干净的监控页面。
我开始着手制作一个利用类方法提供Production数据的 CSP(Cache Server Page)页面。
我与马克分享了我的第一次尝试。他根据自己的想法定制了这个页面。我喜欢他对页面的布局,使其在视觉上更有吸引力。我把他的设计整合到我的应用程序Production监控中。
我看到马克对业务服务的显示进行了过滤,只显示需要注意的服务。他在页面的底部添加了服务器的名称和它的镜像状态。
你可以在Open Exchange和当前的竞赛中找到我的Production监控器的应用。
https://openexchange.intersystems.com/package/production-monitor
文章
Michael Lei · 八月 18, 2022
Hi 社区的朋友们,大家好!
有时我们需要以编程方式自动将CSV数据从文件或者UR网址L导入到InterSystems IRIS。我们希望创建具有适当数据类型的类并导入数据。
我在Open Exchange上发布了一个模块csvgen,它正是这样做的。
如果你只需要将CSV文件导入IRIS,你可以这么做:
USER>do ##class(community.csvgen).Generate("/usr/data/titanic.csv",,"Data.Titanic")
Class name: Data.Titanic
Header: PassengerId INTEGER,Survived INTEGER,Pclass INTEGER,Name VARCHAR(250),Sex VARCHAR(250),Age INTEGER,SibSp INTEGER,Parch INTEGER,Ticket VARCHAR(250),Fare MONEY,Cabin VARCHAR(250),Embarked VARCHAR(250)
Records imported: 891
USER>
或者你的CSV文件在互联网上, 例如GitHub上面的新冠疫情数据 你可以这样获得数据:
USER>d ##class(community.csvgen).GenerateFromURL("https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_daily_reports/05-29-2020.csv",",","Data.Covid19")
Class name: Data.Covid19
Header: FIPS INTEGER,Admin2 VARCHAR(250),Province_State VARCHAR(250),Country_Region VARCHAR(250),Last_Update DATE,Lat MONEY,Long_ DOUBLE,Confirmed INTEGER,Deaths INTEGER,Recovered INTEGER,Active INTEGER,Combined_Key VARCHAR(250),Incidence_Rate DOUBLE,Case-Fatality_Ratio DOUBLE
Records imported: 3522
USER>
Installation安装
从ZPM安装:
USER>zpm
zpm:USER>install csvgen
csvgen 模块是打包了 CSV2CLASS method .
ObjectiveScript 的profile还不是很理想,欢迎各种建议意见。谢谢!
公告
Claire Zheng · 十一月 22, 2021
大家好!
我们很高兴地向大家介绍我们开发者社区中文版的新版主——段海华( @Duan.Haihua)!
让我们以热烈的掌声欢迎段海华( @Duan.Haihua),以下是他的个人介绍。
段海华,东华医为利润中心副主管
以下是他的个人介绍 @Duan.Haihua:
– 我主持参加了多家医院的互联互通评审工作,并主持了互联互通成熟度测评(Health Information Connectivity Maturity Evaluation, HICME)工具的开发与维护。我有丰富的基于InterSystems产品和项目工作经验,对互联互通测评相关标准有较深刻的理解。
– 通过开发者社区,我希望能够帮助刚开始接触互联互通标准的开发者快速理解相应内容,分享互联互通测评过程中常见问题的解决方案,为开发者对系统标准化改造过程提供参考意见。
热烈欢迎!
感谢段海华的付出( @Duan.Haihua)!我们相信你会成为一名优秀的版主! 👏🏼 热烈欢迎👏!!!
文章
姚 鑫 · 七月 6, 2021
# 第二十九章 从XML架构生成类
Studio提供了一个向导,该向导读取XML模式(从文件或URL),并生成一组支持XML的类,这些类对应于模式中定义的类型。
所有的类都扩展`%XML.Adaptor`。
指定一个包来包含类,以及控制类定义细节的各种选项。
向导还可以作为类方法使用,也可以使用该类方法。
在内部,SOAP向导在读取WSDL文档并生成web客户端或web服务时使用此方法;
注意:使用的任何XML文档的XML声明都应该指明该文档的字符编码,并且文档应该按照声明的方式进行编码。如果未声明字符编码,InterSystems IRIS将使用本书前面的“输入和输出的字符编码”中描述的默认值。如果这些默认值不正确,请修改XML声明,使其指定实际使用的字符集。
# 使用向导
要使用XML架构向导,请执行以下操作:
1. 选择 Tools > Add-Ins > XML Schema Wizard.

2. 在第一个屏幕上,指定要使用的XML模式。
做以下其中一项:
- 对于模式文件Schema File,选择Browse 以选择XML模式文件。
- 对于URL,指定模式的URL。

3. 选择Next。
下一个屏幕显示模式,以便可以验证选择了正确的模式。

4. 可选择以下选项:
- 保留空类Keep Empty Classes,它指定是否保留没有属性的未使用的类。
如果选择此选项,则不会在向导结束时删除此类;
否则,将删除它们。
- “不创建数组属性”Create No Array Properties控制向导是否生成数组属性。
如果选择此选项,向导不会生成数组属性,而是生成另一个表单。
- 为可为空的元素生成XMLNIL属性参数,它控制向导是否为生成的类中适用的属性指定XMLNIL属性参数。
该选项适用于每个对应于用`nillable="true"`指定的XML元素的属性。
如果选择此选项,向导将向属性定义添加`XMLNIL=1`。
否则不添加该参数。
该参数的详细信息请参见将对象投影到XML中的“处理空字符串和空值”。
- 为可为空的元素生成`XMLNILNOOBJECT`属性参数,它控制向导是否为生成的类中适用的属性指定`XMLNILNOOBJECT`属性参数。
该选项适用于每个对应于用`nillable="true"`指定的XML元素的属性。
如果选择此选项,向导将向属性定义添加`XMLNILNOOBJECT=1`。
否则不添加该参数。
该参数的详细信息请参见将对象投影到XML中的“处理空字符串和空值”。
5. 选择Next。
下一个屏幕显示关于要生成的类的选项的一些基本信息。
6. 在这个屏幕上,指定以下选项:
- 如果希望向导编译生成的类,可以选择“编译生成的类”。
- 可选择“添加NAMESPACE类参数”来指定`NAMESPACE`参数。
在本例中,`NAMESPACE`被设置为模式中`targetNamespace`的值。
如果不设置此选项,则不指定`NAMESPACE`。
建议在所有情况下都选择这个选项,因为每个支持XML的类都应该分配给一个XML名称空间。
(但是,为了向后兼容,可以将此选项清除。)
- 如果希望生成的类是持久类,请选择Create persistent classes。然后类扩展`%Persistent`。
可以稍后在向导中针对各个类更改这一点。
- 如果生成持久类,可以选择如何处理由另一个` b`的``组成的`` a。当向导生成一个包含属性`a`的持久类时,该属性有三种可能的形式。
可以将其定义为对象列表、一对多关系(默认)或父子关系。
下表总结了这些选择:
在持久性类中为集合属性使用关系 |向多对关系添加索引 |使用父子关系|生成的属性A的形式
---|---|---|---
selected (default)| not selected| not selected| 无索引的一对多关系
selected (default) |selected| not selected| 在多侧与索引的一对多关系
selected (default) |如果选择Use parent-child relationship,则忽略此选项| selected| 父子关系
not selected| not selected| not selected |List of objects
此外,如果未选择使用父子关系,则可以选择将`%OnDelete`方法添加到类以级联删除。如果选择此选项,当向导生成类定义时,它会在这些类中包含`%OnDelete()`回调方法的实现。生成的`%OnDelete()`方法删除类引用的所有持久对象。如确实选择了使用父子关系,请不要选择此选项;父子关系已经提供了类似的逻辑。
注意:如果修改生成的类,请确保根据需要修改`%OnDelete()`回调方法。
如果生成持久类,向导可以向每个对象类型类添加临时属性,以便可以为对象投影InterSystems IRIS内部标识符。选项如下:
- None-如果选择此选项,向导不会添加此处描述的任何属性。
- Use Id -如果选择此选项,向导将向每个对象类型类添加以下属性:
```
Property %identity As %XML.Id (XMLNAME="_identity", XMLPROJECTION="ATTRIBUTE") [Transient];
```
- Use Oid -如果选择此选项,向导将向每个对象类型类添加以下属性:
```
Property %identity As %XML.Oid (XMLNAME="_identity", XMLPROJECTION="ATTRIBUTE") [Transient];
```
- Use GUID-如果选择此选项,向导将向每个对象类型类添加以下属性:
```
Property %identity As %XML.GUID (XMLNAME="_identity", XMLPROJECTION="ATTRIBUTE") [Transient];
```
底部的表格列出了模式中的XML名称空间。在这里,指定包含该行中显示的XML名称空间的类的包。要执行此操作,请在程序包名字段中为该行指定程序包名。
7. 选择下一步。
8. 在下一个屏幕上,指定以下选项:
- Java Enabled - 如果选择此选项,则每个类都包括一个Java映射。
- Data Population数据填充-如果选择此选项,则除`%XML.Adaptor`外,每个类还继承会`%Populate`。
- SQL Column Order-如果选择此选项,每个属性将为`SqlColumnNumber`关键字指定一个值,以便属性在SQL中的顺序与它们在架构中的顺序相同。
- No Sequence Check-如果选中此选项,向导将生成的类中的`XMLSEQUENCE`参数设置为0。在某些情况下,如果XML文件的元素顺序与XML架构不同,则此选项非常有用。
默认情况下,`XMLSEQUENCE`参数在生成的类中设置为1。这可确保属性以与架构中相同的顺序包含在类定义中。
- XMLIGNORENULL-如果选择此选项,向导会将`XMLIGNORENULL=1`添加到类定义中。否则,它不会添加此参数。
- 将流用于二进制Use Streams for Binary - 如果选择此选项,向导将为`xsd:base64Binary`类型的任何元素生成`%Stream.GlobalBinary`类型的属性。如果清除此选项,则该属性的类型为`%xsd.base64Binary`。
请注意,向导将忽略`xsd:base64Binary`类型的任何属性。
- 在复选框下方,该表列出了向导将生成的类。对于每个类,确保适当地设置了`Extensions/Type`。在此,可以选择以下选项之一:
- 持久类`Persistent` -如果选择此选项,则类是持久性类。
- `Serial`-如果选择此选项,则类为序列类。
- `Registered Object`-如果选择此选项,则类为注册对象类。
所有生成的类还扩展`%XML.Adaptor`。
- 在表的右列中,为每个应编制索引的属性选择索引。
9. 选择Finish(完成)。
然后,向导将生成这些类,并在需要时编译它们。
对于这些类的属性,如果架构中相应元素的名称以下划线(_)开头,则属性名称以百分号(%)开头。
# 以编程方式生成类
XML架构向导也可用作`%XML.Utils.SchemaReader`类的`process()`方法。要使用此方法,请执行以下操作:
1. 创建`%XML.Utils.SchemaReader`的实例。
2. 可以选择设置实例的属性以控制其行为。
3. 可以选择创建InterSystems IRIS多维数组,以包含有关其他设置的信息。
4. 调用实例的`process()`方法:
```
method Process(LocationURL As %String,
Package As %String = "Test",
ByRef Features As %String) as %Status
```
- LocationURL必须是架构的URL或架构文件的名称(包括其完整路径)。
- Package是用于放置生成的类的包的名称。如果不指定程序包,InterSystems IRIS将使用服务名称作为程序包名称。
- Feature是在上一步中选择创建的多维数组。
# 每种XSD类型的默认IRIS数据类型
对于它生成的每个属性,XML架构向导会根据架构中指定的XSD类型自动使用适当的InterSystems IRIS数据类型类。下表列出了XSD类型和相应的InterSystems IRIS数据类型:
用于XML类型的InterSystems IRIS数据类型
源文档中的XSD类型 |生成的IRIS类中的数据类型
---|---
anyURI | %xsd.anyURI
base64Binary | `%xsd.base64Binary`或`%Stream.GlobalBinary`,具体取决于选择的选项。确定每个字符串是否可能超出字符串长度限制,如果可能,则将生成的属性从`%xsd.base64Binary`修改为适当的流类。)
boolean | `%Boolean`
byte | `%xsd.byte`
date | `%Date`
dateTime | `%TimeStamp`
decimal |`%Numeric`
double | `%xsd.double`
float | `%xsd.float`
hexBinary | `%xsd.hexBinary`
int |`%xsd.int`
integer |`%Integer`
long | `%Integer`
negativeInteger | `%xsd.negativeIntege`
nonNegativeInteger | `%xsd.nonNegativeInteger`
nonPositiveInteger |`%xsd.nonPositiveInteger`
positiveInteger | `%xsd.positiveInteger`
short | `%xsd.short`
string | `%String` (注意:责任确定每个字符串是否可能超出字符串长度限制,如果可能,则将生成的类型修改为适当的流类。)
time | `%Time`
unsignedByte | `%xsd.unsignedByte`
unsignedInt |`%xsd.unsignedInt`
unsignedLong | `%xsd.unsignedLong`
unsignedShort | `%xsd.unsignedShort`
no type given | `%String`
# 生成的属性的属性关键字
对于它生成的每个属性,XML架构向导还使用架构中的信息自动设置以下关键字:
- Description
- Required
- ReadOnly (如果相应的元素或属性是用固定属性定义的)
- InitialExpression (该值取自架构中的固定属性)
- Keywords related to relationships
# 生成的属性的参数
对于它生成的每个属性,XML架构向导会根据需要自动设置`XMLNAME`、`XMLPROJECTION`和所有其他与XML相关的参数。它还根据需要设置其他参数,如`MAXVAL`、`MINVAL`和`VALUELIST`。
# 调整为超长字符串生成的类
在极少数情况下,可能需要编辑生成的类来容纳超长的字符串或二进制值,超出字符串长度限制。
对于任何字符串类型,XML架构都不包含任何指示字符串长度的信息。XML架构向导将所有字符串值映射到InterSystems IRIS `%String`类,并将所有`base64Binary`值映射到`%xsd.base64Binary`类。这些选择可能不合适,具体取决于类要承载的数据。
在使用生成的类之前,应该执行以下操作:
- 检查生成的类,找到定义为`%string`或`%xsd.base64Binary`的属性。考虑将在其中使用这些类的上下文,特别是这些属性。
- 如果认为`%string`属性可能需要包含超出字符串长度限制的字符串,请将该属性重新定义为适当的字符流。同样,如果认为`%xsd.base64Binary`属性可能需要包含超过相同限制的字符串,请将该属性重新定义为适当的二进制流。
- 另请注意,对于类型为`%string`、`%xsd.string`和`%BINARY`的属性,默认情况下,`MAXLEN`属性参数为`50`个字符。可能需要指定更高的限制才能进行正确的验证。
(对于`%xsd.base64Binary`类型的属性,`MAXLEN`为`“”`,这意味着不会通过验证检查长度。但是,字符串长度限制确实适用。)
文章
姚 鑫 · 六月 25, 2021
# 第十八章 签署XML文档
本章介绍如何向XML文档添加数字签名。
# 关于数字签名文档
数字签名的XML文档包括一个或多个``元素,每个元素都是数字签名。
每个``元素对文档中的特定元素进行如下签名:
- 每个签名元素都有一个ID属性,该属性等于某个唯一值。例如:
```xml
```
- 一个``元素包含一个``元素,它指向该Id,如下所示:
```xml
```
``元素是由私钥签名的。此元素包括由签名机构签署的X.509证书。如果已签名文档的接收方信任此签名机构,则接收方可以验证证书,并使用包含的公钥验证签名。
注意: IRIS还支持一种变体,其中有签名的元素有一个名为ID的属性,而不是ID。
下面是一个示例,为了便于阅读,添加了空格:
```xml
Persephone MacMillan
1976-02-20
FHwW2U58bztLI4cIE/mp+nsBNZg=
MTha3zLoj8Tg content omitted
MIICnDCCAYQCAWUwDQYJ content omitted
```
要创建数字签名,可以使用类`%XML.Security.Signature`。
这是一个支持xml的类,它的投影是适当名称空间中的有效``元素。
# 创建数字签名XML文档
要创建数字签名的XML文档,请使用`%XML.Writer`为一个或多个适当定义的启用了XML的对象生成输出。
在为对象生成输出之前,必须创建所需的签名并将其写入对象,以便可以将信息写入目标。
## 签名的前提条件
在签署文档之前,必须至少创建一个IRIS凭据集。InterSystems IRIS凭据集是存储在系统管理器数据库中的以下信息集的别名:
- 包含公钥的证书。证书应由文档接收者信任的签名机构签名。
- 关联的私钥, IRIS在需要时使用,但从不发送。签名需要私钥。
- (可选)私钥的密码, IRIS在需要时使用私钥,但从不发送。可以加载私钥,也可以在运行时提供私钥。
## 启用XML的类的要求
启用XML的类必须包括以下内容:
- 投影为ID属性的特性。
- 至少一个类型为`%XML.Security`的属性。投影为``元素的签名。(一个XML文档可以包含多个``元素。)
考虑以下类:
```java
Class XMLEncryption.Simple Extends (%RegisteredObject, %XML.Adaptor)
{
Parameter NAMESPACE = "http://mynamespace";
Parameter XMLNAME = "Person";
Property Name As %String;
Property DOB As %String;
Property PersonId As %String(XMLNAME = "Id", XMLPROJECTION = "ATTRIBUTE");
Property MySig As %XML.Security.Signature(XMLNAME = "Signature");
}
```
## 生成和添加签名
要生成和添加数字签名,请执行以下步骤:
1. 可以选择包含`%soap.inc`包含文件,该文件定义可能需要使用的宏。
2. 创建`%SYS.X509Credentials`的实例在访问相应InterSystems IRIS凭据集。为此,调用`%SYS.X509Credentials`的`GetByAlias()`类方法。
```java
classmethod GetByAlias(alias As %String, pwd As %String) as %SYS.X509Credentials
```
- alias 别名是证书的别名。
- pwd 是私钥密码。仅当关联的私钥已加密并且在加载私钥文件时未加载密码时,才需要私钥密码。
若要运行此方法,必须以该凭据集的`OwnerList`中包含的用户身份登录,否则`OwnerList`必须为空。
3. 在使用给定凭据集创建 `%XML.Security.Signature`的实例。为此,请调用该类的`Createx509()`类方法:
```java
classmethod CreateX509(credentials As %SYS.X509Credentials, signatureOption As %Integer, referenceOption As %Integer) as %XML.Security.Signature
```
- `credentials` 凭据是刚刚创建`%SYS.X509Credentials`的实例。
- `signatureOption`是`$$$SOAPWSIncludeNone`(还有其他选项,但它们不适用于此方案)
- `referenceOption` 指定对符号元素的引用的性质。
这里使用的宏在`%soap.inc`中定义包括文件。
4. 获取ID属性的值,对于此签名将点的ID。此详细信息取决于启用XML对象的定义。
5. 创建`%XML.Security.Reference`的实例,指向该ID。为此,请调用该类的`Create()`类方法:
```java
ClassMethod Create(id As %String, algorithm As %String, prefixList As %String)
```
- `id`是该参考应该指向的ID。
- `algorithm` 算法应该是以下之一:
- `$$$SOAPWSEnvelopedSignature_","_$$$SOAPWSexcc14`n — 使用此版本获取独占规范化。
- `$$$SOAPWSEnvelopedSignature` — 这相当于前面的选项。
- `$$$SOAPWSEnvelopedSignature_","_$$$SOAPWSexcc14n` — 使用此版本进行包容性规范化。
6. 对于签名对象,调用`AddReference()`方法将此引用添加到签名:
```java
Method AddReference(reference As %XML.Security.Reference)
```
7. 更新启用XML的类的相应属性以包含签名。
```java
set object.MySig=signature
```
8. 创建`%XML.Document`的实例,该实例包含序列化为XML的启用了XML的对象。
这是必要的,因为签名必须包括有关签名文档的信息。
注意:本文档不包含空格。
9. 调用签名对象的`SignDocument()`方法:
```java
Method SignDocument(document As %XML.Document) As %Status
```
此方法的参数是刚刚创建的中`%XML.Document`的实例。`SignDocument()`方法使用该实例中的信息更新签名对象。
10. 使用`%XML.Writer`中为对象生成输出。
注意:生成的输出必须包含与签名中使用的文档相同的空格(或不包含空格)。签名包含文档的摘要,如果将编写器中的缩进属性设置为1,则摘要将与文档不匹配。
例如:
**放入到对应的实体类中,有一些属性需要替换**
```java
Method WriteSigned(filename As %String = "")
{
#Include %soap
//创建签名对象
set cred=##class(%SYS.X509Credentials).GetByAlias("servercred")
set parts=$$$SOAPWSIncludeNone
set ref=$$$KeyInfoX509Certificate
set signature=##class(%XML.Security.Signature).CreateX509(cred,parts,ref,.status)
if $$$ISERR(status) {do $system.OBJ.DisplayError(status) quit}
// 获取我们要签名的元素的ID属性;
set refid=$this.PersonId ; 此详细信息取决于类的结构
// 然后在签名对象中创建对该ID的引用
set algorithm=$$$SOAPWSEnvelopedSignature_","_$$$SOAPWSc14n
set reference=##class(%XML.Security.Reference).Create(refid,algorithm)
do signature.AddReference(reference)
//设置MySig属性,以便$this具有我们为其生成输出时所需的所有信息
set $this.MySig=signature ; 此详细信息取决于类的结构
//除了$this之外,我们还需要%XML.Document的一个实例,该实例包含序列化为XML的对象
set document=..GetXMLDoc($this)
//使用序列化的XML对象对文档进行签名,这将更新部分签名
set status=signature.SignDocument(document)
if $$$ISERR(status) {do $system.OBJ.DisplayError(status) quit}
// 写入对象的输出
set writer=##class(%XML.Writer).%New()
if (filename'="") {
set status=writer.OutputToFile(filename)
if $$$ISERR(status) {do $system.OBJ.DisplayError(status) quit}
}
do writer.RootObject($this)
}
```
前面的实例方法使用以下泛型类方法,该方法可以与任何启用了XML的对象一起使用:
```java
ClassMethod GetXMLDoc2(object) As %XML.Document
{
//步骤1-将对象作为XML写入流
set writer=##class(%XML.Writer).%New()
set stream=##class(%GlobalCharacterStream).%New()
set status=writer.OutputToStream(stream)
if $$$ISERR(status) {do $System.Status.DisplayError(status) quit $$$NULLOREF}
set status=writer.RootObject(object)
if $$$ISERR(status) {do $System.Status.DisplayError(status) quit $$$NULLOREF}
//步骤2-从流中提取%XML.Document
set status=##class(%XML.Document).GetDocumentFromStream(stream,.document)
if $$$ISERR(status) {do $System.Status.DisplayError(status) quit $$$NULLOREF}
quit document
}
```
变体:引用中带有`URI=""`的数字签名
作为一种变体,签名的``元素可以具有`URI=""`,这是对包含签名的XML文档根节点的引用。
要通过以下方式创建数字签名:
1. 可以选择包含`%soap.inc`包含文件,该文件定义可能需要使用的宏。
2. 创建`%SYS.X509Credentials`的实例在访问相应InterSystems IRIS凭据集。为此,请调用`%SYS.X509Credentials`的`GetByAlias()`类方法,如前面的步骤所述。
3. 创建使用给定凭据集的`%XML.Security.Signature`的实例。为此,请调用该类的`CreateX509()`类方法,如前面的步骤所述。
4. 按如下方式创建`%XML.Security.X509Data`的实例:
```java
set valuetype=$$$KeyInfoX509SubjectName_","_$$$KeyInfoX509Certificate
set x509data=##class(%XML.Security.X509Data).Create(valuetype,cred)
```
其中,`cred`是`%SYS`的实例。
`x509credentials`在之前创建的新窗口中打开。
这些步骤创建了一个``元素,其中包含一个``元素和一个``元素。
5. 将``元素添加到签名的``元素中,方法如下:
```java
do signature.KeyInfo.KeyInfoClauseList.Insert(x509data)
```
其中签名是`%XML.Security`的实例。
`x509data`是`%XML.Security.X509Data`的实例。
6. 创建`%XML.Security`的实例。
参考如下:
```java
set algorithm=$$$SOAPWSEnvelopedSignature
set reference=##class(%XML.Security.Reference).Create("",algorithm)
```
7. 在步骤6(调用`AddReference()`)中继续上述步骤。
# 验证数字签名
对于收到的任何数字签名文档,都可以验证签名。不需要具有与文档内容匹配的启用XML的类。
## 验证签名的前提条件
若要验证数字签名,必须首先为签名者向InterSystems IRIS提供受信任的证书。如果InterSystems IRIS可以验证签名者的证书链(从签名者自己的证书到来自InterSystems IRIS信任的证书颁发机构(CA)的自签名证书),包括中间证书(如果有),则InterSystems IRIS可以验证签名。
## 验证签名
要验证数字签名的XML文档中的签名,请执行以下操作:
1. 创建`%XML.Reader`的实例并使用它打开文档。
2. 获取阅读器的`Document`属性。这是 `%XML.Document`的一个实例。包含作为`DOM的XML文档的文档
3. 使用阅读器的`correlation()`方法将``元素或元素与类`%XML.Security.Signature`关联起来。
例如:
```java
do reader.Correlate("Signature","%XML.Security.Signature")
```
4. 遍历文档以读取``元素或多个元素。为此,可以使用阅读器的`Next()`方法,该方法通过引用返回一个导入的对象(如果有的话)。
例如:
```java
if 'reader.Next(.isig,.status) {
write !,"Unable to import signature",!
do $system.OBJ.DisplayError(status)
quit
}
```
导入的对象是`%XML.Security.Signature`的实例
5. 调用导入签名的`ValidateDocument()`方法。该方法的参数必须是`%XML`的实例。先前检索到的文档。
```java
set status=isig.ValidateDocument(document)
```
例如:
```java
ClassMethod ValidateDoc(filename As %String)
{
set reader=##class(%XML.Reader).%New()
set status=reader.OpenFile(filename)
if $$$ISERR(status) {do $System.Status.DisplayError(status) quit }
set document=reader.Document
//获取 元素
//假设只有一个签名
do reader.Correlate("Signature","%XML.Security.Signature")
if 'reader.Next(.isig,.status) {
write !,"无法导入签名",!
do $system.OBJ.DisplayError(status)
quit
}
set status=isig.ValidateDocument(document)
if $$$ISERR(status) {do $System.Status.DisplayError(status) quit }
}
```
# 变体:引用ID的数字签名
在典型的情况下,``元素包含一个``元素,该元素指向文档中其他地方的唯一Id。
InterSystems IRIS还支持一种变体,其中``元素指向名为ID(而不是ID)的属性。
在这种变体中,需要额外的工作来签署文档和验证文档。
要对文档进行数字签名,请遵循“创建数字签名XML文档”中的步骤,并进行以下更改:
- 对于支持xml的类,包含一个作为ID属性而不是ID属性投影的属性。
- 在生成和添加签名时,调用`%XML`的`AddIDs()`方法。文档实例。
在获得序列化的XML文档之后,在调用签名对象的`SignDocument()`方法之前,执行此操作。
例如:
```java
//设置MySig属性,使$this在为其生成输出时拥有所需的所有信息
set $this.MySig=signature ; 这个细节取决于类的结构
//除了$this之外,我们还需要%XML的实例 包含序列化为XML的对象的文档
set document=..GetXMLDoc($this)
//***** 当签名引用ID属性时添加步骤 *****
do document.AddIDs()
//使用序列化的XML对象对文档进行签名,这更新了部分签名
set status=signature.SignDocument(document)
if $$$ISERR(status) {do $system.OBJ.DisplayError(status) quit}
```
- 当验证文档时,在调用`Correlate()`之前包含以下步骤:
1. 调用 `%XML.Document`的`AddIDs()`方法。
2. 调用XML阅读器的`Rewind()`方法。
例如:
```
set document=reader.Document
//添加签名引用ID属性时的步骤
do document.AddIDs()
do reader.Rewind()
//获取 元素
do reader.Correlate("Signature","%XML.Security.Signature")
```
文章
姚 鑫 · 四月 13, 2021
# 第二章 定义和构建索引(一)
# 概述
索引是由持久类维护的结构,InterSystems IRIS®数据平台可以使用它来优化查询和其他操作。
可以在表中的字段值或类中的相应属性上定义索引。(还可以在多个字段/属性的组合值上定义索引。)。无论是使用SQL字段和表语法还是类属性语法定义相同的索引,都会创建相同的索引。当定义了某些类型的字段(属性)时,InterSystems IRIS会自动定义索引。可以在存储数据或可以可靠派生数据的任何字段上定义附加索引。InterSystems IRIS提供了几种类型的索引。可以为同一字段(属性)定义多个索引,为不同的目的提供不同类型的索引。
无论是使用SQL字段和表语法,还是使用类属性语法,只要对数据库执行数据插入、更新或删除操作,InterSystems IRIS就会填充和维护索引(默认情况下)。可以覆盖此默认值(通过使用`%NOINDEX`关键字)来快速更改数据,然后作为单独的操作生成或重新生成相应的索引。可以在用数据填充表之前定义索引。还可以为已经填充了数据的表定义索引,然后作为单独的操作填充(构建)索引。
InterSystems IRIS在准备和执行SQL查询时使用可用的索引。**默认情况下,它选择使用哪些索引来优化查询性能。** 可以根据需要覆盖此默认值,以防止对特定查询或所有查询使用一个或多个索引。
## 索引属性
每个索引都有一个唯一的名称。此名称用于数据库管理目的(报告、索引构建、删除索引等)。**与其他SQL实体一样,索引同时具有SQL索引名和相应的索引属性名;这些名称在允许的字符、区分大小写和最大长度方面有所不同。如果使用`SQL CREATE INDEX`命令定义,系统将生成相应的索引属性名称。如果使用持久类定义进行定义,则`SqlName`关键字允许用户指定不同的SQL索引名(SQL映射名称)。** Management Portal SQL界面的Catalog Details显示每个索引的SQL索引名称(SQL映射名称)和相应的索引属性名称(索引名称)。
索引类型由两个索引类关键字`Type`和`Extent`定义。IRIS提供的索引类型包括:
- 标准索引(`Type = index`)——一个持久数组,它将索引值与包含该值的行的 RowID相关联。
**任何没有明确定义为位图索引、位片索引或区段索引的索引都是标准索引。**
- 位图索引(`Type = Bitmap`)——一种特殊的索引,使用一系列位字符串来表示与给定索引值对应的RowID值集;
InterSystems IRIS包括许多位图索引的性能优化。
- 位片索引(`Type = Bitslice`)——一种特殊的索引,能够非常快速地计算某些表达式,例如总和数和范围条件。
某些SQL查询自动使用位片索引。
- 区段索引(`Extent Indices`)——一个区段中所有对象的索引。
有关更多信息,请参阅类定义参考中的区段索引关键字页。
- 范围索引-范围中所有对象的索引。
**表(类)的最大索引数为400。**
## 存储类型和索引
这里描述的索引功能适用于存储在持久化类中的数据。
InterSystems SQL支持使用InterSystems IRIS默认存储结构存储的数据的索引功能:`%Storage.Persistent`(`%Storage.Persistent`映射类)。
InterSystems SQL还支持使用`%Storage.SQL`(`%Storage.SQL`映射的类)存储的数据的索引功能。可以使用函数索引类型为`%Storage.SQL`映射的类定义索引。索引的定义方式与使用默认存储的类中的索引相同,但有以下特殊注意事项:
- 如果`IdKey`函数索引不是系统自动分配的,则该类必须定义`IdKey`函数索引。
- 此功能索引必须定义为索引。
请注意,不应直接调用`%Storage.Persistent`和`%Storage.SQL`类方法。相反,应该使用`%Persistent`类方法和本章中描述的操作调用索引功能。
## 索引全局名称
使用以下两种策略之一生成用于存储索引数据的下标全局:
- `%CLASSPARAMETER USEEXTENTSET=0`使用全局命名策略创建由用户指定的名称、附加的字母代码和索引名称组成的“传统”全局名称。用户可以理解这些全局名称,但它们可能很长,并且效率低于散列的全局名称。
- 如果`USEEXTENTSET=0`且未指定`DEFAULTGLOBAL`,则以下示例将描述生成的全局名称:`Sample.MyTest`持久类将定义名为`^Sample.MyTestD`的master map全局名`^Sample.MyTestD`位图范围索引全局名`^Sample.MyTestI(“$MyTest”)`(或`^Sample.MyTestI(“DDLBEIndex”)`),并且对于定义的索引`NameIDX`,它将定义名为`^Sample.MyTestI(“DDLBEIndex”)`的全局名。请注意,这些全局变量指定的是持久性类名(区分大小写),而不是SQL表名。
- 如果`USEEXTENTSET=0`并指定了`DEFAULTGLOBAL`,则指定的全局名称将替换永久类名。这允许指定一个比持久类名称更短或更清晰的名称。例如,如果`DEFAULTGLOBAL=“MyGlobal”`,则全局变量的名称如下:`^MyGlobalD`和`^MyGlobalI(“NameIDX”)`。
- `%CLASSPARAMETER USEEXTENTSET=1`使用创建哈希全局名称的全局命名策略。这包括对包名进行散列,对类名进行散列,然后追加一个点和一个标识索引的连续整数后缀。这些全局名称对用户来说不太容易理解,但往往更短、效率更高。
整数后缀仅作为索引名的关键字;与索引名和索引类型相关联的字段对整数编号没有影响。例如,`^EW3K.CgZk.1` 是 `Master Map`,`^EW3K.CgZk.2`是位图范围(`Bitmap Extent`),`^EW3K.CgZk.3`是`LastName`字段的已定义标准索引`NameIDX`,`^EW3K.CgZk.4`是已定义索引`WorkIdIDX`。如果删除`NameIDX`,则全局`^EW3K.CgZk.3`也会被删除,从而在整数序列中产生间隙。如果为`LastName`字段定义`LNameIDX`,则会创建全局`^EW3K.CgZk.5`;但是,如果稍后为`FullName`字段创建位图索引`NameIDX`,则全局索引将再次为`^EW3K.CgZk.3`。
- 如果`USEEXTENTSET=1`并且未指定`DEFAULTGLOBAL`,则包名和类名将被散列,如上所述。将追加连续的整数后缀。
- 如果`USEEXTENTSET=1`并指定了`DEFAULTGLOBAL`,则使用`DEFAULTGLOBAL`名称,而不是散列的包名和类名。将追加连续的整数后缀。例如,如果`DEFAULTGLOBAL="MyGlobal"`,则全局变量的名称如下:`^MyGlobal.1`和`^MyGlobal.3`
如果使用`CREATE TABLE`命令定义表,则`USEEXTENTSET`默认为1。因此,默认情况下,`CREATE TABLE`创建散列全局名称。可以使用`%CLASSPARAMETER`关键字以及`USEEXTENTSET`和`DEFAULTGLOBAL`参数更改此默认行为。可以使用`$SYSTEM.SQL.Util.SetOption()`方法在系统范围内更改此默认设置。
```java
SET status=$SYSTEM.SQL.Util.SetOption("DDLUseExtentSet",0,.oldval).
```
如果定义投影到表的持久类,则`USEEXTENTSET`默认为0。因此,默认情况下,使用传统的全局名称。
`DEFAULTGLOBAL`(如果已定义)将作为默认值。如果定义了`ExtentLocation`、`DataLocation`或`IndexLocation`存储关键字,则使用这些值,而不是上述默认值。
可以向`ZWRITE`提供全局名称以显示索引数据。
## Master Map
系统自动为每个表定义一个主图(`Data/Master`)。M`aster Map`不是索引,它是使用其`Map`下标字段直接访问数据本身的`Map`。默认情况下,`Master Map`下标字段是系统定义的`RowID`字段。默认情况下,使用`RowID`字段进行的这种直接数据访问由SQL映射名称(SQL索引名称)`IDKEY`表示。
默认情况下,用户定义的主键不是`IDKEY`。**这是因为使用`RowID`整数查找 `Master Map`总是比使用主键值查找效率更高。** 但是,如果指定主键为`IDKEY`,则主键索引被定义为表的主映射,SQL映射名称为主键SQL索引名。
对于单字段`key/IDKEY,`,主键索引是主映射,但主映射数据访问列仍然是`RowID`。这是因为在记录的唯一主键字段值和其`RowID`值之间存在一对一的匹配,而`RowID`被认为是更高效的查找。对于多字段主键/`IDKEY`,会为`Master Map`指定主键索引名称,并且`Master Map Data Access`列是主键字段。
可以通过Management Portal SQL Catalog Details(管理门户SQL目录详细信息)选项卡查看主图定义。除其他项目外,它还显示存储Master Map数据的全局名称。对于SQL和默认存储,此主映射全局默认为`^Package.classnameD`,并记录命名空间以防止歧义。对于自定义存储,未定义主地图数据存储全局名称;可以使用`DATALOCATIONGLOBAL`类参数指定数据存储全局名称。
对于SQL和默认存储,主映射数据存储在下标全局名为`^package.classnameD` 或 `^hashpackage.hashclass.1`。请注意,全局名指定持久类名,而不是相应的SQL表名,并且全局名区分大小写。可以向`ZWRITE`提供全局名称以显示`Master Map`数据。
**使用`Master Map`访问数据效率很低,尤其是对于大型表。因此,建议用户定义可用于访问`WHERE`条件、联接操作和其他操作中指定的数据字段的索引。**
# 自动定义的索引
定义表时,系统会自动定义某些索引。在为表格定义并在添加或修改表数据时,自动生成以下索引。如果定义:
- 不是`IDKEY`的主键,则系统会生成唯一类型的相应索引。主键索引的名称可以是用户指定的,也可以是从表名派生的。例如,如果定义一个未命名的主键,则相应的索引将命名为`tablenamePKEY#`,其中`#`是每个`UNIQUE`和`PRIMARY KEY`约束的顺序整数。
- 唯一的字段,Intersystems Iris为每个唯一字段生成索引,其中名称T`ableNameUnique#`,其中`#`是每个唯一和主键约束的顺序整数。
- 唯一约束,系统为每个具有指定名称的唯一约束生成索引,为共同定义唯一值的字段编制索引。
- shard key,系统在shard key字段上生成一个索引,命名为`ShardKey`。
可以通过Management Portal SQL Catalog Details选项卡查看这些索引。`CREATE INDEX`命令可用于添加唯一字段约束;D`ROP INDEX`命令可用于删除唯一字段约束。
默认情况下,系统在`RowID`字段上生成`IDKEY`索引。定义身份字段不会生成索引。但是,如果定义标识字段并将该字段作为主键,则InterSystems IRIS将在标识字段上定义`IDKEY`索引并将其作为主键索引。下面的示例显示了这一点:
```sql
CREATE TABLE Sample.MyStudents (
FirstName VARCHAR(12),
LastName VARCHAR(12),
StudentID IDENTITY,
CONSTRAINT StudentPK PRIMARY KEY (StudentID) )
```
同样,如果定义标识字段并为该字段提供唯一约束,则InterSystems IRIS将在标识字段上显式定义`IdKey/Unique`索引。下面的示例显示了这一点:
```sql
CREATE TABLE Sample.MyStudents (
FirstName VARCHAR(12),
LastName VARCHAR(12),
StudentID IDENTITY,
CONSTRAINT StudentU UNIQUE (StudentID) )
```
这些标识索引操作仅在没有明确定义的`idkey`索引时出现,并且表不包含任何数据。
## 位图范围索引
**位图范围索引是表的行的位图索引,而不是针对表的任何指定字段。在位图范围索引中,每个位表示顺序`ROWID`整数值,并且每个位的值指定相应的行是否存在。**
**SQL使用此索引来提高`Count(*)`的性能,返回表中的记录数(行)。**
**一个表最多可以有一个位图区段索引。创建多个位图范围索引导致`SQLCode -400`错误。** 其中 `ERROR #5445: Multiple Extent indices defined:DDLBEIndex.`
所有使用`CREATE TABLE`定义的表都会自动定义位图区段索引。
将此自动生成的索引分配索引名称(索引属性名称)`DDLBEIndex`和SQL MapName (SQL索引名称)`%%DDLBEIndex`。
定义为类的表可以有一个位图区索引,索引名和`$ClassName`的SQL MapName(其中`ClassName`是表的持久化类的名称)。
可以使用带有`BITMAPEXTENT`关键字的`CREATE INDEX`命令将位图区段索引添加到表中,或者重命名自动生成的位图区段索引。
可以通过管理门户SQL Catalog详细选项卡查看表的位图范围索引。虽然表只有一个位图范围索引,但是从另一个表中继承的表在其自身位图范围索引和它从其扩展的表中的位图范围索引中列出。例如,`Sample.employee`表扩展了`Sample.person`表;在目录详细信息映射 `Sample.Employee`列出`$Employee` 和 `$Person Bitmap`范围索引。
在经历许多删除操作的表格中,位图范围索引的存储可以逐渐变得效率较低。可以通过选择表的“目录详细信息”选项卡,“映射”选项和选择重建索引来重建从管理门户中重建位图范围索引。

**%SYS.Maint.Bitmap实用程序方法压缩位图范围索引,以及位图指数和bitslice索**引。
在以下任何情况下,调用`%BuildIndices()`方法都会构建现有的位图范围索引:未指定`%BuildIndices() pIndexList`参数(构建所有定义的索引);`pIndexList`按名称指定位图范围索引;或`pIndexList`指定任何定义的位图索引。
# 定义索引
## 使用类定义定义索引
在Studio中,可以使用新建索引向导或通过编辑类定义的文本将索引定义添加到`%Persistent`类定义。索引在一个或多个索引属性表达式上定义,后跟一个或多个可选索引关键字(可选)。它采用以下形式:
```sql
INDEX index_name ON index_property_expression_list [index_keyword_list];
```
- `index_name`是有效的标识符。
- `index_property_expression_list`是一个或多个以逗号分隔的属性表达式的列表,它们作为索引的基础。
- `index_keyword_list`是一个可选的索引关键字列表,用逗号分隔,用方括号括起来。
用于指定位图或位片索引的索引类型。
也用于指定唯一的、`IdKey`或`PrimaryKey`索引。
(根据定义,`IdKey`或`PrimaryKey`索引也是唯一索引。)
索引关键字的完整列表出现在类定义引用中。
`index_property_expression_list`参数由一个或多个索引属性表达式组成。
索引属性表达式包括:
- 要建立索引的属性的名称。
- 可选(元素)或(键)表达式,提供对集合子值进行索引的方法。
如果`index`属性不是一个集合,用户可以使用`BuildValueArray()`方法生成一个包含键和元素的数组。
- 可选的排序规则表达式。
它包含一个排序规则名称,后面可选地跟着一个或多个以逗号分隔的排序规则参数列表。
不能为惟一索引、`IdKey`索引或`PrimaryKey`索引指定索引排序规则。
唯一索引或`PrimaryKey`索引从正在建立索引的属性(字段)中获取其排序规则。
`IdKey`索引总是精确(`EXACT`)的排序。
例如,下面的类定义定义了两个属性和一个基于它们的索引:
```java
Class MyApp.Student Extends %Persistent [DdlAllowed]
{
Property Name As %String;
Property GPA As %Decimal;
Index NameIDX On Name;
Index GPAIDX On GPA;
}
```
更复杂的索引定义是:
```sql
Index Index1 On (Property1 As SQLUPPER(77), Property2 AS EXACT);
```
### 可以建立索引的属性
唯一可以被索引的属性是:
- 那些存储在数据库中的
- 那些可以从存储的属性可靠地派生出来的
必须使用`SQLComputed`关键字定义可以可靠地派生(并且未存储)的属性; `SQLComputeCode`指定的代码必须是导出属性值的唯一方法,并且无法直接设置属性。
如果可以直接设置一个派生属性的值,比如是一个简单的情况下(non-collection)属性定义为瞬态和不也定义为计算,然后直接设置属性的值将覆盖`SQLComputeCode`中定义的计算和存储的值不能可靠地来自属性;
这种类型的派生属性称为不确定性。(计算的关键字实际上意味着没有分配实例内存。)
一般规则是,只有定义为`calculate`和`SQLComputed`的派生属性才能被索引。
但是,派生集合有一个例外:派生的(`SQLComputed`)集合是暂时的(没有存储)集合,也没有定义为计算的集合(意味着没有实例内存)可以被索引。
**注意:`IdKey`索引所使用的任何属性的值内都不能有连续的一对竖条(`||`),除非该属性是对持久类实例的有效引用。**
这个限制是InterSystems SQL内部机制所要求的。
在`IdKey`属性中使用`||`会导致不可预知的行为。
### 多个属性的索引
可以在两个或多个属性(字段)的组合上定义索引。在类定义中,使用索引定义的`ON`子句指定属性列表,例如:
```java
Class MyApp.Employee Extends %Persistent [DdlAllowed]
{
Property Name As %String;
Property Salary As %Integer;
Property State As %String(MAXLEN=2);
Index MainIDX On(State,Salary);
}
```
如果需要执行使用字段值组合的查询,例如:
```sql
SELECT Name,State,Salary
FROM Employee
ORDER BY State,Salary
```
### 索引排序
唯一索引、`PrimaryKey`索引或`IdKey`索引不能指定排序规则类型。
对于其他类型的索引,索引定义中指定的每个属性都可以有一个排序规则类型。
应用索引时,索引排序类型应与属性(字段)排序类型匹配。
1. 如果索引定义包含为属性显式指定的排序规则,则索引使用该排序规则。
2. 如果索引定义不包括为属性显式指定的排序规则,则索引使用属性定义中显式指定的排序规则。
3. 如果属性定义不包括显式指定的排序规则,则索引使用属性数据类型的默认排序规则。
例如,`Name`属性被定义为字符串,**因此在默认情况下具有`SQLUPPER`排序规则。**
如果在`Name`上定义一个索引,默认情况下,它接受属性的排序规则,索引也将使用`SQLUPPER`定义。
属性排序和索引排序匹配。
但是,如果比较应用不同的排序规则,例如,`WHERE %EXACT(Name)=%EXACT(:invar)`,则此用法中的属性排序规则类型不再与索引排序规则类型匹配。属性比较排序规则类型与索引排序规则类型之间的不匹配可能会导致不使用索引。因此,在这种情况下,可能希望为具有精确(`EXACT`)排序规则的`Name`属性定义索引。如果`JOIN`语句的`ON`子句指定了排序规则类型,例如,`FROM Table1 LEFT JOIN Table2 ON %EXACT(Table1.Name) = %EXACT(Table2.Name)`,此处指定的属性排序类型与索引排序类型不匹配可能导致InterSystems IRIS不使用该索引。
以下规则控制索引和属性之间的排序规则匹配:
- **匹配的排序规则类型总是最大限度地使用索引。**
- **排序规则类型不匹配,其中属性指定为精确的排序规则(如上所示),并且索引有一些其他的排序规则,允许使用索引,但是它的使用不如匹配排序类型有效。**
- **排序规则类型不匹配,其中属性排序规则不准确,属性排序规则不匹配索引排序规则,这将导致不使用索引。**
要在索引定义中显式地为属性指定排序规则,语法如下:
```sql
Index IndexName On PropertyName As CollationName;
```
- `IndexName`是索引的名称
- `PropertyName`是被索引的属性
- `CollationName`是用于索引的排序规则的类型
例如:
```sql
Index NameIDX On Name As Exact;
```
不同的属性可以有不同的排序规则类型。
例如,在下面的例子中,`F1`属性使用`SQLUPPER`排序,而F2使用`EXACT`排序:
```sql
Index Index1 On (F1 As SQLUPPER, F2 As EXACT);
```
**注意:指定为`Unique`、`PrimaryKey`或`IdKey`的索引不能指定索引排序规则。
索引从属性`collations`中获取其`collation`。**
文章
Nicky Zhu · 一月 11, 2021
当我向技术人员介绍InterSystems IRIS时,我一般会先讲其核心是一个多模型DBMS。
我认为这是其主要优势(在DBMS方面)。数据仅存储一次。您只需访问您想用的API。
- 您想要数据的概要?用SQL!
- 您想用一份记录做更多事情?用对象!
- 想要访问或设置一个值,并且您知道键?用Globals!
乍一看挺好的,简明扼要,又传达了信息,但当人们真正开始使用InterSystems IRIS时,问题就来了。类、表和Globals是如何关联的?它们之间有什么关系?数据是如何存储的?
本文我将尝试回答这些问题,并解释这些到底是怎么回事。
## 第一部分 模型偏见
处理数据的人往往对他们使用的模型有偏见。
开发者们把数据视为对象。对他们而言,数据库和表都是通过CRUD(增查改删,最好是基于ORM)交互的盒子,但底层的概念模型都是对象(当然这对于我们大多数使用面向对象编程语言的开发者来说没错)。
而DBA大部分时间都在搞关系型DBMS,他们把数据视为表。对象只是行的封装器。
对于InterSystems IRIS,持久类也是一个表,将数据存储在Global中,因此需要进行一些澄清。
## 第二部分 举例
假设您创建了类Point:
```objectscript
Class try.Point Extends %Persistent [DDLAllowed]
{
Property X;
Property Y;
}
```
您也可以用DDL/SQL创建相同的类:
```
CREATE Table try.Point (
X VARCHAR(50),
Y VARCHAR(50))
```
编译后,新类将自动生成一个存储结构,将原生存储在Global中的数据映射到列(对于面向对象开发者而言,是属性):
```
Storage Default
{
%%CLASSNAME
X
Y
^try.PointD
PointDefaultData
^try.PointD
^try.PointI
^try.PointS
%Library.CacheStorage
}
```
这是怎么回事?
自下向上(加粗文字很重要):
- Type: 生成的存储类型,本例中是持久对象的默认存储
- StreamLocation - 存储流的Global
- IndexLocation - 索引Global
- IdLocation - 存储ID自增计数器的Global
- **DefaultData** - 存储将Global值映射到列/属性的XML元素
- **DataLocation** - 存储数据的Global
现在我们的DefaultData是PointDefaultData,让我们分析下它的结构。本质上Global节点有这样的结构:
- 1 - %%CLASSNAME
- 2 - X
- 3 - Y
所以我们可能期望我们的Global是这样的:
```
^try.PointD(id) = %%CLASSNAME, X, Y
```
但如果我们输出 Global 它会是空的,因为我们没有添加任何数据:
```
zw ^try.PointD
```
让我们添加一个对象:
```
set p = ##class(try.Point).%New()
set p.X = 1
set p.Y = 2
write p.%Save()
```
现在我们的Global变成了这样
```
zw ^try.PointD
^try.PointD=1
^try.PointD(1)=$lb("",1,2)
```
可以看到,我们期望的结构%%CLASSNAME, X, Y是用 $lb("",1,2) 设置的,它对应的是对象的X和Y属性(%%CLASSNAME 是系统属性,忽略)。
我们也可以用SQL添加一行:
```
INSERT INTO try.Point (X, Y) VALUES (3,4)
```
现在我们的Global变成了这样:
```
zw ^try.PointD
^try.PointD=2
^try.PointD(1)=$lb("",1,2)
^try.PointD(2)=$lb("",3,4)
```
所以我们通过对象或SQL添加的数据根据存储定义被存储在Global中(备注:可以通过在PointDefaultData 中替换X和Y来手动修改存储定义,看看新数据会怎样!)。
现在,如果我们想执行SQL查询会怎样?
```
SELECT * FROM try.Point
```
这段sql查询被转换为ObjectScript代码, 遍历^try.PointD,并根据存储定义(其中的 PointDefaultData 部分)填充列。
下面是修改。让我们从表中删除所有数据:
```
DELETE FROM try.Point
```
看看我们的Global变成什么样了:
```
zw ^try.PointD
^try.PointD=2
```
可以看到,只剩下ID计数器,所以新对象/行的ID=3。我们的类和表也继续存在。
但如果我们运行会怎样:
```
DROP TABLE try.Point
```
它会销毁表、类并删除Global。
```
zw ^try.PointD
```
看完这个例子,希望您现在对Global、类和表如何相互集成和互补有了更好的理解。根据实际需要选用正确的API会让开发更快、更敏捷、bug更少。
文章
姚 鑫 · 七月 17, 2021
# 第六章 使用%File对象
如果想要操作文件本身,需要使用`%Library.File`的`%New()`方法实例化`%File`对象。该类还提供了允许使用该文件的实例方法。
注意:本节提供了几个使用`%File`对象的示例,以供说明。
对于简单的文件读写,使用`%Stream.FileCharacter`和%`Stream.FileBinary`。因为它们提供了额外的功能,例如,以正确的模式自动打开文件。
## 创建%File对象的实例
要使用文件,需要使用`%New()`方法实例化表示该文件的%File对象。该文件可能已经存在,也可能不存在于磁盘上。
以下示例在默认目录中为文件`export.xml`实例化一个%File对象。
```java
set fileObj = ##class(%File).%New("export.xml")
```
## 打开和关闭文件
实例化`%File`对象后,需要使用`open()`方法打开文件,以读取或写入该文件:
```java
USER>set status = fileObj.Open()
USER>write status
1
```
使用`Close()`方法关闭文件:
```java
USER>do fileObj.Close()
```
## 检查%File对象的属性
一旦实例化了文件,就可以直接检查文件的属性。
```java
USER>write fileObj.Name
export.xml
USER>write fileObj.Size
2512
USER>write $zdate(fileObj.DateCreated)
11/18/2020
USER>write $zdate(fileObj.DateModified)
11/18/2020
USER>write fileObj.LastModified
2020-11-18 14:24:38
USER>write fileObj.IsOpen
0
```
请注意,`LastModified`是人类可读的时间戳,而不是`$H`格式的日期。
属性“大小Size”、“创建日期DateCreated”、“修改日期DateModified”和“最后修改日期LastModified”是在访问时计算的。为不存在的文件访问这些属性会返回-2,表示找不到该文件。
注意:Windows是目前唯一跟踪实际创建日期的平台。其他平台存储最后一次文件状态更改的日期。
```java
USER>write ##class(%File).Exists("foo.xml")
0
USER>set fooObj = ##class(%File).%New("foo.xml")
USER>write fooObj.Size
-2
```
如果文件已打开,可以通过访问`CanonicalName`属性来查看其规范名称,这是根目录的完整路径。
```java
USER>write fileObj.CanonicalName
USER>set status = fileObj.Open()
USER>write fileObj.IsOpen
1
USER>write fileObj.CanonicalName
c:\intersystems\IRIS\mgr\user\export.xml
```
## 从文件中读取
要读取文件,可以打开文件,然后使用`Read()`方法。
以下示例读取`messages.log`的前`200`个字符。
```java
USER>set messages = ##class(%File).%New(##class(%File).ManagerDirectory() _ "messages.log")
USER>set status = messages.Open("RU")
USER>write status
1
USER>set text = messages.Read(200, .sc)
USER>write text
*** Recovery started at Mon Dec 09 16:42:01 2019
Current default directory: c:\intersystems\IRIS\mgr
Log file directory: .\
WIJ file spec: c:\intersystems\IRIS\mgr\IR
USER>write sc
1
USER>do messages.Close()
```
要从文件中读取整行,请使用`ReadLine()`方法,该方法继承自`%Library.File`的父类`%Library.AbstractStream`。
下面的示例读取`E:\temp\new.txt`的第一行。
```java
/// desc: 读取数据
/// w ##class(Demo.FileDemo).ReadFileData("E:\temp\new.txt")
ClassMethod ReadFileData(str)
{
s fileObj = ##class(%File).%New(str)
s status = fileObj.Open("RU")
w status,!
s text = fileObj.ReadLine(,.sc)
w text,!
w sc,!
d fileObj.Close()
q ""
}
```
## 写入文件
要写入文件,可以打开文件,然后使用`Write()`或`WriteLine()`方法。
以下示例将一行文本写入新文件。
```java
/// desc: 写入数据
/// w ##class(Demo.FileDemo).WriteFileData("E:\temp\new.txt")
ClassMethod WriteFileData(str)
{
s fileObj = ##class(%File).%New(str)
s status = fileObj.Open("RUWSN")
w status,!
s status = fileObj.WriteLine("Writing to a new file.")
w status,!
w fileObj.Size,!
d fileObj.Rewind()
s text = fileObj.ReadLine(,.sc)
w text,!
q ""
}
```
## 倒回文件
从文件读取或写入文件后,希望使用`Rewind()`方法倒回文件,以便可以从文件开头执行操作。
从上一个示例停止的地方开始,`fileObj`现在位于其末尾。倒回文件并再次使用`WriteLine()`会覆盖该文件。
```java
USER>set status = fileObj.Rewind()
USER>write status
1
USER>set status = fileObj.WriteLine("Rewriting the file from the beginning.")
USER>write status
1
USER>write fileObj.Size
40
```
关闭文件并重新打开它也会倒回文件。
```java
USER>do fileObj.Close()
USER>set status = fileObj.Open("RU")
USER>write status
1
USER>set text = fileObj.ReadLine(,.sc)
USER>write sc
1
USER>write text
Rewriting the file from the beginning.
```
## 清除文件
要清除文件,可以打开文件,然后使用`Clear()`方法。这将从文件系统中删除该文件。
以下示例清除默认目录中的`junk.xml`。
```java
USER>write ##class(%File).Exists("junk.xml")
1
USER>set fileObj = ##class(%File).%New("junk.xml")
USER>set status = fileObj.Open()
USER>write status
1
USER>set status = fileObj.Clear()
USER>write status
1
USER>write ##class(%File).Exists("junk.xml")
0
```
文章
Hao Ma · 一月 4, 2023
以下是我们应客户的要求拟定的Caché系统健康检查的建议。InterSystems的工程师们认为其中的项目足以了解客户当前的系统健康状况。
这些项目中有些,比如Buttons, pButtons报告是必须的,其他内容,尤其是问卷部分,越多回答对系统健康的了解也越清楚。InterSystems公司的技术支持中心WRC(World Response Center),在合适的条件下可以协助用户解读健康检查的结果。
在后面的内容中, 我会详细介绍这些检查的项目,比如报告的执行步骤,已经如何简单的发现问题。
检查的内容也适用于IRIS,仅仅是执行的步骤上有细微的区别,后面文章会详细说。
## 健康检查项目
本健康检查只用于Caché系统本身的内容, 不包括Caché上使用的各种应用。
建议用户收集下列两部分数据和资料:
### 系统运行数据
- [ ] 所有Caché实例服务器的网络架构图,包含所有的数据服务器,应用服务器,镜像服务器,灾备服务器。还应该包含网段的划分, 相关的Web服务器,负载均衡设备的部署等情况。以及一切客户认为和Caché工作相关的网络配置的情况。
- [ ] Caché数据库使用的存储设备的信息, 不限于类型,大小,品牌等等任何可以帮助了解存储设备的信息。
- [ ] 所有数据库上一次的完整性检查报告。
- [ ] 所有Caché实例的
- [ ] 系统监控检查报告(Buttons)
- [ ] 24小时系统性能报告(pButtons):
所有关联的系统,比如一个Caché数据服务器以及和它连接的应用服务器(ECP服务器),应该在尽量相同的时间执行24小时pButton测量
- [ ] 一年内或自上次启动后(以其中更长时间为准)的Console日志
- [ ] 导出的日常任务(Task)
- [ ] 导出的后台任务历史列表
- [ ] 系统时钟同步的配置
- [ ] 所有CSP Gateway的配置文件,以及CSP Gateway工作的Apache Web Server, Nginx Web Server,Windows IIS的配置文件。
- [ ] 如果用户使用了外部备份,请提供外部备份的操作步骤及使用的脚本程序。
### 维护工作的问卷
以下问题的回答能帮助InterSystems的工程师更好的了解客户的Caché工作情况,以及更方便的分析上面采集的数据。
- [ ] 请列出近一年内Caché的软硬件变动
- [ ] 是否有测试环境(TestBed), 测试服务器的梳理,配置
- [ ] 请提供Caché的日常维护的情况说明,尽可能提供以下日常维护的方案,执行频率,执行时长等等。包括但不限于:
- [ ] 备份恢复
- 方案,Caché在线备份还是外部备份。如果是Caché在线备份,各种备份类型的安排情况(全备份,增量备份,累计备份)
- 执行频率,执行的时间点
- 各种数据量情况下的执行时长,不如全备份的时长,增量备份的数据量是多少,执行时长是多少等等
- [ ] 数据库完整性检查
- 完整性检查的方案,频率
- 数据库的大小及对应的完整性检查的执行时长
- [ ] 告警通知
- 告警通知发送的方式。(告警通知默认是Console log里严重级别为2,3的条目)
- 告警通知的处理流程
- 告警通知的产生:是否有客户定制的通知消息
- Console Log中出现的严重级别为1的消息(Warning消息)是否被通知,或者是否有任何处理方式
- [ ] 性能测量
- 提供业务活动量在一段时间内的变动模式, 比如一周,一天中业务量的忙时,闲时,以及是否月初活着月底有大的报表生成等等
- 详细列出各种周期性执行的和Caché性能相关的操作的时间点和时长,处了上面提到的备份恢复,数据库完整性检测等,还可以是任意的Caché操作,以及Caché所在的虚拟机,服务器的操作,还可以包括可能影响Caché性能表现的连接的第3方的业务系统监控系统,审计系统的与之有关的操作
- 是否有常规的性能测试方案,包括Caché上的指标测量(pButtons), 以及操作系统的性能指标测量
- 无论以何种形式,是否能提供Caché系统的性能基准。这个性能基准应该以客户的业务活动量做为采样周期,比如以周为单位
- 上述指标是否能提供图表的展示
- [ ] 尽可能的提供近一年中在Caché日常维护中遇到的各种故障及异常的列表。对列表中的每一项,尽量提供详细的描述和信息,包括并不限于:
- 是否报告InterSystems, 如果报告了, WRC号码是多少
- 发生的频率如何?
- 如果已经有解决,解决的方案是什么?
- 如果没有经过人工处理,那么故障恢复的时长平均是多少?
- 维护工程师对故障产生的原因以及造成后果的分析讨论的结果,如果有。
- [ ] 其他内容(可选)
- Caché维护团队的工作分配, 以及相关的外部团队的职责,比如应用实施方,用户的其他IT团队,硬件维护,硬件监控团队等等。
- 对Caché维护最期待的改进,工具的提供等
- 其他任何有关Caché维护工作而上面各项中未涵盖的内容。
文章
Lilian Huang · 七月 20, 2022
本文将讨论 FHIR 中的问卷和问卷反馈(Questionnaire and Questionnaire Response), 从创建表单到上传到服务器以及如何填写它们。
tl;dr :
通过使用该工具链接“ this online tool” ,您可以轻松的开始构建您自己的表单,或者使用现有模版。
通过使用InterSystems 本地FHIR 服务器链接“ this InterSystems local FHIR server” ,您可以轻松的存储您的FHIR资源和问卷。
通过使用此应用程序“this app” ,您可以像医生一样操作,对您的 FHIR 服务器上的每位患者进行问卷调查和回复。
需要注意的是,该应用程序不使用 Content-Type 'application/json+fhir' 进行通信,而只是使用 Content-Type 'application/json' ,所以它不会像我们的本地 InterSystems FHIR 服务器那样工作。
这就是为什么我创建了这个 GitHub 存储库“this GitHub repo”,其中包含应用程序的修改版本,使用 Content-Type 'application/json+fhir',拥有本地 FHIR 服务器和指向问卷生成器工具的链接以及一些解释。
克隆 repo 后,通过执行 docker-compose up -d, npm ci, npm run build 然后出现 npm run start ,您将可以访问该应用程序,通过选择您想要的 FHIR 服务器和您想要工作的患者使用,您将能够填写调查问卷并将其保存到您的服务器中,只需单击 2 次。
End of tl;dr
接下来是 GitHub 的自述文件。
1. 使用本地 fhir 服务器的 FHIR 表单应用程序包括如下
1. App for FHIR forms using a local fhir server(使用本地 fhir 服务器的 FHIR 表单应用程序)
2. Requirements(要求)
2.1. Add Node.js and npm to your path(将 Node.js 和 npm 添加到您的路径)
2.2. Install Dependencies(安装依赖项)
3. Local FHIR server(本地FHIR服务器)
4. Using the app(使用应用程序)
4.1. Build the application(构建应用程序)
4.2. Run the Application(运行应用程序)
5. FHIR form / questionnaire (FHIR表格/问卷)
5.1. Creating your own FHIR form(创建您自己的 FHIR 表单)
5.2. Importing your FHIR form(导入您的 FHIR 表格)
这是一个基于此“this repo”的应用程序,可用于显示, “FHIR” “SDC” “Questionnaire”并收集数据作为 FHIR 问卷反馈的资源。
通过使用docker-compose up -d ,您将可以访问本地 FHIR 服务器“local FHIR server” 然后可以使用该服务器来测试应用程序。
2. 要求
该应用程序依赖于“LHC-Forms “ 渲染,用于显示表单的小部件,它支持部分 FHIR 问卷(版本 STU3 和 R4) 和结构化数据捕获实现指导“Structured Data Capture ImplementationGuide“。此小部件将与依赖项一起安装。
为可以尝试一些示例表单,这个库里在这个下面e2e-test/data/附带了一些表单,
会在构建时自动加载到本地 FHIR 服务器。
2.1. 将 Node.js 和 npm 添加到您的路径
文件 bashrc.lforms-fhir-app 指定了我们正在为开发使用的 Node.js 的版本,下载Node.js的该版本,并将其 bin 目录添加到你的路径。
2.2. 安装依赖项
通过运行此命令,您将能够安装应用程序运行所需的一切。
npm ci
3. 本地FHIR 服务器
如果您没有 FHIR 服务器来试用此应用程序,您可以在 fhir-form 文件夹中启动并使用由 InterSystems 技术支持的本地 FHIR 服务器:
docker-compose up -d
等待一段时间后,您的本地 FHIR 服务器已启动,您可以使用 http://localhost:32783/fhir/r4请注意,此链接已在应用程序中注册。
4. 使用应用程序
要使用该应用程序,您必须构建“build” 并启动“start” 它。您现在可以使用应用程序的菜单访问您选择的任何 FHIR 服务器,但如果您愿意,您可以使用此本地 FHIR 服务器“ local FHIR server”。
4.1. 构建应用程序
npm run build
这将在“dist”目录中创建用于生产的文件,但也会从node_modules 复制一些需要的文件中。
4.2.运行应用程序
npm run start
将启动一个在 8000 端口运行的 http 服务器。
现在在localhost:8000/lforms-fhir-app/浏览到应用程序。
在这里您可以选择要连接的服务器。
如果要使用本地 FHIR 服务器,请启动本地 FHIR 服务器“start the local FHIR server”, 然后在应用程序上选择第一个选项 http://localhost:32783/fhir/r4
5. FHIR表格/问卷
5.1. 创建您自己的 FHIR 表单
通过使用这个在线工具“this online tool”, 您可以轻松地从头开始构建自己的表单或使用现有的表单。
我们建议您导入 e2e-tests/data/R4 文件夹中现有的一个,然后从这里开始了解该工具的工作原理。
5.2. 导入您的FHIR表格
使用该应用程序,您可以轻松导入本地表单并使用上传按钮upload 立即使用它们。
如果您使用的是工具”formbuilder tool “, 如果您有支持Content-Type 'application/json' 的 FHIR 服务器,则可以使用导出按钮将您正在创建的表单直接导出到 fhir 服务器。
如果您的服务器不支持 Content-Type 'application/json' 而只支持 Content-Type 'application/json+fhir' 例如,作为我们的本地 FHIR 服务器“local FHIR server “,您必须将表单导出到文件,然后在应用程序上 ,将文件上传到服务器,因为应用程序以 Content-Type 'application/json+fhir' 进行通信。
原文请点击该链接:https://community.intersystems.com/post/questionnaire-forms-fhir-creation-usage#3-local-fhir-server
文章
姚 鑫 · 二月 21, 2021
# 第四十三章 Caché 变量大全 $ZTIMEZONE 变量
包含格林威治子午线的时区偏移量。
# 大纲
```
$ZTIMEZONE
$ZTZ
```
# 描述
`$ZTIMEZONE`可以通过两种方式使用:
- 返回计算机的本地时区偏移量。
- 为当前进程设置本地时区偏移量。
`$ZTIMEZONE`包含从格林威治子午线偏移的时区(以分钟为单位)。 (格林威治子午线包括整个英国和爱尔兰。)此偏移量表示为-1440到1440范围内的有符号整数。格林威治以西的时区指定为正数;格林威治东部的时区指定为负数。 (时区必须以分钟为单位,因为并非所有时区都以小时为单位。)默认情况下,`$ZTIMEZONE`初始化为计算机操作系统设置的时区。
注意:`$ZTIMEZONE`将本地时间调整为固定的偏移量。它不适应夏令时或其他当地时间的变化。 InterSystems IRIS从基础操作系统获取本地时间,该操作系统将本地时间变体应用于为该计算机配置的位置。因此,使用`$ZTIMEZONE`调整的本地时间将从配置的语言环境中获取其本地时间变化,而不是在`$ZTIMEZONE`中指定的时区。
使用格林威治子午线(`$ZTIMEZONE = 0`)的时区计数来计算UTC时间。它与当地格林威治时间不同。格林威治标准时间(GMT)一词可能令人困惑;格林威治的当地时间与冬季的UTC相同。在夏季,它与UTC的差异为一小时。这是因为应用了称为英国夏令时的本地时间变体。
对于使用`$ZTIMEZONE`的函数和程序,经过的本地时间始终是连续的,但是时间值可能需要季节性调整以与本地时钟时间相对应。
## 设定时区
可以使用`$ZTIMEZONE`设置当前InterSystems IRIS进程使用的时区。设置`$ZTIMEZONE`不会更改默认的InterSystems IRIS时区或计算机的时区设置。
注意:更改`$ZTIMEZONE`特殊变量是为某些特殊情况设计的功能。更改`$ZTIMEZONE`并不是更改InterSystems IRIS用于本地日期/时间操作的时区的一致方法。除非已准备好处理所有导致的不一致的程序,否则不应更改`$ZTIMEZONE`特殊变量。
在某些平台上,更改时区可能比更改`$ZTIMEZONE`特殊变量更好。如果平台具有特定于进程的时区设置(例如POSIX系统上的TZ环境变量),则进行外部系统调用来更改特定于进程的时区可能比更改`$ZTIMEZONE`更好。在操作系统级别更改特定于流程的时区将更改UTC的本地时间偏移,并应用确定何时应用本地时变的相应算法。如果默认系统时区在北半球,而所需的过程时区在南半球,则这尤其重要。更改`$ZTIMEZONE`会将本地时间更改为与UTC偏移的新时区,但是确定何时应用本地时变的算法保持不变。
使用`SET`命令将`$ZTIMEZONE`设置为指定的带符号整数分钟数。数字的前导零和小数部分将被忽略。如果在设置`$ZTIMEZONE`时指定非数字值或无值,则InterSystems IRIS会将`$ZTIMEZONE`设置为0(格林威治子午线)。
例如,北美东部标准时间(EST)在格林威治以西五个小时。因此,要将当前的InterSystems IRIS流程设置为EST,则需要指定300分钟。要指定格林威治以东一小时的时区,请指定–60分钟。要指定格林威治本身,可以指定0分钟。
设置`$ZTIMEZONE`:
- 影响无参数的`$NOW()`当地时间值。它更改了`$NOW()`的时间部分,并且此时间更改也可以更改当前进程的`$NOW()`的日期部分。 `$NOW()`精确地反映了`$ZTIMEZONE`设置,其值未针对本地时变进行调整。
- 影响`$HOROLOG`当地时间值。 `$HOROLOG`从`$ZTIMEZONE`获取其时区值,然后季节性调整本地时间,例如夏令时。因此,`$HOROLOG`始终符合本地时钟时间,但全年的`$HOROLOG`经过时间不是连续的。
- 影响`%SYSTEM.Util`类方法`IsDST()`,`UTCtoLocalWithZTIMEZONE()`和`LocalWithZTIMEZONEtoUTC()`。
- 不会影响`$ZTIMESTAMP`或`$ZHOROLOG`值。
- 不会影响`$ZDATE`,`$ZDATEH`,`$ZDATETIME`,`$ZDATETIMEH`,`$ZTIME`和`$ZTIMEH`函数执行的日期和时间格式转换。
- 不会影响`$NOW(n`)函数。
- 不会影响`%SYSTEM.Process`类的`FixedDate()`类方法,该方法将`$HOROLOG`中的日期设置为固定值。
更改`$ZTIMEZONE`后发生以下异常:
- `$ZDATETIME($HOROLOG,1,7)`通常返回UTC时间,但是如果`$ZTIMEZONE`已更改,它将不返回UTC时间。
- 如果`$ZTIMEZONE`已更改,`$ZDATETIME($HOROLOG,1,5)`将不会返回正确的时区偏移量。
- 如果`$ZTIMEZONE`已更改,则本地时间和UTC时间之间的`$ZDATETIME($HOROLOG,-3)`和`$ZDATETIMEH($ZTIMESTAMP,-3)`转换将不正确。
## 其他时区方法
可以通过调用`TimeZone()`类方法来获取相同的时区信息,如下所示:
```java
DHC-APP>WRITE $SYSTEM.SYS.TimeZone()
-480
```
可以使用tformat值为5或6的`$ZDATETIME`和`$ZDATETIMEH`函数,将本地时间变化作为日期和时间字符串的一部分返回,如以下示例所示
```java
DHC-APP>WRITE !,$ZDATETIME($HOROLOG,1,5)
02/10/2021T18:24:21+08:00
```
该字符串的最后一部分(+08:00)表示系统的本地时间变化设置,以格林威治子午线为单位,以小时和分钟为单位进行偏移。注意,这种变化不一定是时区偏移量。在上述情况下,时区位于格林威治(-5:00)西部5小时,但是本地时区(夏令时)将时区时间偏移一小时到-04:00。设置`$ZTIMEZONE`将更改`$ZDATETIME($HOROLOG,1,5)`返回的当前处理日期和时间,但不会更改系统本地时间变化设置。
## `$ZDATETIMEH`使用时区设置
可以将`$ZDATETIMEH`与`dformat = -3`一起使用,以将协调世界时(UTC)日期和时间值转换为本地时间。该函数将UTC值(`$ZTIMESTAMP`)作为输入。它使用本地时区设置来返回相应的日期和时间,并在适用的情况下应用本地时变(例如夏时制)。
```java
/// d ##class(PHA.TEST.SpecialVariables).ZTIMEZONE()
ClassMethod ZTIMEZONE()
{
SET clock=$HOROLOG
SET stamp=$ZDATETIMEH($ZTIMESTAMP,-3)
WRITE !,"本地/本地日期和时间: ",$ZDATETIME(clock,1,1,2)
WRITE !,"UTC/本地日期和时间: ",$ZDATETIME(stamp,1,1,2)
}
```
```java
DHC-APP>d ##class(PHA.TEST.SpecialVariables).ZTIMEZONE()
本地/本地日期和时间: 02/10/2021 18:31:27.00
UTC/本地日期和时间: 02/10/2021 18:31:27.94
```
## 使用`$ZTIMEZONE`的本地/UTC转换方法
`%SYSTEM.Util`类的两个类方法在本地日期和时间与UTC日期和时间之间进行转换:`UTCtoLocalWithZTIMEZONE()`和`LocalWithZTIMEZONEtoUTC()`。这些方法受`$ZTIMEZONE`更改的影响。
```java
/// d ##class(PHA.TEST.SpecialVariables).ZTIMEZONE1()
ClassMethod ZTIMEZONE1()
{
WRITE $SYSTEM.Util.UTCtoLocalWithZTIMEZONE($ZTIMESTAMP),!
WRITE $HOROLOG,!
WRITE $SYSTEM.Util.LocalWithZTIMEZONEtoUTC($H),!
WRITE $ZTIMESTAMP
}
```
```java
DHC-APP>d ##class(PHA.TEST.SpecialVariables).ZTIMEZONE1()
65785,66819.613
65785,66819
65785,38019
65785,38019.614
```
# 示例
以下示例返回当前时区:
```java
/// d ##class(PHA.TEST.SpecialVariables).ZTIMEZONE2()
ClassMethod ZTIMEZONE2()
{
SET zone=$ZTIMEZONE
IF zone=0 {
WRITE !,"时区是格林威治标准时间"
} ELSEIF zone>0 {
WRITE !,"时区是 ",zone/60," 格林威治以西"
} ELSE {
WRITE !,"时区是 ",(-zone)/60," 格林威治以东"
}
}
```
```java
DHC-APP>d ##class(PHA.TEST.SpecialVariables).ZTIMEZONE2()
时区是 8 格林威治以东
```
以下示例显示了设置时区可以更改日期和时间:
```java
/// d ##class(PHA.TEST.SpecialVariables).ZTIMEZONE3()
ClassMethod ZTIMEZONE3()
{
SET zonesave=$ZTIMEZONE
WRITE !,"当前时区的日期: ",$ZDATE($HOROLOG)
IF $ZTIMEZONE=0 {
SET $ZTIMEZONE=720
} ELSEIF $ZTIMEZONE>0 {
SET $ZTIMEZONE=($ZTIMEZONE-720)
} ELSE {
SET $ZTIMEZONE=($ZTIMEZONE+720)
}
WRITE !,"Date halfway around the world: ",$ZDATE($HOROLOG)
WRITE !,"格林威治天文台的日期: ",$ZDATE($ZTIMESTAMP)
SET $ZTIMEZONE=zonesave
}
```
```java
DHC-APP> d ##class(PHA.TEST.SpecialVariables).ZTIMEZONE3()
当前时区的日期: 02/10/2021
Date halfway around the world: 02/10/2021
格林威治天文台的日期: 02/10/2021
```
以下示例确定本地时间是否与时区时间相同:
```java
/// d ##class(PHA.TEST.SpecialVariables).ZTIMEZONE4()
ClassMethod ZTIMEZONE4()
{
SET localnow=$HOROLOG, stamp=$ZTIMESTAMP
WRITE !,"当地日期和时间: ",$ZDATETIME(localnow,1,1)
SET clocksecs=$PIECE(localnow,",",2)
SET stampsecs=$EXTRACT(stamp,7,11)-($ZTIMEZONE*60)
IF clocksecs=stampsecs {
WRITE !,"没有本地时间变量:"
WRITE !,"本地时间是时区时间"
} ELSE {
IF clocksecs=stampsecs+3600 {
WRITE !,"夏令时变体:"
WRITE !,"从时区时间偏移1小时的本地时间"
} ELSE {
WRITE !,"当地时间和时区时间为"
WRITE !,(clocksecs-stampsecs)/60," 分钟不同"
}
}
QUIT
}
```
```java
DHC-APP>d ##class(PHA.TEST.SpecialVariables).ZTIMEZONE4()
当地日期和时间: 02/10/2021 18:40:21
没有本地时间变量:
本地时间是时区时间
```
文章
姚 鑫 · 六月 16, 2021
# 第九章 将XML导入到对象中
本章介绍如何使用%XML.Reader将XML文档导入到 IRIS对象中。
**注意:使用的任何XML文档的XML声明都应该指明该文档的字符编码,并且文档应该按照声明的方式进行编码。如果未声明字符编码, IRIS将使用前面的“输入和输出的字符编码”中描述的默认值。如果这些默认值不正确,请修改XML声明,使其指定实际使用的字符集。**
还可以使用`%XML.Reader`读取任意XML文档并返回DOM(文档对象模型)。
# 创建XML读取器概述
IRIS提供了一些工具,用于读取XML文档并创建与该文档的元素相对应的启用XML的 IRIS对象的一个或多个实例。基本要求如下:
- 该对象的类定义必须扩展`%XML.Adaptor`。除了少数例外,该对象引用的类还必须扩展`%XML.Adaptor`。
提示:如果相应的XML模式可用,可以使用它来生成类(以及任何支持的类)。
- 要导入XML文档,创建`%XML.Reader`的实例,然后调用该实例的方法。这些方法指定XML源文档,将XML元素与启用XML的类相关联,并将源中的元素读取到对象中。
`%XML.Reader`使用类中的`%XML.Adaptor`提供的方法执行以下操作:
- 它使用InterSystems IRIS SAX接口解析和验证传入的XML文档。验证可以包括DTD或XML架构验证。
- 它确定是否有任何启用了XML的对象与XML文档中包含的元素相关,并在读取文档时创建这些对象的内存中实例。
**请注意,`%XML.Reader`创建的对象实例不存储在数据库中;它们是内存中的对象。如果要将对象存储在数据库中,则必须调用%Save()方法(对于持久对象),或者将相关属性值复制到持久对象并保存它。应用程序还必须决定何时插入新数据和何时更新现有数据;`%XML.Reader`无法进行此区分。**
下面的终端会话显示了一个简单的示例。在这里,我们将XML文件读入一个新对象,检查该对象,然后保存该对象:
```java
/// w ##class(PHA.TEST.Xml).ReadXml()
ClassMethod ReadXml()
{
Set reader = ##class(%XML.Reader).%New()
Set file="E:\temp\samplePerson.xml"
Set status = reader.OpenFile(file)
if $$$ISERR(status) { do $System.Status.DisplayError(status) quit }
Write status,!
Do reader.Correlate("Person","Sample.Person")
Do reader.Next(.object,.status)
if $$$ISERR(status) { do $System.Status.DisplayError(status) quit }
Write object.Name,!
Do object.%Save()
q ""
}
```
此示例使用以下示例XML文件:
```java
Worthington,Jeff R.
1976-11-03
Elm City
27820
Best,Nora A.
Weaver,Dennis T.
```
# 创建导入方法
## 总体方法结构
方法应按以下顺序执行以下部分或全部操作:
1. 创建`%XML.Reader`的实例。
2. 也可以指定此实例的`Format`属性,以指定要导入的文件的格式。
默认情况下,InterSystems IRIS假定XML文件为文字格式。如果文件是SOAP编码格式,则必须指明这一点,以便可以正确读取该文件。
3. 可以选择设置此实例的其他属性。
4. 请使用`%XML.Reader`的以下方法之一
- `OpenFile()` -打开文件。
- `OpenStream()`-打开一个流。
- `OpenString()` -打开一个字符串。
- `OpenURL()` -打开一个URL。
在每种情况下,可以选择性地为该方法指定第二个参数,以覆盖`Format`属性的值。
5. 将这个文件中的一个或多个XML元素名与具有相应结构的支持InterSystems IRIS XML的类关联起来。
有两种方法可以做到这一点:
- 使用`Correlate()`方法,它有以下签名:
```
method Correlate(element As %String,
class As %String,
namespace As %String)
```
其中`element`是XML元素名,class是InterSystems IRIS类名(带包),`namespace`是可选的名称空间`URI`。
如果使用`namespace`参数,则匹配仅限于指定命名空间中的指定元素名。
如果将命名空间参数指定为"",则与`Next()`方法中给出的默认命名空间相匹配。
如果不使用`namespace`参数,则只使用元素名进行匹配。
提示:可以反复调用`Correlate()`方法来关联多个元素。
- 使用`CorrelateRoot()`方法,它有以下签名:
```
method CorrelateRoot(class As %String)
```
其中class是InterSystems IRIS类名(带包)。此方法指定XML文档的根元素与指定的类相关。
6. 按如下方式实例化类实例:
如果使用`Correlate()`,则遍历文件中的相关元素,一次循环一个元素。在循环中,使用Next()方法,该方法具有以下签名:
```
method Next(ByRef oref As %ObjectHandle,
ByRef sc As %Status,
namespace As %String = "") as %Integer
```
其中`OREF`是该方法创建的对象,`sc`是状态,`Namespace`是文件的默认名称空间。
- 如果使用`CorrelateRoot()`,请调用`next()`方法一次,这会导致实例化相关类。
`Next()`方法在到达文件末尾时返回0。如果在此之后再次调用`next()`,则将从文件顶部开始再次循环遍历文件中的对象。(指定的关联仍然有效。)
## 错误检查
**上一节提到的大多数方法都返回状态。应该在每个步骤之后检查状态,并在适当的情况下退出。**
## 基本导入示例
名为`test.xml`的以下XML文件:
```xml
姚
鑫
```
我们首先定义一个启用XML的类`MyApp.Person`,它是`Person`的对象表示:
```java
Class MyApp.Person Extends (%Persistent, %XML.Adaptor)
{
Parameter XMLNAME = "Person";
Property Name As %String;
Storage Default
{
%%CLASSNAME
Name
^MyApp.PersonD
PersonDefaultData
^MyApp.PersonD
^MyApp.PersonI
^MyApp.PersonS
%Library.CacheStorage
}
}
```
要将此文件导入到`MyAppPerson`类的实例中,我们可以编写以下方法:
```java
/// w ##class(PHA.TEST.Xml).ImportXml()
ClassMethod ImportXml()
{
// 创建%XML.Reader的实例
Set reader = ##class(%XML.Reader).%New()
// 开始处理文件
Set status = reader.OpenFile("E:\temp\testPerson.xml")
If $$$ISERR(status) {do $System.Status.DisplayError(status)}
// 将类名与XML元素名相关联
Do reader.Correlate("Person","MyApp.Person")
// 从XML文件读取对象
While (reader.Next(.object,.status)) {
Write object.Name,!
}
// 如果在处理过程中发现错误,则将其显示
If $$$ISERR(status) {do $System.Status.DisplayError(status)}
q ""
}
```
```java
DHC-APP>w ##class(PHA.TEST.Xml).ImportXml()
姚
鑫
```
此方法执行几个任务:
- 它使用InterSystems IRIS `SAX`接口解析输入文件。这包括根据文档的DTD或架构(如果指定)验证文档。
- `Correlate()`方法将类`MyApp`关联起来。
`MyPerson`与XML元素``;
``中的每个子元素都成为`MyPerson`的一个属性。
- 它从输入文件中读取每个``元素,直到没有剩余元素。
- 最后,如果循环因错误而终止,则该错误将显示在当前输出设备上。
如上所述,此示例不将对象存储到数据库。因为`MyPerson`是持久对象,所以可以通过在`While`循环中添加以下行来完成此操作:
```java
/// w ##class(PHA.TEST.Xml).ImportXml()
ClassMethod ImportXml()
{
// 创建%XML.Reader的实例
Set reader = ##class(%XML.Reader).%New()
// 开始处理文件
Set status = reader.OpenFile("E:\temp\testPerson.xml")
If $$$ISERR(status) {do $System.Status.DisplayError(status)}
// 将类名与XML元素名相关联
Do reader.Correlate("Person","MyApp.Person")
// 从XML文件读取对象
While (reader.Next(.object,.status)) {
Write object.Name,!
Set savestatus = object.%Save()
If $$$ISERR(savestatus) {do $System.Status.DisplayError(savestatus)}
}
// 如果在处理过程中发现错误,则将其显示
If $$$ISERR(status) {do $System.Status.DisplayError(status)}
q ""
}
```

## 通过HTTPS URL访问文档
对于`OpenURL()`方法,如果文档位于需要`SSL/TLS`的`URL`,请执行以下操作:
1. 使用管理门户创建包含所需连接详细信息的`SSL/TLS`配置。这是一次性的步骤。
2. 使用`%XML.Reader`时,请设置读取器实例的`SSLConfiguration`属性。对于该值,请指定在上一步中创建的SSL/TLS配置的名称。
或者,当使用`%XML.Reader`,还可以执行以下操作:
1. 创建`%Net.HttpRequest`实例。
2. 将该实例的`SSLConfiguration`属性设置为等于管理门户中创建的`SSL/TLS`配置的配置名称。
3. 使用`%Net.HttpRequest`的实例作为`OpenURL()`的第三个参数。
例如:

```java
Class YX.Config Extends (%Persistent, %XML.Adaptor)
{
Parameter XMLNAME = "update";
Property version As %String;
Property name As %String;
Property url As %String;
}
```
```java
/// 请求http的xml,映射到本地类
/// w ##class(PHA.TEST.Xml).ReadXmlHttp("http://192.168.10.3/dthealth/web/csp/version.xml")
ClassMethod ReadXmlHttp(url)
{
set reader = ##class(%XML.Reader).%New()
set request = ##class(%Net.HttpRequest).%New()
set request.SSLConfiguration="yx"
set status = reader.OpenURL(url,,request)
If $$$ISERR(status) {do $System.Status.DisplayError(status)}
// 将类名与XML元素名相关联
Do reader.Correlate("update","YX.Config")
While (reader.Next(.object,.status)) {
Write object.version,!
Write object.name,!
Write object.url,!
}
q ""
}
```
```java
DHC-APP>w ##class(PHA.TEST.Xml).ReadXmlHttp("http://192.168.10.3/dthealth/web/csp/version.xml")
27
Herb
http://192.168.31.124/dthealth/web/csp/Herb.apk
```
### 在服务器需要身份验证时访问文档
**如果服务器需要身份验证,请创建`%Net.HttpRequest`的实例,并设置该实例的用户名和密码属性。还可以如上所述使用SSL(因此还要设置`SSLConfiguration`属性)。然后使用`%Net.HttpRequest`的实例作为`OpenURL()`的第三个参数,如上例所示。**
问题
kun an · 五月 27, 2021
按照此网址的指示 https://gettingstarted.intersystems.com/language-quickstarts/java-quickstart/ 下载quickstarts-java示例代码 运行xepplaystocktsTask1模块程序提示InterSystems XEP is not supported by the specified server 请问有人知道具体原因吗我的server有什么不对吗。
使用客户端的server manager添加server没有server类型的选项,是因为我使用的cache版本目前不支持吗 不知您测试使用的具体是IRIS哪个版本,以及运行在哪个操作系统上。
还有提到的客户端使用的是哪个IRIS版本。低版本的客户端没法连上高版本的IRIS服务端,Caché客户端更加连不上IRIS的服务器。 您好 感谢解答。我使用的是IRISHealth_Community-2020.1.0.217.1-win_x64 运行在windows10上。我在官网教程上下载的示例代码,其余访问方式都运行正常就xep方式报错, 提示指定的server不支持, 不知道都有什么类型的server,哪些server可以支持 请参考下面资料检查是否符合运行需求以及相关配置是否正确:https://docs.intersystems.com/irisforhealth20201/csp/docbook/Doc.View.cls?KEY=BJAVXEP_intro#BJAVXEP_intro_config
其中的检查包括JDK版本、安装时的选项、服务%Service_CallIn是否打开以及环境变量CLASSPATH 的设置