搜索​​​​

清除过滤器
公告
Michael Lei · 十月 24, 2023

官宣对 CentOS 的支持终止

终止对 CentOS 的支持 自 InterSystems IRIS 2023.3 发布起,CentOS 将不再是受支持的开发平台。 CentOS 一直是一个受支持的开发平台,为开发人员提供了相当于 Red Hat Enterprise Linux (RHEL) 的免费版本,用于 IRIS 开发。您可能知道,Red Hat 对 CentOS 进行了重大更改,CentOS 已成为 RHEL 的“上游”。这意味着它具有 RHEL 中尚未包含的错误和功能,这可能会给在该平台上构建的开发人员带来问题。 我们鼓励使用 CentOS 的开发人员利用 Red Hat 的免费开发人员计划来获得 RHEL 的免费开发许可证。 CentOS 继续支持 IRIS 2023.2(及更早版本)。
文章
Claire Zheng · 五月 21, 2023

【视频文字版】对话:“数据二十条”与FHIR标准

视频文字版。点击查看视频。 CHIMA主任委员:王才有 老卢,你好。疫情三年我们没见,中国发生了很大的变化,你回来会感觉到。特别是在信息化和数字化方面,确实中国的这三年可以说是政策和制度设计上都有了很大的一些调整和进步,特别是去年政府发布了关于数据基本制度的设计方案,我们管它叫“数据二十条”。 这个制度呢,实际上是避开了数据的所有权的问题,如果说我们在数据的所有权和使用权上长期去争论的话,它的价值就很难发挥。所以说中国这次首次提出了一个“三权”,数据“三权分立”的新的战略设计,不再强调数据是谁的,我们强调数据的控制权——就是数据现在在谁手里;数据的开发权——他这些数据可以用来干什么;数据的受益权——他把这数据再分享给别人,再创造二次价值,或者再创造三次价值,这样数据的资源就得到充分的利用。 所以说一方面要对这种数据基本制度的建立,为今后数据资源市场化是开了一道新的大门。中国是一个数据资源的大国,那这些资源怎么利用好,怎么用在生产,怎么用在服务,怎么用在改善我们医院的管理,给我们患者服务,给这个居民服务,这项我想是一个很大的一个制度上的创新。 当然这个制度的创新也给我们提出了很多新的要求:数据安全的要求,个人信息保护的要求,数据标准的要求…… 我国基础制度的形成,将促进数据合规高效流通使用。今后,医疗健康数据将在更大空间交换和共享,这就需要新一代标准体系了,例如FHIR。 我想这方面你也许知道了,在这方面有什么体会呢?咱们可以做做交流。 InterSystems 亚太区总经理卢侠亮(Luciano Brustia): 好的,谢谢。 您所说的这些让我感到很兴奋,谢谢您的发言。因为我同意,当我们在谈论标准、谈论专业术语的时候,每个人都会立即想到FHIR标准。在这方面,InterSystems在全球绝对是领先的。 你可能知道,在过去的这些年里,我们做了许多工作,我们的IRIS数据平台,在全球范围内得到了大范围部署,同时,我们在中国持续地进行本地化创新,构建了中国版的互联互通平台(内嵌FHIR资源仓库),我们称之为“互联互通套件”,用来实现基于FHIR标准的互联互通 。 当谈到专业术语(统一语义)是我们的目标时,FHIR绝对可以作为一个最佳标准选择,我们已经帮助150多家医院通过了国内互联互通标准化成熟度等级评测,其中有近30家医院通过了最高级别的认证(五级乙等),值此(“数据二十条”颁布的)重要时机,我们肯定还会继续加大投入,在这些方面(打通互联互通和FHIR两大生态)开展更多活动。我们现在已经发布了互联互通套件的第三版,目前正在计划进行更多升级。 CHIMA主任委员王才有: 卢总,您刚才讲的系联在中国的一些做法,我很认可。因为系联公司(InterSystems)在中国已经多年,特别是在推进互联互通,推进信息标准的应用方面发挥了很重要的作用。 对这个FHIR来讲,它是针对的移动互联网出现之后,这信息在更大的范围里面交换共享和随时的调用的时候,随机调用的时候,它只有利用这个标准,它才可能实现信息的动态的、语义互操作的这种交换。 而HL7 2.X它面对的是语法层面的交换,语义层面的这个能力呢,它是比较弱的。所以说如果要做得更好,信息在互联网和移动互联网时代,特别是数字,我们说的数字时代,你在更大的空间里面交换和共享信息的时候,同时保持信息理解上的一致性,那我们只有选用FHIR才可能实现得会更好一些。这一方面是技术上的进步,一方面是应用上的需求。 那当然这种技术上的提供和应用上的需求是必须要依赖于工具,依赖于产品。那系联公司这方面的平台支撑和工具支撑上,我想对于促进FHIR在中国的应用还是发挥了很好的作用的。 系联过去确实做得不错,但是我们看到了FHIR的应用还不是特别地普及,原因是多方面的,可能也有一些新的制约和一些新的挑战。我想系联公司在这方面也会进一步地做出努力,推进FHIR在中国的应用。 这方面,卢总下一步你有什么打算呢? InterSystems 亚太区总经理卢侠亮(Luciano Brustia): 是的,当然有。我们已经将FHIR作为我们所有开发部署的核心,包括我们所有医疗平台。首先,我很高兴在今年CHIMA大会上(HL7中国)将发布《FHIR白皮书》 ,明确界定HL7、CDA和FHIR之间的区别。因为医院采用CDA标准,他们拥有一个庞大的临床数据中心,但我们还需要一个额外的步骤来实现FHIR。 为了让大家更容易理解,我经常举这样一个例子:我总是把FHIR的重要性比作普通话的重要性,因为普通话可以作为人们互相交流的一种共同语言。我也总是喜欢这样说,我看到了一张图片,上面是一匹带着黑色条纹的白马,大家都明白它是什么。你也可以说,你看到了一张图片,上面是一匹带着白色条纹的黑马,大家也都明白它是什么。FHIR不会告诉你这是一匹白马还是一匹黑马,而是会说这是一匹“斑马”——只用一个词,就确切指出了图片内容的唯一标识,以及我们在谈论的是什么。这是深层次的主要区别。 我很高兴《FHIR白皮书》即将在CHIMA上发布,这将有助于理解这一点。InterSystems作为FHIR标准在全球范围的推广者,已经在美国和亚太地区拥有众多成功案例,并且帮助越来越多的国家部署并遵循这种标准,为当地居民提供更多价值,我们很高兴能在中国做同样的事情。 CHIMA主任委员王才有: 刚才没想到您作为一个总裁,对技术细节了解得这么清楚。确实我们要把技术细节搞清楚,把关键的概念搞清楚,这样才能使我们的行动走到正确的轨道上来。对FHIR,它到底是适用于什么场景,什么场景下应该用哪些标准解决什么问题,这我觉得是一个非常重要的一个考虑。任何一个企业、任何一个用户都应该考虑你选用标准的适宜性,我们不能说哪个标准好,哪个标准不好。所以说我们CHIMA,刚才老卢也提到了,我们要在下一次CHIMA大会上发一个白皮书,而这个白皮书实际上就是我们对这些标准的基本概念,它的用途,在什么场景下应用,我们要做一个比较清晰的这种引导,让大家在选择标准和使用标准上,使标准能真正解决自己互联互通上的问题。 那我们CHIMA跟系联一样,也是在推进标准的应用和标准的采纳,实现我们互联互通的目标。前年,我们也组织了专家,在中国的医院编了一个中国医院里边的关于FHIR的应用的一些案例的分享,我们是请的北京友谊医院的专家来共同承担这个课题,也非常感谢系联的专家对这个项目给予了很好的支持。 卢总,我们系联今后在这个信息互联互通和促进,我们叫数字时代的健康场景下,我们公司还有什么好的想法和建议? InterSystems 亚太区总经理卢侠亮(Luciano Brustia):谢谢。我认为此时来到中国,这是一个非常令人兴奋的时机,因为我们能够带来许多在世界各地获得的经验。我们在日本群马大学建立了日本首个FHIR数据库;我们正在与印度尼西亚的一家超大型医疗集团合作,该医疗集团拥有40多家医院,(借助InterSystems技术)采用FHIR标准,将他们的数据汇聚起来,并在此基础上进行分析。毫无疑问,InterSystems一直都在采用、推广FHIR标准,在FHIR成为互操作性标准之前就是如此,而且我们将带来更多的创新,以确保中国始终与国际最前沿的标准接轨,因为最终真正重要的是,我们这样做是为了确保所有中国居民的利益,而且我认为这对像你我这样从事IT行业,特别是从事医疗IT领域的从业者来说,才是最重要的。我们不仅仅是在做生意,更多的是在提供价值。在人口不断增长的情况下助力全民健康水平提升,中国居民的寿命越来越长,这是一件非常好的事情。但也带来了很多问题,特别是人口老龄化的问题,建设高质量的数据库,采用先进的标准,可以有效地预防更多的慢性疾病发生,我认为这是非常有价值的,也让我们所做的事情更有意义,以上是我的看法,非常感谢。CHIMA主任委员王才有:非常高兴看您为中国医疗信息化做出的贡献。系联公司把国际上的一些标准引用到中国,我们也看到中国自身医疗信息化发展也是非常地快,特别是中国具有一个独特的优势——就是数据资源丰富、应用场景非常之多,所以中国的经验呢,我想也在逐渐地积累和形成。所以说我认为系联公司将来的更重要的任务,把中国的经验引向世界,为全球的人民的健康共同做出贡献。InterSystems 亚太区总经理卢侠亮(Luciano Brustia):是的,当然。我的意思是,就像我一直在说的,我很荣幸来到中国,这已经是我在中国的第15个年头了,而系联来到中国已经不止25年了,我们打算再呆上另一个20年、25年……如果我还能再活二十年,(我会)继续留在这里,继续为各位和所有聪明睿智的中国居民服务。毫无疑问,InterSystems一直致力于把我们的先进技术带到中国,助力中国本土企业做大做强,当然,我们也会将在中国获得的经验,作为一种资源共享给世界其他地方。所以我真的很高兴再次回到中国,我认为这是最好的时候,非常感谢大家!
文章
Michael Lei · 五月 17, 2021

iris-fhir-portal 概述

我创建了 iris-fhir-portal 来参加当前竞赛 **[InterSystems IRIS for Health FHIR](https://community.intersystems.com/post/welcome-intersystems-iris-health-fhir-contest-developers),**本篇快速概述旨在介绍我的应用程序提供的功能。 iris-fhir-portal 的目标是说明使用 IRIS for Health 中的 FHIR 功能创建患者图表并让用户拥有自己的数据有多么简单。 ## 功能 ### 患者列表 在左侧面板上,有一个患者列表,顶部是一个筛选栏。 ![](/sites/default/files/inline/images/images/search.png) ### 患者详细信息 ![](/sites/default/files/inline/images/images/formloaded_badges.png) 表格提供以下信息: * FHIR 患者 ID * SSN(社会保障号码) * 名字 * 姓氏 * 出生日期 * 性别 * 地址 * 城市 * 州/省 * 国家/地区 在患者详细信息表格后面,是一个包含四个信息块的可折叠项。 提供这些信息的 FHIR 资源为: * AllergyIntolerance * Observation * 类别:vital-signs * 类别:laboratory * Immunization 右侧的徽章显示每个项目的结果总数。 附注:我在上一篇文章中写到了如何从 FHIR 资源获取所有这些信息。 https://community.intersystems.com/post/my-experience-working-fhir 例如,以下是实验室数据结果的屏幕截图: ![](/sites/default/files/inline/images/images/accordionresults.png) 为了以透明的方式处理患者数据,在页面末尾有一个模版,其中包含 FHIR 资源提供的所有信息。 ![](/sites/default/files/inline/images/images/fhir_resourcedata.png) ####   #### 您可以在这里试用本应用程序!   如果您喜欢本应用程序,并认为我值得您投票,请为 iris-fhir-portal 投一票!  ![laugh](https://community.intersystems.com/sites/all/libraries/ckeditor/plugins/smiley/images/teeth_smile.png "laugh") ****  
文章
Michael Lei · 三月 21, 2022

消息转换即服务--轻松实现从HL7v2 转换为 FHIR !

# IRIS Healthtoolkit Service 软件即服务 [![Video](https://raw.githubusercontent.com/grongierisc/iris-healthtoolkit-service/main/misc/images/Cover.png)](https://youtu.be/lr2B7zSFkds "Video") 轻松实现HL7v2 转 FHIR, CDA 转 FHIR, FHIR 转 HL7v2 即服务. 这个项目的目标是提供 REST API 可以轻松转化不同的医疗行业格式。 在Rest body 发布需要的格式,在答案中获得新的格式。 * ![emoji](https://community.intersystems.com/sites/all/libraries/ckeditor/plugins/smiley/images/tongue_smile.png) InterSystems 消息转换公有云服务: https://aws.amazon.com/marketplace/pp/prodview-q7ryewpz75cq2 ![emoji](https://community.intersystems.com/sites/all/libraries/ckeditor/plugins/smiley/images/tongue_smile.png) * ![emoji](https://community.intersystems.com/sites/all/libraries/ckeditor/plugins/smiley/images/devil_smile.png) 视频(油管) : https://youtu.be/lr2B7zSFkds ![emoji](https://community.intersystems.com/sites/all/libraries/ckeditor/plugins/smiley/images/devil_smile.png) ## 安装 克隆这个 repository ``` git clone https://github.com/grongierisc/iris-healthtoolkit-service.git ``` Docker ``` docker-compose up --build -d ``` ## 使用 * 访问 : http://localhost:32783/swagger-ui/index.html ## API 细节 * HL7 转 FHIR ``` POST http://localhost:32783/api/hl7/fhir ``` * FHIR 转 HL7 ADT ``` POST http://localhost:32783/api/fhir/hl7/adt ``` * FHIR 转 HL7 ORU ``` POST http://localhost:32783/api/fhir/hl7/oru ``` * FHIR 转 HL7 vxu ``` POST http://localhost:32783/api/fhir/hl7/vxu ``` * CDA 转 FHIR ``` POST http://localhost:32783/api/cda/fhir ``` * FHIR repo ``` GET http://localhost:32783/api/fhir/metadata ``` ## 支持的 HL7 inbound 格式 : * ADT_A01, ADT_A02, ADT_A03, ADT_A04, ADT_A05, ADT_A06, ADT_A07, ADT_A08, ADT_A09, ADT_A10, ADT_A11, ADT_A12, ADT_A13, ADT_A17, ADT_A18, ADT_A23, ADT_A25, ADT_A27, ADT_A28, ADT_A29, ADT_A30, ADT_A31, ADT_A34, ADT_A36, ADT_A39, ADT_A40, ADT_A41, ADT_A45, ADT_A47, ADT_A49, ADT_A50, ADT_A51, ADT_A60 * BAR_P12 * MDM_T02, MDM_T04, MDM_T08, MDM_T11 * OMP_O09 * ORM_O01 * ORU_R01 * PPR_PC1, PPR_PC2, PPR_PC3 * RDE_O11 * SIU_S12, SIU_S13, SIU_S14, SIU_S15, SIU_S16, SIU_S17, SIU_S26 * VXU_V04 ## 如何工作 这个项目基于SDA模型工作. SDA (Summary Document Architecture) 是InterSystems系联公司的临床数据格式。 SDA FHIR 的对应关系在 [这里](https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.cls?KEY=HXFHIR_transforms), CDA -> SDA 的对应在[这里](https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=HXCDA). ![gif sda pivot](https://raw.githubusercontent.com/grongierisc/iris-healthtoolkit-service/main/misc/images/Gif_SDA_Pivot.gif)
文章
Muhammad Waseem · 八月 11, 2022

FHIR 客户端使用嵌入式 python 连接任何打开的 FHIR 服务器

嗨社区, 这篇文章公开介绍我的 iris-fhir-client 客户端应用。 iris-fhir-client 可以可以借助嵌入式 python 连接到任何开放的 FHIR 服务器 fhirpy 图书馆. 通过终端和使用 CSP Web 应用程序获取资源信息。 查看和激活注册服务器 连接到 IRIS 终端 docker-compose exec iris iris session iris 应用程序将默认注册 InterSystems FHIR Accelerator Service 和 SmartHealthIT Open FHIR Server,两者都可以使用。使用以下命令列出已注册的服务器 do ##class(dc.FhirClient).ServerList() InterSystems FHIR 加速器服务处于激活状态。 为了选择 SmartHealthIT 打开 FHIR 服务器,通过传递服务器 ID 使用 dc.FhirClient 类的 SetFhirServer 函数 do ##class(dc.FhirClient).SetFhirServer(2) 注册的 FHIR 服务器 要注册新服务器,请使用 dc.FhirClient 类的 RegisterServer() 函数class(dc.FhirClient).RegsterServer("Server Name","Endpoint","ApiKey"[optional],"EndpointOAuth"[optional] do ##class(dc.FhirClient).RegisterServer("INTERSYSTEMS FHIR Server","http://localhost:52773/csp/healthshare/samples/fhir/r4/","","") 从 FHIR 服务器获取资源 要检索当前服务器的所有资源,请使用 dc.FhirClient 类的 ListResources() 方法 do ##class(dc.FhirClient).ListResources() 为了显示任何资源的记录数,通过传递 dc.FhirClient 的 Resource 使用 CountResource() 方法下面的命令将从激活的 FHIR 服务器获取患者资源计数器 set count = ##class(dc.FhirClient).CountResource("Patient") write count 要检索所有创建的资源及其计数,只需将 1 传递给 ListResource() 函数 do ##class(dc.FhirClient).ListResources(1) 要获取资源的详细信息,请通过传递 dc.FhirClient 类的 Resource 使用 GetResource()下面的命令将从激活的FHIR 服务器中检索所有患者 do ##class(dc.FhirClient).GetResource("Patient") 下面的命令将从激活的 FHIR 服务器中检索所有观察结果 do ##class(dc.FhirClient).GetResource("Observation") 从 FHIR 服务器获取特定患者的资源 下面的命令将从激活的 FHIR 服务器中检索针对 Patinet ID 1 的观察详细信息 do ##class(dc.FhirClient).GetPatientResources("Observation","1") 从 CSP Web 应用程序查看 FHIR 服务器信息 导航 http://localhost:55037/csp/fhirclient/index.csp索引页面将显示激活的服务器中患者、观察、从业者和就诊次数以及患者和注册服务器的详细信息 索引页面将显示 FHIR 服务器列表,其中选择了激活的服务器。 从列表中选择其他服务器以查看所选服务器的详细信息 将鼠标悬停到患者 ID 并选择以获取患者资源的详细信息 此页面将显示一些患者资源的数量以及患者观察的详细信息 谢谢
文章
Jingwei Wang · 五月 5, 2023

IRIS SQL 编辑器和 IRIS JAVA 连接

WIN SQL是大多数用户使用的普通编辑器。但是我们不能使用winsql下载大量数据。所以我写了一个教程如何连接一个新的基于 Java 的编辑器,叫做 Squirrel SQL,它可以很容易地下载或导出 excel 或任何其他格式的数据。我还包括一个 Java JCBC 连接程序来连接 IRIS 数据库,尤其是镜像/故障转移服务器。 基于 SQL Java 的编辑器导出大量数据和用于 IRIS 连接的 Java JDBC 程序 基于 SQL Java 的编辑器导出大量数据 WinSql 是通常用于从 Iris 数据库中提取数据的编辑器,但是,如果没有许可的 winsql,则无法导出大量数据。 解决方案是使用基于 java 的编辑器,称为 Squirrel SQL。这是一个基于 java 的编辑器,您可以在从 IRIS 数据库执行 fetch 从编辑器中导出大量数据。这是用 Java 构建的开源 SQL 客户端,它使用 JDBC 连接到 IRIS 数据库。 Squirrel SQL 的特点 Java 19 兼容性 多个插入符/光标编辑 全局首选项和新会话属性搜索 Saved Sessions 的多项改进(用于保存和恢复 Session 的所有 SQL 编辑器的特性) 可配置的鼠标右键菜单 重新设计的添加/编辑 JDBC 驱动程序对话框 安装 Squirrel SQL 的步骤 Squirrel SQL 可以从 Squirrel 官网下载https://squirrel-sql.sourceforge.io/ 连接 IRIS 数据库的步骤 向 Squirrel Sql 添加驱动程序 点击“+”图标创建一个新的驱动程序,如下图所示 在“添加驱动程序对话框”中,选择“额外类路径”并单击“添加”,为“Intersystems-jdbc-3.2.0.jar”(jdbc 驱动程序jar 文件)添加一个新条目,如下所示。如果您在本地计算机的 C 盘上安装了 IRIS,这将是基于 IRIS 版本的正常路径 C:\InterSystems\IRISHealth2\dev\java\lib\JDK18\intersystems-jdbc-3.2.0.jar。 如下图所示, 输入驱动程序的名称“Intersystems IRIS”(选择任何有意义的名称) 输入示例 URL 作为 jdbc:IRIS://<host>:<port>/<database> 网站 URL 是可选的。 单击右侧的“List Drivers”按钮并选择“com.intersystems.jdbc.IRISDriver”,如下图所示。 单击“确定”保存驱动程序条目。现在您可以在驱动程序下的左侧菜单栏中看到驱动程序。 添加基于驱动程序的别名(连接) 选择squirrel sql左侧的“别名”选项卡,点击“+”添加新别名,如下图。 在“添加别名”窗口中,为别名输入一个有意义的名称。 从下拉菜单中选择我们新创建的 IRIS 驱动程序。选择驱动程序后,URL 格式将填充为新创建的驱动程序配置。通过添加正确的主机名或 IP 地址、端口号和数据库命名空间来编辑 URL。 例如:jdbc:IRIS://00.00.00.00.00:12345/TEST-TRAK 输入具有 SQL 权限的 IRIS 数据库的用户名和密码 单击测试按钮并验证连接是否成功。 单击“确定”保存新别名 连接到 IRIS 数据库 双击新创建的别名连接到数据库,squirrel 编辑器将打开,您可以尝试使用 sql 查询。 用于编写程序的 IRIS 数据库的 JDBC 连接 import java.sql.*; import com.intersystems.jdbc.*; import java.util.logging.*; import java.io.IOException; import java.util.*; public class Extract { public static Connection TrakCache () throws Exception { IRISDataSource ds = new IRISDataSource(); Connection conn = null ; ds.setURL( "jdbc:IRIS://1.12.333.444:12345/NAMESPACE-TRAK" ); ds.setUser( "username" ); ds.setPassword( "Password" ); try { conn = ds.getConnection(); } catch (Exception e) { System.out.println( "catch" +conn); //You can write another connection here if automatically fail over to another server. } return conn; } }
文章
姚 鑫 · 四月 7, 2023

第二十一章 配置镜像

# 第二十一章 配置镜像 本章提供了镜像和镜像成员的设置、配置和管理的相关信息和步骤。 # 镜像的自动部署方法 本章提供了使用管理门户创建镜像和将现有实例配置为成员的过程。 IRIS Data平台还提供了几种自动部署镜像的方法,这些镜像在部署后完全可运行。 ## 使用云管理器(ICM)部署镜像 ISC建议使用InterSystems Cloud Manager(ICM)部署 IRIS,包括镜像配置。通过将纯文本声明性配置文件、简单的命令行界面和Docker Containers中的 IRIS部署相结合,ICM为提供了一种简单、直观的方式来配置云或虚拟基础架构,并在该基础架构上部署所需的InterSystems IRIS体系结构以及其他服务。ICM可以显著简化部署流程,尤其是对于复杂的水平群集配置。 除了部署独立的镜像实例外,ICM还可以部署具有镜像数据服务器的分布式缓存集群和具有镜像数据节点的分片集群。 ## 使用 Kubernetes运算符(IKO)部署镜像 KUBERNETES一个开源的编排引擎,用于自动部署、扩展和管理容器化的工作负载和服务。可以定义想要部署的容器化服务以及希望它们遵循的策略;Kubernetes以尽可能高效的方式透明地提供所需的资源,在部署偏离规范时修复或恢复部署,并自动或按需扩展。InterSystems Kubernetes运算符(ICO)使用IrisCluster定制资源扩展了Kubernetes API,该资源可以作为InterSystems IRIS分片集群、分布式缓存集群或独立实例部署在任何Kubernetes平台上,所有这些都是可选的镜像。 在Kubernetes下部署 IRIS并不需要ICO,但它极大地简化了过程,并向Kubernetes添加了 IRIS特定的集群管理功能,支持将节点添加到集群等任务,否则您必须通过直接与实例交互来手动完成这些任务。 ## 使用配置合并部署镜像 配置合并功能在Linux和UNIX®系统上可用,它允许通过将所需的声明性配置合并文件应用于部署中的每个实例,来改变从相同映像部署的InterSystems IRIS容器的配置,或从相同工具包安装的本地实例的配置。此合并文件也可在重新启动现有实例时应用,它更新实例的配置参数文件(CPF),其中包含其大部分配置设置;这些设置在每次启动时从CPF中读取,包括部署实例后的第一个设置。当在部署期间应用配置合并时,实际上是用自己的更新版本替换了随实例提供的默认CPF。 使用配置合并,可以部署(或从现有实例配置)一个或多个镜像,包括它们的镜像数据库,方法是将单独的合并文件应用到不同的镜像角色,按顺序部署或配置第一个故障切换成员,然后是第二个故障切换成员,然后是灾难恢复异步成员。(部署或配置镜像后,必须手动将报告异步成员添加到镜像中。)。如果部署主机的名称与特定模式匹配,还可以自动部署多个故障转移对,或为现有主映像部署多个备份。在这种情况下,您可以将单个合并文件同时用于主备份和备份,然后在自动部署故障切换对后,对任何灾难恢复异步成员使用单独的合并文件。 还可以使用配置合并功能来部署具有镜像数据服务器的分布式缓存集群和具有镜像数据节点的分片集群。
文章
姚 鑫 · 五月 18, 2021

第四章 使用Setup和tear Down方法执行测试

# 第四章 使用Setup和tear Down方法执行测试 # 示例:使用Setup和tear Down方法执行测试 以通常的方式执行新的单元测试。 1. 在一直在使用的命名空间中打开终端。 2. 将`^UnitTestRoot`的值设置为包含测试类的目录的父级: ```java USER> Set ^UnitTestRoot="c:\unittests" ``` 3. 使用`%UnitTest.Manager`执行测试: ```jav USER> Do ##class(%UnitTest.Manager).RunTest("mytests") ``` 4. IRIS加载测试类、编译类、执行测试并向终端发送报告。 ```java =============================================================================== Directory: C:\unittests\mytests\cls\MyPackage\ =============================================================================== mytests\cls\MyPackage begins ... Load of directory started on 01/09/2018 14:36:57 '*.xml;*.XML;*.cls;*.mac;*.int;*.inc;*.CLS;*.MAC;*.INT;*.INC' Loading file C:\unittests\mytests\cls\MyPackage\Tests.xml as xml Imported class: MyPackage.Tests Compilation started on 01/09/2018 15:44:01 with qualifiers '' Compiling class MyPackage.Tests Compiling routine MyPackage.Tests.1 Compilation finished successfully in 0.033s. Load finished successfully. MyPackage.Tests begins ... TestAdd() begins ... AssertEquals:Test Add(2,2)=4 (passed) AssertNotEquals:Test Add(2,2)'=5 (passed) LogMessage:Duration of execution: .000073 sec. TestAdd passed TestEditContact() begins ... AssertStatusNotOK:ContactType = Friend (passed) AssertStatusOK:ContactType = Personal (passed) LogMessage:Duration of execution: .001227 sec. TestEditContact passed MyPackage.Tests passed mytests\cls\MyPackage passed Use the following URL to view the result: http://10.0.75.1:52773/csp/sys/%25UnitTest.Portal.Indices.cls?Index=10&$NAMESPACE=USER All PASSED ``` # 执行测试的选项:测试规格和限定符 通常,可以使用以下形式的命令执行`RunTest`: ```java Do ##class(%UnitTest.Manager).RunTest("testspec","qualifiers") ``` `Testspec`参数确定要运行哪些测试以及在哪里可以找到它们。`Testspec`的一般形式是`testSuite:testcase:testmethod`,其中 - `testsuite`(必填)。包含导出的测试类的文件目录。该目录必须是名为`^UnitTestRoot`的目录的子目录。默认情况下,测试管理器执行此目录及其子目录中包含的所有文件中的所有测试。 - `testcase`测试用例(可选)。选择包含要执行的测试方法的单个类。格式为`PackageName.ClassName`。如果存在,则测试管理器仅执行命名类中的测试。 - `testmethod`(可选)。挑选由测试用例指示的测试类的一个方法来执行。 限定符参数指定用于运行测试的各种选项。正如我们已经看到的,当想要从`.cls`文件加载测试时,可以使用`“/loadudl”`限定符。还可以使用限定符来控制测试类在执行后是否从服务器中删除,是否应该从这些外部文件加载测试,或者系统是否应该在测试失败后进入调试模式,等等。限定符参数是一个可选的命令行参数字符串,用于打开或关闭某些测试管理器行为。例如,`“/NoLoad/DEBUG”`告诉管理器不要从目录加载任何测试,也就是说,使用当前在InterSystems IRIS中的测试,并在调试模式下运行测试。这些限定符就是所谓的可否定布尔值。例如,这意味着`“/NoLoad”`等同于`“/Load=0”`。 限定符 | 含义 ---|--- `/load` (default) |从目录加载测试。使用`/NoLoad`不加载测试,并执行InterSystems IRIS中已包含的测试。 `/run` (default) |运行测试。使用`/norun`加载但不运行任何测试。 `/delete` (default) |执行后从InterSystems IRIS中删除测试类。使用`/nodelete`保存类。 `/recursive` (default)| 在指定目录的子目录中查找测试。使用`/norecsive`不执行子目录中包含的测试。 `/debug` (default is /nodebug)| 使用/DEBUG,第一次测试失败后不会执行任何测试。从终端执行时,终端将在第一次故障后进入调试模式。 `/autoload` |使用`/autoload=dir`从`^UnitTestRoot`目录的子目录`“dir”`加载测试。 `/loadudl`|从`.cls`而不是`XML`文件加载测试。 # RunTest 示例 以下是使用`RunTest`执行单元测试的一些示例。 要使用`RunTest`,必须首先为`^UnitTestRoot`分配一个有效的目录名: ```java USER>Set ^UnitTestRoot = "C:\UnitTests" ``` 例1: ```java USER>Do ##class(%UnitTest.Manager).RunTest() ``` 在`^UnitTestRoot`目录的所有子目录中搜索包含测试类的XML文件。加载它找到的任何测试类并执行测试。 执行后从InterSystems IRIS中删除所有加载的测试类。 例2: ```java USER>Do ##class(%UnitTest.Manager).RunTest("mytests") ``` - 加载并执行`^UnitTestRoot`的`mytests`子目录(及其子目录)中的测试。 - 在测试类执行后从InterSystems IRIS中删除它们。 例3: ```java USER>Do ##class(%UnitTest.Manager).RunTest("mytests:MyPackage.Tests") ``` - 从`^UnitTestRoot`目录的`mytest`子目录(及其子目录)加载测试。仅执行`MyPackage.Tests`中的测试。 - 执行测试后从InterSystems IRIS中删除所有测试类。 例4: ```java USER>Do ##class(%UnitTest.Manager).RunTest("mytests:MyPackage.Tests", "/noload/nodelete") ``` - 不将测试加载到IRIS。 - 在`MyPackage.Tests`中执行测试。请注意,`mytest`必须仍然包含带有`MyPackage.Tests`类的XML文件。 - 不从IRIS中删除`MyPackage.Tests`。 # DebugRunTestCase `%UnitTest.Manager`类还包含`DebugRunTestCase`方法。若要使用此方法,仍必须先将`^UnitTestRoot`分配给有效目录: ```java USER>Set ^UnitTestRoot="C:\UnitTests" ``` 例如: ```java USER>Do ##class(%UnitTest.Manager).DebugRunTestCase("mytests","MyPackage.Tests","","") ``` - 该方法不从任何目录加载任何类,也不从InterSystems IRIS删除任何类。 - 该方法执行`MyPackage.Tests`中包含的测试。 - 可选的第三个参数用于限定符。 - 可选的第四个参数用于指定测试类中要执行的单个测试方法。 - 如果测试失败,该方法将继续执行其余的测试方法,但将在测试完成时中断。因此,如果从终端执行,则终端将进入调试模式。 **注意:使用`DebugRunTestCase`时,`mytest`目录实际上不需要包含`MyPackage.Tests`。相比之下,`RunTest`总是要求要执行的测试包含在`^UnitTestRoot`的子目录中,即使在使用`NoLoad”`时也是如此。** # 练习 练习1:`MyPackage.TestMe`包含一个名为`CreateContact`的方法。此方法创建并返回`Contact`实例。它接受`Name`和`ContactType`值作为参数。创建一个测试以下内容的单元测试: - 从`CreateContact`返回的`Contact`实例具有正确的`Name`值。 - 从`CreateContact`返回的`Contact`实例具有正确的`ContactType`值。 - `CreateContact`返回的`Contact`实例保存正确,即`%Save`返回`OK`状态。 练习2:`MyPackage.Contact`包含名为`ByContactType`的类查询。它返回具有`ContactType`指定值的所有`Contact`实例的`ID`值。将单元测试添加到`MyPackages.Tests`,用于测试以下各项: - 该查询返回指定`ContactType`的正确`ID`值数量。为此,必须正确初始化数据库。 - 查询返回的每个`ID`值对应于一个具有指定`ContactType`值的联系人。 请注意,添加此测试不应破坏在完成教程正文中的示例时添加到`MyPackage.Tests`中的测试。因此,必须以正确的方式初始化和恢复数据库。 > 把答案发到评论上!!! 或加群QQ 410039091 分享 # [源码](https://download.csdn.net/download/yaoxin521123/18703118)
文章
Hao Ma · 一月 30, 2021

精华文章--WebGateway系列(1): Web Gateway介绍

本文介绍InterSystems Web Gateway的安装和配置。 在2018以前的ISC产品中, InterSystems Web Gateway被称为CSP Gateway。, CSP是Cache'的页面技术。InterSystems的产品页面,Web服务等大多是CSP写成的。IRIS发布后CSP Gateway改名成Web Gateway, 但内部的配置文件,说明等等还到处可见CSP Gateway的叫法。在本文里不同的地方有这两个说法别奇怪,他们是一个东西。 IRIS通过它和外部Web服务器连接。 本文的内容适用任何ISC产品的部署,包括页面的选项Cache'. IRIS,HealthConnect, Ensemble等等。它的作用和表现是一样的。 无论您使用的是Cache',IRIS , HealthConnect还是HealthShare, 只有在生产环境中使用HTTP请求,基本上都需要使用Web Gateway。 如果需要更详细的内容,请参考在线文档:[InterSystems Web Gateway](https://docs.intersystems.com/healthconnectlatest/csp/docbook/DocBook.UI.Page.cls?KEY=PAGE_web_gateway) ### 什么是Web Gateway CSP是Cache' Server Page的缩写,如同JSP(Java Server Page)是Java的前端技术, CSP是InterSystems的前端技术。要在IRIS或者HealthConnect上提供一个HTTP服务,唯一安全可靠的技术是CSP. 创建HTTP,REST服务直接创建CSP页面, 创建SOAP服务使用%SOAP.WebService或者EnsLib.SOAP.Service, 它们都是%CSP.Page的子类,因此在IRIS的在线文档中有CSP Server的称法,指的就是IRIS中负责CSP处理的那部分功能。 CSP Server并不监听TCP端口得到HTTP消息,它只能通过CSP Gateway从Web服务器接收请求。用户的请求要先发给IIS/Apach/Nginx等Web服务器,转发给IRIS, 而Web Gateway就是Web服务器发请求给IRIS所使用的网关。 或者说, 它是InterSystems提供的给第三方Web服务器的一个组件,或者称为模块。在Windows系统中是若干DLL库文件,在LINUX环境是SO动态链接库。安装CSP Gateway就是诸如”CSPa24.so"等文件拷贝到Web服务器的目录,将这些模块配置到Web服务器,并将以.csp,.cls,.zen结尾的HTTP请求发送给IRIS。如果Web服务器和IRIS独立安装在不同的硬件服务器上(更安全的方式),发送的是TCP消息,到IRIS的superserver端口,默认是51773(Cache'是1972)。 CSP Gateway支持3种Web服务器:IIS, Apache Web Server, Nginx。 后面的链接提供了完整的在各种操作系统中ISC产品支持的Web Server的版本: [IRIS支持的第三方Web Server列表](https://docs.intersystems.com/healthconnectlatest/csp/docbook/platforms/ISP_technologies.html#ISP_webservers) 听上去是不是挺简单?那用户还有什么可糊涂的? ### Private Web Server(PWS)带来的混乱 混乱来自IRIS的安装过程会安装一个私有的Apache Web服务器,被称作PWS。它的作用有两个:支持访问维护页面;给一个测试环境提供测试Web服务的能力。在线文档是这么描述PWS的: >> The PWS is not supported for any other purpose. For deployments of http-based applications, including REST, CSP, Zen, and SOAP over http or https, you should not use the private web server for any application other than the Management Portal; instead, you must install and deploy one of the supported web servers. For information, see the section “Supported Web Servers” in the online InterSystems Supported Platforms document for this release.(**如果要部署http应用, 包括在http或者https上层的REST, CSP, Zen, SOAP,你绝不能让除Management Portal以外的任何应用使用PWS. 你必须安装一个IRIS兼容的Web服务器。了解这部分内容, 请查看InterSystems在线文档的"Supported Web Servers"部分**) 然后很多用户没有意识到这个提醒。当安装IRIS时被问到”你想要安装CSP网关并未CSP网关配置外部Web服务器(IIS和Apache)吗?"时,他们选择了“不要安装CSP网关",然后浏览器接入维护界面,开发了若干Web服务,一直没有意识使用PWS访问IRIS上的Web服务在生产环境是不可接受的。PWS是一个非常轻量级的Apache Web服务器。它的程序包在IRIS安装目录下的httpd子目录里。IRIS启动后, 它开始工作,监听IRIS上配置的Web端口,默认是57772,或者52773.它的工作机制决定了它无法承受大的负载,因此不能用于生产环境的http应用。 它和CSP/IRIS Server的连接用的是与上面讲的CSP Gateway完全相同的方式,也就是说,这里有一个PWS专用的Gateway, 我们可以称它为Private CSP Gateway。为了写的更清楚,总结了下面几点: ***CSP Gateway*** - 安装IRIS实例时用户可以选择是否安装CSP Gateway. 如果这时没选择安装,后面可以用单独的安装包安装。 - 安装的程序可以放在任何位置。比如在Linux默认放在"/opt/webgateway"目录,配置文件在Web Gateway的配置文件目录。 - 访问CSP Gateway的管理页面是 http://WebServer:80/csp/bin/Systems/Module.cxw 。 (这里的WebServer是Web服务器的地址,➕它的端口是默认的80) ***PWS*** - 安装时自动安装 - 程序和配置都在IRIS的安装目录,比如"C:/InterSystems/HealthConnect/CSP/bin/" - 访问管理页面的地址是 http://IRIS:57772/csp/bin/Systems/Module.cxw,这里的IRIS是IRIS服务器的地址,如果是本机登录,也就是localhost. 注意一点:从PWS访问IRIS管理页面, 比如 http://localhost/csp/sys/UtilHome.csp, 选择其中的 “系统管理 > 配置 > Web网关管理"进入的是PWS的配置。如果是从 http://WebServer/csp/sys/UtilHome.csp进入的IRIS管理页面, 那么同样的操作进入的是CSP Gateway的管理页面。 这很容易从页面显示的Web Server的类型和版本发现区别。 ### 其他关于部署CSP Gateway的疑问 - 一个Web服务器可以连接多个CSP Gateway吗? 如果你真正理解了CSP Gateway, 你就明白它是Web服务器工作的一部分,比如在IIS里面它就是配置的一个虚拟路径。技术上你可以多配一个,但完全没有必要。 如果要把HTTP从一个Web服务器发到多个IRIS, 可以在一个Web Gateway里配置多个"Server Access"连接。 - 一个CSP Gateway是怎么连接多个IRIS Server的? CSP Gateway可以配置多个"Server Access”, 只是要区分出收到的请求应该发给那个IRIS Server.如果分发给不同的IRIS的URL是不同的,比如CSP Gateway可以路由"/csp/demo1"到第一个IRIS, "/csp/demo2"到第2个IRIS。 - Web Server要和IRIS部署在一台服务器吗? 生产环境中, 部署单独的Web Server通常是好选择。为了安全起见,很多用户会部署Web Server的高可用。 如果Web Server和Caché/IRIS分别装在两台服务器,IRIS安装时选择“不要安装CSP网关”,在Web Server的服务器上安装单独的Web Gateway软件包,测试和Caché/IRIS的连接。 如果是Web Server和Caché/IRIS装在同一台服务器, 那么应该先安装Web Server, 然后使用Caché/IRIS安装包安装Caché, 选择 “安装CSP网关”, 这样CSP网关会被安装在Web Server的目录下, 相关的模块和Web Server配置也会自动完成。 如果顺序反过来, 那么需要手工配置Web Server, 增加的不必要的复杂步骤。 - 安装外部Web Server能使用私有Web Gateway吗? 对Web服务器有了解的用户更会有这样的疑问。既然Web Gateway只是给Web Gateway工作的程序组件,那么是否从外部服务器就可以直接使用私有的Web Gateway了,何必再多安装一个。 是的,技术上这样是可行的。前提是,1. 外部Web服务器和IRIS在一台硬件服务器上。2. 客户要对外部服务器的配置非常熟悉,可以手工配置外部Web服务器对私有Web Gateway的访问, 包括路径或者虚假路径,文件夹的访问权限,用户或者用户组的权限等等。总的说, 这样既麻烦,又不便于后期的管理,因此我推荐还是重新装一个Web Gateway。只是要分清它和私有的连接PWS的Web Gateway的区别,而永远不要让他们混在一起。 安装CSP Gateway的具体步骤请参考下面的文章: WebGateway系列(2): 配置Apache连接IRIS WebGateway系列(3): 配置IIS连接IRIS
文章
Michael Lei · 六月 7, 2022

绕过ODBC使用ObjectScript访问远程系统

绕过ODBC使用ObjectScript访问远程系统 这是一个在 IRIS 2020.1 和 Caché 2018.1.3 上工作的代码示例 不会与新版本保持同步 也不会获得 InterSystems 提供的支持服务! 我们经常会遇到这样的情况,由于各种原因ODBC是访问一个远程系统的唯一选择。如果你只需要检查或改变表,这就足够了。但你不能直接执行一些命令或改变一些Global。 特别感谢@Anna.Golitsyna 启发我发表此文。 这个例子提供了3种 SQLprocedure 方法来实现访问远程系统这个目的,如果其他的访问方式被阻止,通常是被一些防火墙阻止。 SQLprocedure Ping() 返回 Server::Namespace::$ZV 并允许检查连接 SQLprocedure Xcmd(<commandline>,<resultvar>) 执行你提交的命令行,并返回一个存放在你命名的变量中的结果。 SQLprocedure Gset(<global>,<subscript>,<value>,<$data>) 允许你设置或删除一个Global节点 。参数<global> 是远程命名空间中的GlobalName,包括前导符;例如'^MyGlobal' (注意:sql 用单引号!) 。参数<subscript>代表完整的下标,包括括号;例如 '(1,3, "something",3)' (注意:sql 用单引号!)。 参数<$data> 控制你是对Global节点赋值,还是对其执行ZKILL;例如,1,11表示设置,0,10表示ZKILL;正如你可能从名字中猜到的那样,在Global复制过程中,这特别有用。 程序Gset是为了利用前一篇文章的Global扫描而设计的,结合起来用,它们支持在任何ODBC连接中进行Global复制。 安装: - 在远程系统中,你需要我们应用市场OpenExchange中提供的类 - 在本地(源)系统中你需要定义这些程序为连接的SQL Procedures SMP>System>SQL> Wizards>Link Procedure 同时定义你的本地包名称 (在这个例子中我使用zrccEX)- 如果你想运行Global 复制你还需要从我们应用市场安装Global Scanning 类 (只是为了舒服一点) 例子: USER>do $system.SQL.Shell() SQL Command Line Shell [SQL]USER>>select rccEX.Ping() Expression_1 cemper9::CACHE::IRIS for Windows (x86-64) 2020.1 (Build 215U) Mon Mar 30 2020 20:14:33 EDT 检查Global是否存在 ^rcc [SQL]USER>>select rccEX.Xcmd('set %y=$d(^rcc)','%y') ok: 10 赋值 ^rcc4(1,"demo",3,4) [SQL]USER>>select rccEX.Gset('^rcc4','(1,"demo",3,4)','this is a demo',1) Expression_1 ok: ^rcc4(1,"demo",3,4) 从 ^rcc2 到 ^rcc4 进行Global 复制.首先显示 ^rcc2 USER>>select reference,value,"$DATA" from rcc_G.Scan where rcc_G.scan('^rcc2',4)=1 Reference Value $Data ^rcc2 10 (1) 1 1 (2) 2 11 (2,"xx") 10 (2,"xx",1) "XX1" 1 (2,"xx",10) "XX10" 1 (2,"xx",4) "XX4" 1 (2,"xx",7) "XX7" 1 (3) 3 1 (4) 4 11 (4,"xx") 10 (4,"xx",1) "XX1" 1 (4,"xx",10) "XX10" 1 (4,"xx",4) "XX4" 1 (4,"xx",7) "XX7" 1 (5) 5 1 16 Rows(s) Affected 现在执行复制到远程global [SQL]USER>>select rccEX.Gset('^rcc4',reference,value,"$DATA") from rcc_G.Scan where rcc_G.scan('^rcc2',4)=1 Expression_1 ok: ^rcc4 ok: ^rcc4(1) ok: ^rcc4(2) ok: ^rcc4(2,"xx") ok: ^rcc4(2,"xx",1) ok: ^rcc4(2,"xx",10) ok: ^rcc4(2,"xx",4) ok: ^rcc4(2,"xx",7) ok: ^rcc4(3) ok: ^rcc4(4) ok: ^rcc4(4,"xx") ok: ^rcc4(4,"xx",1) ok: ^rcc4(4,"xx",10) ok: ^rcc4(4,"xx",4) ok: ^rcc4(4,"xx",7) ok: ^rcc4(5) 16 Rows(s) Affected
文章
姚 鑫 · 三月 4, 2021

第三章 SQL语言元素(一)

# 第三章 SQL语言元素(一) # 命令和关键字 InterSystems SQL命令(也称为SQL语句)以关键字开头,后跟一个或多个参数。其中一些参数可能是子句或函数,由它们自己的关键字标识。 - **InterSystems SQL命令没有命令终止符,除非在特殊情况下(例如SQL过程代码或触发代码),在这种情况下,SQL命令以单个分号(`;`)终止。否则,InterSystems SQL命令不需要或接受分号命令终止符**。在InterSystems SQL中指定分号命令终止符会导致`SQLCODE -25`错误。 TSQL的InterSystemsIRIS®数据平台实现(Transact-SQL)接受但不需要分号命令终止符。在将SQL代码导入Inter Systems SQL时,会去除分号命令终止符。 - **InterSystems SQL命令没有空格限制。如果命令项之间用空格隔开,则至少需要一个空格。** 如果命令项之间用逗号分隔,则不需要空格。算术运算符之前或之后不需要空格。可以在以空格分隔的项目之间,以逗号分隔的参数列表中的项目之间或在算术运算符之前或之后插入换行符或多个空格。 InterSystems SQL关键字包括命令名称,函数名称,谓词条件名称,数据类型名称,字段约束,优化选项和特殊变量。它们还包括`AND`,`OR`和`NOT`逻辑运算符,`NULL`列值指示符以及ODBC函数构造,例如`{d dateval}`和`{fn CONCAT(str1,str2)}`。 - 关键字不区分大小写。按照惯例,在本文档中,关键字用大写字母表示,但是InterSystems SQL没有大小写限制。 - 有许多关键字是SQL保留字。 InterSystems SQL仅保留那些不能明确解析的关键字。 SQL保留字可用作分隔符。 # 函数:内在的和外在的 - 内在的:InterSystems SQL支持大量内在的(系统提供的)函数。 这些函数包括数字函数、字符串函数以及日期和时间函数。 聚合函数是SQL固有函数,它计算列的所有值并返回单个聚合值。 - InterSystems SQL也可以支持用户提供的ObjectScript函数调用(外部函数),如下所示: 这种写法只能在mac routine里,类文件里编译报错。 ```sql MySQL &sql(SELECT Name,$$MyFunc() INTO :n,:f FROM Sample.Person) WRITE "name is: ",n,! WRITE "function value is: ",f,! QUIT MyFunc() SET x="my text" QUIT x ``` 如果将用户提供的(外部)函数的使用配置为系统范围的选项,则该SQL语句只能调用用户提供的(外部)函数。默认为“否”。默认情况下,尝试调用用户提供的函数会发出`SQLCODE -372`错误。可以使用`%SYSTEM.SQL类的SetAllowExtrinsicFunctions()`方法在系统范围内配置SQL对外部函数的使用。若要确定当前设置,请调用`$SYSTEM.SQL.CurrentSettings()`,该显示显示“允许在SQL语句中使用外部函数”选项。 不能使用用户提供的函数来调用`%routine`(名称以%字符开头的例程)。 尝试这样做会发出`SQLCODE -373`错误。 # 文字 InterSystems SQL文字具有以下语法: ``` literal ::= number | string-literal number ::= {digit}[.]digit{digit}[E[+|-]digit{digit}] digit ::= 0..9 string-literal ::= std-string-literal | ObjectScript-empty-string std-string-literal ::= ' {std-character-representation} ' std-character-representation ::= nonquote-character | quote-symbol quote-symbol ::= '' ObjectScript-empty-string ::= "" ``` 文字是一系列代表实际(文字)值的字符。它可以是数字或字符串。 - 数字不需要任何分隔符。它可以由数字0到9,小数点字符,指数符号以及加号和减号组成。数字中只能使用一个小数点字符。该小数点只能用于数字的基数部分,不能用于指数部分。小数点后不需要数字。允许前导零和尾随零。指数(科学符号)符号为字母E;大写字母E和小写字母E都可以接受,但是大写字母E是首选用法。加号或减号可以加一个底数或一个指数。多个加号和减号可以加上x个基数; SQL将这些符号视为运算符。 x只能有一个正负号。 SQL将此符号视为文字的一部分。请勿在数字中使用逗号或空格。 - 字符串文字包含一对分隔符,其中包含任何类型的字符串。首选的定界符是单引号字符。要将分隔符指定为字符串中的文字,请将该字符加倍;例如: `'Mary's office'`. **空字符串是文字字符串;它由两个单引号字符(`''`)表示。 `NULL`不是文字值;它表示没有任何值。** 注意:在嵌入式SQL中,不允许在字符串文字中使用以`##`开头的一些字符序列,如“使用嵌入式SQL”一章的“文字值”中所述。此限制不适用于其他SQL调用,例如动态SQL。 # 字符串分割符 使用单引号(`'`)字符作为字符串定界符。 SQL兼容性支持双引号字符(`“`)的使用,但由于与定界标识符标准冲突,因此强烈建议不要使用。将一对双引号字符`""`解析为无效的定界标识符。并生成`SQLCODE -1`错误。 要在字符串中指定单引号字符作为字面字符,请指定一对这样的字符作为字面转义序列。 例如,`'a 'normal' string'`。 ## 串联 双竖条(`||`)是首选的SQL连接操作符。 它可以用于连接两个数字、两个字符串或一个数字和一个字符串。 下划线(`_`)作为SQL连接操作符提供,以保证ObjectScript的兼容性。 此连接操作符只能用于连接两个字符串。 如果两个操作数都是字符串,并且两个字符串都具有相同的排序规则类型,则所得的级联字符串具有该排序规则类型。在所有其他情况下,连接的结果是排序类型`EXACT`。 # NULL和空字符串 使用`NULL`关键字表示没有指定值。 在SQL中,`NULL`始终是表示数据值因任何原因未指定或不存在的首选方式。 SQL零长度字符串(空字符串)由两个单引号字符指定。 空字符串(`"`)与空字符串是不同的。 空字符串是一个已定义的值,一个不包含字符的字符串,一个长度为0的字符串。 一个零长度的字符串在内部由非显示字符`$CHAR(0)`表示。 **注意:不建议使用SQL零长度字符串作为字段输入值或字段默认值。 使用`NULL`表示数据值的缺失。** **在SQL编码中应避免使用SQL零长度字符串。 但是,由于许多SQL操作都会删除末尾的空格,所以只包含空格字符(空格和制表符)的数据值可能会导致SQL的零长度字符串。** 注意,不同的SQL length函数返回不同的值:`length`、`CHAR_LENGTH`和`DATALENGTH`返回SQL长度。 `$LENGTH`返回ObjectScript表示长度。 长度不计算尾随空格; 所有其他长度函数都计算末尾的空格。 # null 处理 NOT NULL数据约束要求字段必须接收一个数据值; 不允许指定NULL而不是值。 这个约束不阻止使用空字符串值。 `SELECT`语句的`WHERE`或`HAVING`子句中的`IS NULL`谓词选择空值; 它不选择空字符串值。 `IFNULL`函数计算一个字段值,如果字段值为`NULL`,则返回第二个参数中指定的值。 它不会将空字符串值视为非空值。 `COALESCE`函数从提供的数据中选择第一个非空值。 它将空字符串值视为非空值。 当`CONCAT`函数或`concenate`操作符(`||`)连接一个字符串和一个`NULL`时,结果是`NULL`。 如下面的例子所示: ```sql SELECT {fn CONCAT('fred',NULL)} AS FuncCat, -- returns 'fred'||NULL AS OpCat -- returns ``` `AVG、COUNT、MAX、MIN`和`SUM`聚合函数在执行操作时忽略`NULL`值。 (`COUNT *`统计所有行,因为不可能有一个所有字段都为空值的记录。) `SELECT`语句的`DISTINCT`关键字在其操作中包含`NULL`; 如果指定的字段有空值,`DISTINCT`返回一个空行. `AVG`、`COUNT`和`MIN`、聚合函数受空字符串值的影响。 `MIN`函数将空字符串视为最小值,即使存在值为0的行。 `MAX`和`SUM`聚合函数不受空字符串值的影响。 ## null 表达式 对大多数SQL函数提供`NULL`作为操作数将返回`NULL`。 任何以NULL作为操作数的SQL算术操作都返回`NULL`值。 因此,7 +零=零。 这包括二元运算加法(`+`)、减法(`-`)、乘法(`*`)、除法(`/`)、整数除法(`\`)和取模(`#`),以及一元符号运算符加号(`+`)和减号(`-`)。 算术操作中指定的空字符串将被视为0(零)值。 除法(`/`),整数除法(`\`),或对空字符串(`6/ "`)取模(`#`)会导致``错误。 ## NULL的长度 在SQL中,`NULL`的长度是没有定义的(它返回`< NULL >`)。 然而,空字符串的长度被定义为长度为0。 如下面的例子所示: ```sql SELECT LENGTH(NULL) AS NullLen, -- returns LENGTH('') AS EmpStrLen -- returns 0 ``` 如本例所示,SQL `LENGTH`函数返回SQL长度。 可以使用`ASCII`函数将SQL的零长度字符串转换为`NULL`,示例如下: ```sql SELECT LENGTH(NULL) AS NullLen, -- returns LENGTH({fn ASCII('')}) AS AsciiEmpStrLen, -- returns LENGTH('') AS EmpStrLen -- returns 0 ``` 但是,对标准`SQL`的某些系统间IRIS扩展对`NULL`和空字符串的长度的处理是不同的。 $LENGTH函数返回这些值的InterSystems IRIS内部表示:`NULL`表示为长度为0的已定义值,SQL空字符串表示为长度为0的字符串。 该功能与ObjectScript兼容。 ```sql SELECT $LENGTH(NULL) AS NullLen, -- returns 0 $LENGTH('') AS EmpStrLen, -- returns 0 $LENGTH('a') AS OneCharStrLen, -- returns 1 $LENGTH(CHAR(0)) AS CharZero -- returns 0 ``` 这些值的内部表示方式的另一个重要位置是`%STRING`、`%SQLSTRING`和`%SQLUPPER`函数,它们将空格附加到值中。 因为`NULL`实际上没有值,所以在它后面添加一个空格会创建一个长度为1的字符串。 但是一个空字符串确实有一个字符值,所以在它后面加上一个空格会创建一个长度为2的字符串。 如下面的例子所示: ```sql SELECT CHAR_LENGTH(%STRING(NULL)) AS NullLen, -- returns 1 CHAR_LENGTH(%STRING('')) AS EmpStrLen -- returns 2 ``` 注意,这个例子使用的是`CHAR_LENGTH`,而不是`LENGTH`。 因为`LENGTH`函数删除了末尾的空格,所以`LENGTH(%STRING(NULL))`返回长度为0的字符串; `LENGTH(%STRING("))`返回长度为2的字符串,**因为`%STRING`追加的是前导空格,而不是尾随空格。** ## ObjectScript和SQL 当SQL `NULL`输出到ObjectScript时,它由ObjectScript空字符串(`""`)表示,长度为0的字符串。 当SQL零长度字符串数据输出到ObjectScript时,它由包含`$CHAR(0)`的字符串表示,该字符串长度为1。 ```sql /// d ##class(PHA.TEST.SQL).Null() ClassMethod Null() { &sql(SELECT NULL,'' INTO :a,:b) WRITE !,"NULL length: ",$LENGTH(a) // returns 0 WRITE !,"empty string length: ",$LENGTH(b) // returns 1 } ``` ```sql DHC-APP>d ##class(PHA.TEST.SQL).Null() NULL length: 0 empty string length: 1 ``` 在ObjectScript中,没有值通常用空字符串(`""`)表示。 当这个值被传递到嵌入式SQL中时,它会被视为空值,如下面的例子所示: ```sql /// d ##class(PHA.TEST.SQL).Null1() ClassMethod Null1() { SET x="" SET myquery="SELECT NULL As NoVal,:x As EmpStr" SET tStatement=##class(%SQL.Statement).%New() SET qStatus=tStatement.%Prepare(myquery) IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT} SET rset=tStatement.%Execute() WHILE rset.%Next() { WRITE "NoVal:",rset.%Get("NoVal")," length ",$LENGTH(rset.%Get("NoVal")),! // length 0 WRITE "EmpStr:",rset.%Get("EmpStr")," length ",$LENGTH(rset.%Get("EmpStr")),! // length 0 } WRITE "End of data" } ``` ```sql DHC-APP>d ##class(PHA.TEST.SQL).Null1() NoVal: length 0 EmpStr: length 0 End of data ``` 如果指定了一个未定义的输入主机变量,嵌入式SQL将其值视为`NULL`。 当将NULL或空字符串值从嵌入式SQL传递到ObjectScript时,`NULL`被转换为长度为0的字符串,空字符串被转换为长度为1的字符串。 如下面的例子所示: ```sql /// d ##class(PHA.TEST.SQL).Null2() ClassMethod Null2() { &sql(SELECT NULL, '' INTO :a,:b) WRITE !,"The length of NULL is: ",$LENGTH(a) // length 0 WRITE !,"The length of empty string is: ",$LENGTH(b) // length 1 } ``` ```sql DHC-APP>d ##class(PHA.TEST.SQL).Null2() The length of NULL is: 0 The length of empty string is: 1 ``` 在下面的例子中,SQL的空字符串加上一个空格被传递为长度为2的字符串: ```sql /// d ##class(PHA.TEST.SQL).Null3() ClassMethod Null3() { &sql(SELECT %SQLUPPER('') INTO :y ) WRITE !,"SQL empty string length: ",$LENGTH(y) } ``` ```sql DHC-APP> d ##class(PHA.TEST.SQL).Null3() SQL empty string length: 2 ``` good, mark Thank you! It's very useful for us while using InterSystems SQL. 学习了,谢谢大佬 很实用!感谢总结! 大佬,已被圈粉,长期追踪学习!
文章
Lilian Huang · 七月 13, 2022

用 Python 的 Native API 调用类方法

InterSystems Native SDK for Python是 InterSystems IRIS APIs 的轻量级接口,曾经只能通过 ObjectScript 使用。 准确地说,我对调用 ObjectScript 方法、类方法的能力特别感兴趣。 它可以工作,而且效果很好,但默认情况下,调用只支持标量参数:字符串、布尔值、整数和浮点数。 但如果你想: - 传递或返回结构,例如字典或列表 - 传递或返回流 您需要编写一些粘合代码或使用这个project (使用 pip install edpy 安装)。 edpy 包会给你一个简单的签名: call(iris, class_name, method_name, args) 它允许您调用任何 ObjectScript 方法并返回结果。 像这样导入它: from edpy import iris call accepts 4 required arguments:- iris - a reference to an established IRIS object- class_name - IRIS class to call- method_name - IRIS method to call- args - list of 0 or more arguments 参数 每个参数可以是以下其中之一: string字符串(任意长度,如果大于 $$$MaxStringLength或 3641144 个符号,则会自动转换为流 boolean布尔值 integer整数 float 浮动 dict字典(转换为动态对象) list or tuple列表或元组(转换为动态数组) 字典、列表和元组参数可以递归地包含其他字典、列表和元组(持续性内存)。 返回值 在返回值,我们期望是动态对象/数组或 JSON 字符串/流。 在这种情况下,edpy 会首先将其转换为 Python 字符串,并在可能的情况下将其解释为 Python 字典或列表。 否则,结果将按原样返回给调用者。 就是这样,但让我给你一些 ObjectScript 方法的例子,以及如何使用这个 Python 函数调用它们。 示例1: Pong ClassMethod Test(arg1, arg2, arg3) As %DynamicArray{ return [(arg1), (arg2), (arg3)]} 调为: >>> iris.call(iris_native, "User.Py", "Test", [1, 1.2, "ABC"])[1, 1.2, 'ABC'] 这里没有惊喜。 参数被打包回一个数组并返回给调用者。 示例 2: 属性 ClassMethod Test2(arg As %DynamicObject) As %String{ return arg.Prop} 像这样调用: >>> iris.call(iris_native, "User.Py", "Test2", [{"Prop":123}])123 现在进行更嵌入式的调用: >>> iris.call(iris_native, "User.Py", "Test2", [{"Prop":{"Prop2":123}}]) {'Prop2': 123} 如果属性太长也没关系 - 流将用于将其发送到 IRIS 和/或返回: ret = iris.call(iris_native, "User.Py", "Test2", [{"Prop":"A" * 10000000}]) >>> len(ret) 10000000 如果您需要保证InterSystems IRIS 端的流,您可以使用%Get: set stream = arg.%Get("Prop",,"stream") 如果流是 base64 编码的,您可以使用以下命令自动对其进行解码: set stream = arg.%Get("Prop",,"stream<base64") 示例 3: 字符串或流 ClassMethod Test3(arg As %Stream.Object) As %String { set file = ##class(%Stream.FileCharacter).%New() set file.TranslateTable = "UTF8" set filename = ##class(%File).ManagerDirectory() _ "test.txt" do file.LinkToFile(filename) if $isObject(arg) { set sc = file.CopyFromAndSave(arg) } else { do file.Write(arg) set sc = file.%Save() } if $$$ISERR(sc) { set jsonret = {"status":0, "payload":($system.Status.GetErrorText(sc))} } else { set jsonret = {"status":1} } quit jsonret.%ToJSON() } 这里我们将字符串或流写入 <mgr>test.txt。 >>> iris.call(iris_native, "User.Py", "Test3", ["😊"]) {'status': 1} 注意:在所有代码示例中输入为“&# x1f642;“。 如果我打开文件,我会看到一个 😊 而不是两个 ?? - 所以我们保留编码。 >>> iris.call(iris_native, "User.Py", "Test3", ["🙂" * 10000000]) {'status': 1} 为简便起见,我将省略文件输出,但它就在那里。 最后,通过在内部传递一个动态对象或数组,您可以完全避免字符串/流二分法,即使您不知道该属性是比字符串限制短还是长。 在这种情况下,您始终可以像流一样获取可疑属性。 示例4: 返回流 ClassMethod Test4(arg As %DynamicArray) As %String { return arg.%Get(0) } 看起来是这样: >>> ret = iris.call(iris_native, "User.Py", "Test4", [["😊" * 10000000]]) >>> len(ret) 10000000 >>> ret[:5] '😊😊😊😊😊' 还有一件事 还有一个 get_iris(ip="localhost", port=1972, namespace="USER", username="_SYSTEM", password="SYS") 函数可以让您获得一个工作的 IRIS 对象。 所以这里有一个完整的例子,如果你想自己尝试一下: 首先加载 User.Py class 并安装 edpy python 库: pip install edpy 然后在python调用中: from edpy import iris iris_native = iris.get_iris() iris.call(iris_native, "User.Py", "Test", [1, 1.2, "ABC"]) iris.call(iris_native, "User.Py", "Test2", [{"Prop":123}]) iris.call(iris_native, "User.Py", "Test2", [{"Prop":{"Prop2":123}}]) ret2 = iris.call(iris_native, "User.Py", "Test2", [{"Prop":"A" * 10000000}]) iris.call(iris_native, "User.Py", "Test3", ["😊"]) iris.call(iris_native, "User.Py", "Test3", ["😊" * 10000000]) ret4 = iris.call(iris_native, "User.Py", "Test4", [["😊" * 10000000]]) 结论 适用于 Python 的 Native SDK 是一个强大的工具,为您提供对 InterSystems IRIS 的完整且不受限制的访问。 我希望这个项目可以为您节省一些时间来编组 InterSystems IRIS 调用。 是否有一些它不支持的方法参数组合? 如果是这样,请在评论中分享您是如何调用的。 链接: Native SDK Docs User.Py class Repo 原文请参考链接:https://community.intersystems.com/node/521336
文章
姚 鑫 · 八月 23, 2022

第十章 配置数据库(二)

# 第十章 配置数据库(二) # 本地数据库 “本地数据库”页面显示关于系统上的数据库的以下信息: - `Name`—数据库名称。 - `Mirror`——如果数据库是镜像的,则镜像的名称; - `Directory` - `IRIS.DAT` 文件的位置。 - `Size` 大小 (MB) — 以 `MB` 为单位的数据库大小。 - `Status` 状态 — 指定数据库是挂载、卸载还是卸载;如果已挂载,则指定它是否具有只读或读写权限。 - `Resource Name`资源名称 — 控制对数据库的访问的数据库资源的名称; - `Encrypted` — 指定数据库是否加密; - `Journal` 指定数据库是否被记录; ## 创建本地数据库 要创建本地数据库,请导航到本地数据库页面(系统管理 > 配置 > 系统配置 > 本地数据库)。 1. 单击创建新数据库以打开数据库向导。 2. 在文本框中输入数据库名称。数据库名称必须: - 尚未在 `IRIS` 实例中使用 - 长度在 `1` 到 `30` 个字符之间 - 以字母字符或下划线开头;其余部分可以包括字母数字字符、破折号或下划线 3. 首次使用特定浏览器在 `IRIS` 实例中创建本地数据库时,必须 - 输入数据库目录的名称,在这种情况下,包含 `IRIS.DAT` 文件的目录在确认后将在 `c:\InterSystems\mgr` 中创建 - 单击文件夹图标浏览到现有目录,在这种情况下,将在该目录中创建 `IRIS.DAT` 文件 此后,默认情况下,将在与先前数据库目录相同的位置创建一个与提供的数据库名称相同的目录,其中包含 `IRIS.DAT` 文件。例如,如果首先在 `c:\InterSystems\mgr` 下的任何目录中创建数据库 `db22`,当再次单击 `Create New Database` 并在 `Enter the name of your database` 框中输入 `db33` 时,`c:\InterSystems\mgr\db33` 会自动填写到数据库目录文本框中。如果将其更改为 `c:\InterSystems\db33` 并创建 `db33`,则下次将填充基本目录 `c:\InterSystems`。 注意:不支持在配置数据库目录时使用符号链接。 4. 单击下一步继续配置数据库。如果指定的目录中已经存在 `IRIS.DAT` 文件,会收到警告,并且可以 - 单击完成以使用现有文件,在这种情况下,所有数据库特征都由 `IRIS.DAT` 文件确定。您通常会在从另一个实例复制或移动数据库时执行此操作,或者在同一系统上临时挂载在另一个实例中创建的数据库时执行此操作。 - 单击 `Back` 指定另一个目录,然后再次单击 `Next` 以在下一步中继续指定新数据库的特征。 5. 在 `Initial Size` 文本框中,键入数据库大小的兆字节数(默认值为 `1 MB`)。 **注意:不能创建或编辑数据库,使其大小大于可用的总磁盘空间。如果指定的大小在磁盘可用空间的 `90%` 以内,会收到警告并且必须确认操作。** 6. 从 `Block size for this database will be` 下拉列表中选择所需的块大小。默认情况下,所有新数据库都使用 `8 KB` 的块大小创建。 注意:请勿从下拉列表中选择 `8 KB` 以外的块大小,除非已阅读并理解大块大小注意事项`/` 7. 从 `Journal globals` 中选择是否要在此数据库中记录全局变量`?`下拉列表。 注意:如果将数据库配置为存储临时全局变量,将 `Journal globals` 属性设置为 `No` 与将临时全局变量存储在 IRSTEMP 中是不同的。 8. 如果激活了加密,可以通过选择是加密数据库来加密这个数据库?。 9. 如果实例是镜像的一部分,可以通过为 `Mirrored Database?` 选择 `Yes` 来将此数据库添加到镜像中。 10. 从此面板开始,可以单击下一步。继续配置数据库或完成以接受剩余的默认值。 11. 选择资源来控制对该数据库的访问: - 默认值 — `%DB_%DEFAULT` - 现有 - 从现有数据库资源列表中选择 - 新建 — 创建新的数据库资源(新名称默认为 `%DB_%` 数据库名称) 12. 单击下一步查看数据库属性列表。 13. 单击完成以添加数据库。 现在已准备好配置和管理新数据库。 **注意:为防止意外损坏数据库,不能打开或写入名为 `IRIS.DAT` 的操作系统文件,即使它不是已安装的数据库。**
文章
姚 鑫 · 五月 9, 2021

第四章 多维存储的SQL和对象使用(一)

# 第四章 多维存储的SQL和对象使用(一) 本章介绍InterSystems IRIS®对象和SQL引擎如何利用多维存储(全局变量)来存储持久对象、关系表和索引。 尽管InterSystems IRIS对象和SQL引擎会自动提供和管理数据存储结构,但了解其工作原理的详细信息还是很有用的。 数据的对象视图和关系视图使用的存储结构是相同的。为简单起见,本章仅从对象角度介绍存储。 # 数据 每个使用`%Storage.Persistent`存储类(默认)的持久化类都可以使用多维存储(全局变量)的一个或多个节点在InterSystems IRIS数据库中存储其自身的实例。 每个持久化类都有一个存储定义,用于定义其属性如何存储在全局变量节点中。这个存储定义(称为“默认结构”)由类编译器自动管理。 ## 默认结构 用于存储持久对象的默认结构非常简单: - 数据存储在名称以完整类名(包括包名)开头的全局变量中。附加`“D”`以形成全局数据的名称,而附加`“I”`作为全局索引。 - 每个实例的数据都存储在全局数据的单个节点中,所有非瞬态属性都放在`$list`结构中。 - 数据全局变量中的每个节点都以对象`ID`值作为下标。默认情况下,对象`ID`值是通过调用存储在全局变量数据根(没有下标)的计数器节点上的`$Increment`函数提供的整数。 例如,假设我们定义了一个简单的持久化类`MyApp.Person`,它有两个文本属性: ```java Class MyApp.Person Extends %Persistent { Property Name As %String; Property Age As %Integer; } ``` 如果我们创建并保存此类的两个实例,得到的全局变量结果将类似于: ```java ^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`所基于的任何特性。 ```java Class MyApp.Person Extends %Persistent { Index IDKEY On Name [ Idkey ]; Property Name As %String; Property Age As %Integer; } ``` 如果我们创建并保存`Person`类的两个实例,得到的全局变量结果现在类似于: ```java ^MyApp.PersonD("Abraham") = $LB("",530,"Abraham") ^MyApp.PersonD("Philip") = $LB("",680,"Philip") ``` 请注意,不再定义任何计数器节点。还要注意,通过将对象`ID`基于`Name`属性,我们已经暗示了`Name`的值对于每个对象必须是唯一的。 如果`IDKEY`索引基于多个属性,则主数据节点具有多个下标。例如: ```java Class MyApp.Person Extends %Persistent { Index IDKEY On (Name,Age) [ Idkey ]; Property Name As %String; Property Age As %Integer; } ``` 在这种情况下,生成的全局变量现在类似于: ```java ^MyApp.PersonD("Abraham",530) = $LB("",530,"Abraham") ^MyApp.PersonD("Philip",680) = $LB("",680,"Philip") ``` **重要提示:`IDKEY`索引使用的任何属性的值中都不能有连续的一对竖线(`||`),除非该属性是对持久类实例的有效引用。 这种限制是由InterSystems SQL机制的工作方式强加的。 在`IDKey`属性中使用`||`会导致不可预知的行为。** ## Subclasses 默认情况下,持久性对象的子类引入的任何字段都存储在附加节点中。 子类的名称用作附加的下标值。 例如,假设我们定义了一个具有两个文本属性的简单持久`MyApp.Person`类: ```java Class MyApp.Person Extends %Persistent { Property Name As %String; Property Age As %Integer; } ``` 现在,我们定义了一个持久子类`MyApp.Students`,它引入了两个额外的文本属性: ```java Class MyApp.Student Extends Person { Property Major As %String; Property GPA As %Double; } ``` 如果我们创建并保存此`MyApp.Student`类的两个实例,得到的全局结果将类似于: ```java ^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”`-它标识包含学生数据的节点。 ## 父子关系 在父子关系中,子对象的实例存储为它们所属的父对象的子节点。这种结构确保子实例数据与父数据在物理上是集群的。 ```java /// 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`: ```java /// 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`对象,则得到的全局变量结果将类似于: ```java ^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`结构),然后以与任何其他属性相同的方式存储此串行状态。 例如,假设我们定义了一个具有两个文字属性的简单串行(可嵌入)类: ```java Class MyApp.MyAddress Extends %SerialObject { Property City As %String; Property State As %String; } ``` 现在,我们修改前面的示例以添加嵌入的`Home Address`属性: ```java Class MyApp.MyClass Extends %Persistent { Property Name As %String; Property Age As %Integer; Property Home As MyAddress; } ``` 如果我们创建并保存此类的两个实例,则生成的全局变量相当于: ```java ^MyApp.MyClassD = 2 // counter node ^MyApp.MyClassD(1) = $LB(530,"Abraham",$LB("UR","Mesopotamia")) ^MyApp.MyClassD(2) = $LB(680,"Philip",$LB("Bethsaida","Israel")) ``` ## 流 **通过将全局流的数据拆分成一系列块(每个块小于`32K`字节)并将这些块写入一系列顺序节点,全局流被存储在全局流中。文件流存储在外部文件中。**
文章
姚 鑫 · 六月 25, 2021

第十八章 签署XML文档

# 第十八章 签署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") ```