#InterSystems IRIS for Health

0 关注者 · 862 帖子

InterSystems IRIS for Health™ 是全球第一个也是唯一一个专门为医疗应用程序的快速开发而设计的数据平台,用于管理全世界最重要的数据。它包括强大的开箱即用的功能:事务处理和分析、可扩展的医疗保健数据模型、基于 FHIR 的解决方案开发、对医疗保健互操作性标准的支持等等。所有这些将使开发者能够快速实现价值并构建具有突破性的应用程序。了解更多信息

文章 Nicky Zhu · 五月 9, 2023 5m read

在上一篇文章中,我们看到了如何捕获位于我们服务器上一个文件夹中的DICOM类型的文件,以及如何将它们发送到PACS软件(在我们的案例中是ORTHANC开源解决方案)进行存储和咨询。那么,在这篇文章中,我们要处理的是相反的动作。

在我们的例子中,我们将配置我们的IRIS for Health Prodcution,以接收从我们的PACS通过TCP/IP发送的图像。要做到这一点,我们必须包括一个标准EnsLib.DICOM.Service.TCP类的业务服务,它将允许我们配置接收端。让我们来看看这个配置:

如您所见,我们已经声明了将监听的端口,通过该端口将接收从 PACS 发送的图像。我们还配置了本地 AET (Application Entity Title,应用实体名称)(IRIS) 和将向我们发送 DICOM (ORTHANC) 的 PACS,请记住这些字段是强制性的,并且将用于检查发送和接收 DICOM 消息的有效性。不要忘记必须从 DICOM 设置菜单配置和关联两个 AET:

正如你所看到的,我们已经配置了从IRIS到ORTHANC(前一篇文章的案例)和从ORTHANC到IRIS(本文中的案例)的信息发送。确认你已经为这两个关联上下文配置了你在两个系统中发送和接收的DICOM图像类型的演示上下文。

1
0 301
文章 Michael Lei · 五月 5, 2023 5m read

虽然在一项研究中,与医生的回答相比,一些专业人士确实更喜欢ChatGPT的回答,但凯洛格Kellog研究人员表示,现在判断人工智能是否真的能与医生的专业知识和对待病人的态度相媲美还为时过早。

根据本周发表在《美国医学会杂志》(JAMA Internal Medicine)上的一项研究,人工智能聊天机器人助手可以为患者的健康问题提供可以与医生回复相提并论的同等质量而且富有同情心的答复。

一组有执照的医护人员比较了医生和聊天机器人2022年10月在社交媒体论坛Reddit的r/AskDocs上公开提出的病人问题的回答,到了12月下旬他们被ChatGPT的答复征服了。

研究作者称:"聊天机器人的回答比医生的回答更受欢迎,而且在质量和同情心方面的评价都明显更高。“

加州大学圣地亚哥分校高通研究所的约翰-艾尔斯(John Ayers)撰写的《比较医生和人工智能聊天机器人对发布在公共社交媒体论坛上的患者问题的回应》的研究新闻很快被大量报道,其中充满了决定性的动词,如 "超越"、"击败 "或 "战胜",医生们上了头条,而ChatGPT成为了胜利者。

但其他生成型人工智能学者并不确定ChatGPT的同理心是否能取代医生的同理心。

医生是如何对待回答社交媒体上的问题的?

0
0 121
文章 Weiwei Gu · 五月 4, 2023 11m read

我们客户的一个共同需求是配置 HealthShare HealthConnect 和 IRIS的高可用性模式。

市场上的其他集成引擎通常被宣传为具有“高可用性”配置,但事实并非如此。通常,这些解决方案与外部数据库一起使用,因此,如果这些数据库未配置为高可用性,当发生数据库崩溃或与它的连接丢失时,整个集成工具将变得不可用。

对于 InterSystems 解决方案,这个问题不存在,因为数据库是工具本身的一部分和核心。 InterSystems 如何解决高可用性问题?深奥的配置会把我们拖入异化和疯狂的漩涡?不!在 InterSystems,我们倾听并处理了您的投诉(正如我们一直努力做的那样 ;)),并且我们已将镜像功能提供给所有用户和开发人员。

镜像

镜像如何工作?这个概念本身非常简单。如您所知,IRIS 和 HealthShare 都使用一个日志系统,该系统记录每个实例的数据库上的所有更新操作。这个日志系统是后来帮助我们在崩溃后恢复实例而不会丢失数据的系统。好吧,这些日志文件在镜像中配置的实例之间发送,允许并保持镜像中配置的实例永久更新。

架构

让我们简要解释一下在 Mirror 中配置的系统架构是什么样的:

0
0 429
文章 Lilian Huang · 四月 28, 2023 8m read

     

嗨社区,
在本文中,我将演示 InterSystems Embedded Python 的用法,我们将涵盖以下主题:

  • 1-嵌入式Python概述
  • 2-嵌入式Python的使用
    • 2.1- 从 ObjectScript 使用 Python 库
    • 2.2- 从 Python 调用 InterSystems API
    • 2.3- 一起使用 ObjectScript 和 Python
  • 3-使用python内置函数
  • 4-Python 模块/库
  • 5 个嵌入式 Python 用例
  • 6-总结

我们从概述开始
 

1-嵌入式Python概述

嵌入式 Python 是 InterSystems IRIS 数据平台的一项功能,它允许 Python 开发人员完全直接地访问 InterSystems IRIS 中的数据和功能。

InterSystems IRIS 带有一种名为 ObjectScript 的强大内置编程语言,可在数据平台内部进行解释、编译和运行。

0
0 275
InterSystems 官方 Claire Zheng · 四月 27, 2023

InterSystems 已修复了一个缺陷,该缺陷可能会导致使用 IBM POWER8 或更高版本的 POWER 处理器的 AIX 系统上的数据库和Journal日志文件损坏。只有在使用数据库或Journal日志加密时才会触发此缺陷。

当满足以下条件时,会触发此缺陷:

如果环境满足所有这些条件,则用于数据库或Journal日志加密的密钥计划可能会被损坏。

该缺陷存在于以下产品和基于这些产品的任何 InterSystems 产品中:

  • InterSystems IRIS ®除 2022.1.3 和 2023.1 之外的所有版本
  • InterSystems IRIS for Health ——除 2022.1.3 和 2023.1 之外的所有版本
  • HealthShare ® Health Connect 除 2022.1.3 和 2023.1 之外的所有版本
  • HealthShare ®解决方案——所有版本

请注意,此缺陷不会直接影响 HealthShare 解决方案,因为它们不使用数据元素加密功能。但是,如果 HealthShare 环境有一个使用上述数据元素加密功能的客户化定制,则它们必须遵循此警报中提供的补救措施。

0
0 181
InterSystems 官方 Claire Zheng · 四月 10, 2023

InterSystems已修复一个缺陷,该缺陷在罕见情况下会导致ECP客户端不稳定。

该缺陷存在于以下产品和基于这些产品的任何InterSystems产品中。

受影响的版本是2022.1.x、2022.2和2022.3:

InterSystems IRIS®

InterSystems IRIS for Health™

HealthShare® Health Connect

受影响的版本是2022.2(只针对部署ECP的客户):

InterSystems HealthShare®

这个问题只发生在ECP客户端系统中。当这个问题被触发时,进程可能遇到<SYSTEM>或<DATABASE>错误。出错后,ECP客户端会出现不稳定;在某些情况下,实例可能会挂起。对数据没有影响,ECP数据库服务器不受影响。

要清除不稳定性,必须重新启动ECP客户端实例。

如果您的 IRIS、IRIS for Health 或 Health Connect 实例受到影响,补救措施是升级到版本 2023.1 或 2022.1.3,这两个版本都将很快发布。

如果您有受影响的 HealthShare 实例,一组单独的警报文档和补救建议将很快发布

0
0 195
公告 Claire Zheng · 四月 10, 2023

2023年4月14日-16日,2023 CHITEC将在安徽省合肥市合肥滨湖国际会展中心(安徽省合肥市锦绣大道3899号)盛大召开。点击查看CHITEC 2023完整日程。InterSystems将亮相CHITEC,通过展位展示、主题研讨会、分论坛演讲等方式,全方位多角度展示InterSystems加速评测,助力公立医院高效建设互联互通平台和数据中心的能力。

0
0 395
文章 姚 鑫 · 四月 8, 2023 3m read

第二十二章 配置镜像 - 镜像配置指导原则

为了提供强大、经济的HA解决方案,镜像设计为可适应各种系统配置和体系结构。但是,建议遵循以下一般配置准则:

  • IRIS实例和平台兼容性-在确定要添加到镜像的系统之前,请务必查看IRIS实例兼容性和成员字符顺序注意事项中描述的要求。
  • 故障转移成员相等-假定镜像中的两个故障转移成员相等。无法将首选项配置为主角色,并且根据情况需要将主角色和备份角色互换。因此,最佳做法是使故障转移系统主机彼此尽可能相似,特别是使用相似的计算资源进行配置;即,两个系统上的CPU和内存配置以及磁盘配置应具有可比性。
  • 主实例配置和安全设置-主故障转移成员上的用户、角色、命名空间和映射(包括全局映射和包映射)等元素的配置不会被其他镜像成员上的镜像复制。因此,必须在备份故障切换成员或灾难恢复异步成员上手动复制所有设置,并根据需要进行更新,才能使备份故障切换成员或灾难恢复异步成员有效地从主成员接管。
  • 未镜像数据-仅在主故障转移成员上的镜像数据库中的数据在备份故障转移成员和异步成员上复制和同步。因此,使备份或灾难恢复异步有效地接管主服务器所需的任何文件(例如,包括与SQL网关和Web服务器配置相关的文件)都必须手动复制到这些成员,并根据需要进行更新。

注:镜像数据库的文件流(默认情况下位于数据库目录的Stream子目录中)不会被镜像。

  • ICMP-不要在配置为镜像成员的任何系统上禁用Internet控制消息协议(ICMP);镜像依赖ICMP来检测成员是否可访问。
  • 网络-建议在两个故障转移成员之间使用高带宽、低延迟、可靠的网络。如果可能,最好为两个故障转移成员创建一个专用子网,以便数据和控制通道流量可以在此专用网络上以独占方式进行路由。缓慢的网络可能会影响主故障切换成员和备份故障切换成员的性能,并可能直接影响备份故障切换成员在发生故障切换时作为主故障切换成员接管的能力。有关网络要求和配置的进一步讨论,请参阅网络配置注意事项和网络延迟注意事项。
  • 磁盘子系统-为了使备份故障切换成员与主系统保持一致,两个故障切换成员上的磁盘子系统应该具有可比性;例如,如果在第一个故障切换成员上配置存储阵列,则建议在第二个故障切换成员上配置类似的存储阵列。此外,如果在一个或两个系统上使用网络连接存储(NAS),强烈建议为镜像数据中的磁盘I/O和网络负载配置单独的网络链路,以最大限度地减少网络负担过重的可能性。
  • 日志记录性能和日志存储-由于日志记录/取消日志记录是镜像同步的核心,因此监视和优化故障切换成员上的日志记录性能至关重要。建议增加所有镜像成员上的共享内存堆(Gmheap)大小。出于性能和可恢复性的考虑,还建议将主日志目录和备用日志目录放置在存储设备上,这些存储设备与数据库使用的设备和写入映像日志(WIJ)分开,并且彼此分开。
  • 虚拟化——虽然在虚拟化环境中使用镜像提供了一种结合了两者优点的混合高可用性解决方案,但重要的建议适用;有关详细信息,请参阅虚拟化环境中的镜像。
  • 任务调度——当使用任务管理器在镜像成员上创建任务时,必须指定该任务是否只能在主成员、主成员以外的任何成员或任何镜像成员上运行。打算在多个镜像成员上运行的任务必须在成员上单独创建,或者从一个成员上的任务管理器导出并在其他成员上导入。
  • 启动 — 在主故障转移成员上,可能希望将代码从现有的 ^%ZSTART 例程移动到 ^ZMIRROR 例程,以便在初始化镜像之前不执行它。
0
0 101
InterSystems 官方 Claire Zheng · 四月 6, 2023

InterSystems 已修复一个导致SQL查询返回不正确结果的缺陷。

该缺陷存在于以下产品和基于这些产品的任何InterSystems产品中。

受影响的版本是 2021.2、2022.1.x、2022.2 和 2022.3:

  • InterSystems IRIS®
  • InterSystems IRIS for Health™(医疗版)
  • HealthShare®Health Connect

受影响的版本是 2022.2:

  • InterSystems HealthShare®

 当启用 SQL 运行时计划选择 (RTPC) 被启用(默认),并且查询包含一个“truth value”“WHERE ?=? 时,该缺陷会被触发,当被触发时,一些判断可能不会被正确评估;这导致了不正确的查询结果。

注意:通过查看SQL语句,无法完全评估一个查询的脆弱性。这是因为InterSystems SQL查询优化可以在查询的内部表示中增加“truth value”。

如果你的环境使用 InterSystems SQL,那么你可以通过禁用RTPC 功能来补救这个问题

注意:关于 InterSystems HealthShare® 缓解措施的其他信息将很快发布。

0
0 149
文章 Kelly Huang · 三月 28, 2023 1m read

嗨大家好!

最近我需要使用 IRIS For Health 设置本地 FHIR 服务器,我认为我找到了有史以来最简单的方法!

只需在终端中运行以下两行:

docker run --rm --name my-iris -d --publish 9091:1972 --publish 9092:52773 intersystemsdc/irishealth-community

docker exec -it my-iris iris session iris -U "USER" '##class(%ZPM.PackageManager).Shell("install fhir-server")'

您将在 http://localhost:9092/fhir/r4 本地运行 FHIR 服务器。

就是这么简单!

FHIR 服务器将使用最新版本的 InterSystems IRIS for Health Community Edition,并将通过 FHIRSERVER 命名空间中的 IPM 包从该应用程序部署 FHIR 服务器

这是针对 Mac OS的,所以请在评论中添加它在 Windows 中的工作方式。

这是一篇非常短的文章,因为使用 InterSystems IRIS for Health 和IPM Package Manager 设置本地 FHIR 服务器真的很容易。

0
0 255
文章 姚 鑫 · 三月 23, 2023 24m read

简介

什么是Query

Query是一种查询方法,用于查找满足条件的数据,将结果以数据集的形式展现出来。

Query类别

  • SQL Query,使用类 %SQLQuerySQL SELECT 语句。
  • 自定义Query,使用类 %Query 和自定义逻辑生成查询数据。

说明:在讲通用Query解决方案之前,我们先了解一下Query的基础和基础使用,有助于理解实现原理。如果读者了解Query基本使用,可跳过此章节,直接阅读“现状”。

Query基本使用

SQL Query基本使用

Query QueryPersonByName(name As %String = "") As %SQLQuery(COMPILEMODE = "IMMEDIATE", CONTAINID = 1, ROWSPEC = "id:%Integer:ID,MT_Name:%String:name,age:%String,no:%String", SELECTMODE = "RUNTIME") [ SqlName = QueryPersonByName, SqlProc ]
{
	SELECT top 10 ID, MT_Age, MT_Name, MT_No
	FROM M_T.Person
	WHERE (MT_Name %STARTSWITH :name)
	ORDER BY id
}

说明:

  • Query - 声明Query方法关键字。

  • QueryPersonByName - Query方法的名称。

  • name As %String = "" - Query方法的参数。

  • %SQLQuery - Query类型为%SQLQuery

    • %SQLQuery%Query的子类,使用Query的简单的形式,可在方法体内直接编写Select SQL语句。
  • COMPILEMODE - 为%SQLQuery的参数,表示编译方式。

    • IMMEDIATE - 立即编译,当检测当前SQL语句是否正确。
    • DYNAMIC - 动态编译 ,在运行时在编译SQL语句。
  • CONTAINID - 置为返回 ID 的列的编号。

    • 1 - 返回ID列。
    • 0 - 不返回。
  • SELECTMODE - 表示显示方式。

    • RUNTIME - 无
    • ODBC - 以ODBC方式显示数据。
    • DISPLAY - 以显示方式显示数据。
    • LOGICAL - 以逻辑方式显示数据。
  • ROWSPEC - 提供数据列名称、数据类型、描述。用引号和逗号分隔的变量名和数据类型列表。格式如下:

    • ROWSPEC = "id:%Integer:ID,age:%String,MT_Name:%String:name,no:%String"
      
    • id - 表示数据列名称。

    • %Integer - 表示数据类型。

    • ID - 数据描述。

  • SqlProc - 表示该方法可作为存储过程调用。

  • SqlName - 调用的存储过程名称。

    • 无声明调用方式 - call M.Query_QueryPersonByName()
    • 声明调用方式 - call M.QueryPersonByName()

image

  • 运行Query方法 - d ##class(%ResultSet).RunQuery(className, queryName, arg...)
USER>d ##class(%ResultSet).RunQuery("M.Query", "QueryPersonByName")
 
ID:age:name:no:
1:21:yaoxin:314629:
2:29:yx:685381:
3:18:Umansky,Josephine Q.:419268:
4:27:Pape,Ted F.:241661:
5:25:Russell,Howard T.:873214:
6:30:Xenia,Ashley U.:420471:
7:24:Rotterman,Martin O.:578867:
8:18:Drabek,Hannah X.:662167:
9:19:Eno,Mark U.:913628:
11:18:Tsatsulin,Dan Z.:920134:

自定义Query基本使用

在使用自定义Query时,一般都遵循固定的模版。在同一个类中定义以下类方法:

  • QueryName - 在Query方法类型指定 %Query
  • QueryNameExecute — 此方法主要编写获取数据的业务逻辑,得到数据集。
  • QueryNameFetch — 此方法遍历数据集。
  • QueryNameClose — 此方法删除临时数据或对象。

说明:下面示例展示常用“套路”的自定义Query模版,此模版仅仅是常用的一种,并非是固定写法。


定义QueryName

Query QueryPersonByAge(pAge As %String = "", count As %Integer = "10") As %Query(ROWSPEC = "id:%Integer:ID,MT_Name:%String:name,age:%String,no:%String")
{
}

定义名为QueryPersonByAgeQuery类型指定为%Query。并将查询定义的主体留空。


定义QueryNameExecute

ClassMethod QueryPersonByAgeExecute(ByRef qHandle As %Binary, pAge As %String = "", count As %Integer = "10") As %Status
{
	s pid = $i(^CacheTemp) // 注释1
	s qHandle = $lb(0, pid, 0) // 注释2
	s index = 1 // 注释3
	
	/* 业务逻辑代码 注释4 */ 
	s id = ""
	for {
		s id = $o(^M.T.PersonD(id))
		q:(id = "")
		q:(id > count)
		s data = ^M.T.PersonD(id)
		s i = 1
		s name = $lg(data, $i(i))
		s age = $lg(data, $i(i))
		continue:(age < pAge)
		s no = $lg(data, $i(i))
		d output
	}	
	/* 业务逻辑代码 */

	q $$$OK
	
output
	s ^CacheTemp(pid, index) = $lb(id, age, name, no) // 注释6 
	s index = index + 1 // 注释7
}

QueryNameExecute() 方法提供所有需要的业务逻辑。方法的名称必须是 QueryNameExecute() ,其中 QueryName是定义Query的名称。

其中:

  • qHandle - 用于与实现此查询的其他方法进行通信。qHandle 可以为任何类型。默认为%Binary
  • pAge As %String = "", count As %Integer = "10"Query传入参数,可作为业务逻辑的条件使用。
  • 注释1处代码,s pid = $i(^CacheTemp) - 获取pid
  • 注释2处代码,s qHandle = $lb(0, pid, 0) - 数组内第一个元素0表示循环的开始,第二个元素pid用于获取^CacheTemp数据,第三个元素0用于遍历^CacheTemp起始节点。
  • 业务逻辑代码 - 为获取数据集的主要实现逻辑。
  • 注释3处代码与注释7处代码,为^CacheTemp增加索引节点。
  • 注释6处代码,s ^CacheTemp(pid, index) = $lb(id, name, age, no) - 为^CacheTemp赋值为后续遍历使用。
    • 这里数据格式为%Library.List形式,这样Fetch方法就不用转类型了,否则Fetch方法还需要将数据转为内部列表格式。

定义QueryNameFetch

ClassMethod QueryPersonByAgeFetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ PlaceAfter = QueryPersonByAgeExecute ]
{
	s end = $li(qHandle, 1) // 注释1
	s pid = $li(qHandle, 2)
	s index = $li(qHandle, 3)
	s index = $o(^CacheTemp(pid, index)) // 注释2
	if index = "" {  // 注释3
		s end = 1
		s row = ""
	} else { 
		s row = ^CacheTemp(pid, index)
	}
	s qHandle = $lb(end, pid, index) // 注释4
	q $$$OK
}

QueryNameFetch() 方法必须以 %Library.List 格式返回单行数据。方法的名称必须是 QueryNameFetch,其中 QueryName是定义Query的名称。

其中:

  • qHandle - 用于与实现此查询的其他方法进行通信。它的值应该是Execute定义的值。
  • row - 表示要返回的一行数据的值类型为 %Library.List,如果没有返回数据则为空字符串。
  • end - 当到达最后一行数据时,end必须为 1。如果不指定为1,则会无限循环。
  • PlaceAfter - PlaceAfter方法关键字控制此方法在生成代码中顺序。这里表示在方法QueryPersonByAgeExecute生成之后在生成QueryPersonByAgeFetch方法。
  • 注释1处代码, 1~3 行,解析qHandle数组的值获取endpidindex
  • 注释2处代码,s index = $o(^CacheTemp(pid, index)) 根据解析到的pidindex开始遍历。
  • 注释3处代码,将遍历的^CacheTemp(pid, index)每行属于赋值给row,如果index为空,则一定要将end赋值为1
  • 注释4处代码,s qHandle = $lb(end, pid, index)将取到的endindex重新复制给qHandle为取下一行数据做准备。

注:Fetch方法为多次执行,有多少行数据就遍历多少遍。ExecuteClose方法为一次执行。


定义QueryNameClose

ClassMethod QueryPersonByAgeClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = QueryPersonByAgeExecute ]
{
	s pid = $li(qHandle, 2) // 注释1
	k ^CacheTemp(pid) // 注释2
	q $$$OK
}

QueryNameClose() 方法在数据检索完成后删除清理临时数据或对象等结束收尾工作。方法的名称必须是 QueryNameClose() ,其中 QueryName是定义Query的名称。

  • qHandle - 用于与实现此查询的其他方法进行通信。
  • 注释1处代码,获取qHandle 保存的pid
  • 注释2处代码,清除临时生成的^CacheTemp

调用自定义Query

USER> d ##class(%ResultSet).RunQuery("M.Query", "QueryPersonByAge","20")
 
ID:name:age:no:
1:yaoxin:21:314629:
2:yx:29:685381:
4:Pape,Ted F.:27:241661:
5:Russell,Howard T.:25:873214:
6:Xenia,Ashley U.:30:420471:
7:Rotterman,Martin O.:24:578867:
  • 这里查询是年龄大于20岁并且id小于10的所有人员信息。

现状

上面2Query的基本使用示例,可能是大家最常用的两种方式。

但是经常写查询或者写报表的同学可能会面临如下几个问题:

  1. 每次写Query都需要定义列头ROWSPEC很麻烦,是否可以自己指定列头ROWSPEC
  2. 现在很多方法返回的值是JSON,如何将JSON方法快速转成Query
  3. 是否可以写一个通用Query,只需要写Execute主要逻辑即可?
  4. 是否可以优化现在的模版,例如^CacheTemp替换成^||CacheTemp

以上的问题,都是可以解决的,请继续阅读下面文章部分。

方案

如果想实现通用Query还得需要知道一个回调方法QueryNameGetInfo

ClassMethod Json2QueryGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, extinfo As %List) As %Status
{
	q $$$OK
}

其中:

  • colinfo - 此参数最关键用于定义ROWSPEC列头部分。为在 ROWSPEC 中声明的每一列包含一个列表元素。形式为 name:exttype:caption
    • name - 为列头名称。
    • exttype - 为数据类型。
    • caption - 为描述说明。
  • colinfo 类型必须是%Library.List,定义的列头的类型也是%Library.List

例如:

ClassMethod QueryPersonByAgeGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef %qHandle As %Binary, extoption As %Integer = 0, extinfo As %List) As %Status
{
	s colinfo = $lb($lb("id", "%Integer", "ID"), $lb("age", "%String", ""), $lb("MT_Name", "%String", "name"), $lb("no", "%String", ""))
	s parminfo = ""
	s idinfo = ""
	q $$$OK
}

说明:Query执行顺序 Execute -> GetInfo -> Fetch(n) -> Close

下面分别描述以下几种解决方案:

  • 通过Json数据或方法动态生成Query
  • 通过Select Sql语句动态生成Query
  • 通过Query动态生成Query
  • 支持传统的Query并通过参数形式生成Query列
  • 定义通用Query,只需要实现Exceute方法

通过Json数据或方法动态生成Query


定义Json方法

  • Json方法可任意定义,此示例仅为了测试使用。如下方法:查询当前电脑盘符以Json结果输出。
/// desc:查询盘符
ClassMethod QueryDrives(fullyQualified = 1, type = "D")
{
	s array = []
	s rs = ##class(%ResultSet).%New()
	s rs.ClassName = "%File"
	s rs.QueryName = "DriveList"
	d rs.Execute(fullyQualified)
	while (rs.Next()) {
		s drive = rs.Get("Drive")
		s drive = $zcvt(drive, "U")
		s obj = {}
		s obj.type = "D"
		continue:(type '= "D")
		s obj.drive = drive
		d array.%Push(obj)
	}
	q array
}

运行:

USER> w ##class(M.Query).QueryDrives().%ToJSON()
[{"type":"D","drive":"C:\\"},{"type":"D","drive":"D:\\"},{"type":"D","drive":"E:\\"},{"type":"D","drive":"F:\\"},{"type":"D","drive":"G:\\"}]

定义QueryName

Query Json2Query(className As %String, methodName As %String, arg...) As %Query
{
}

其中:

  • className - 类名。
  • methodName - 需要执行的Json方法名称。
  • arg... - 需要执行的方法参数。

定义QueryNameExecute

ClassMethod Json2QueryExecute(ByRef qHandle As %Binary, className As %String, methodName As %String, arg...) As %Status
{
	s array = $classmethod(className, methodName, arg...) // 注释1
	if ('$isobject(array)) { // 注释2
		s array = [].%FromJSON(array)
	}
	q:('array.%IsA("%Library.DynamicArray")) $$$ERROR($$$GeneralError, "不是数组对象") // 注释3
	q:(array.%Size() = 0) $$$ERROR($$$GeneralError, "没有数据") // 注释4
    s qHandle = array // 注释5
    q $$$OK
}
  • 注释1代码,利用反射机制调用目标方法并获取返回值。
  • 注释2代码,判断如果返回的字符串则转成Json对象。
  • 注释3代码,判断该对象不是%Library.DynamicArray抛出错误信息。
  • 注释4代码,Json数组长度为0抛出错误信息。
  • 注释5代码,获取数组对象。

定义QueryNameGetInfo

ClassMethod Json2QueryGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, extinfo As %List) As %Status
{
	s colinfo = $lb() // 注释1
	s count = 1 
	s obj = qHandle.%GetIterator()
	if obj.%GetNext(.key, .value) { 
		s obj = value.%GetIterator() 
		while obj.%GetNext(.objKey, .objValue) { // 注释2
			s $li(colinfo, count) = $lb(objKey) 
			s count = $i(count) 
		}
	}	
	s parminfo = "" // 注释3
	s idinfo = "" // 注释4
	s qHandle = qHandle.%GetIterator() // 注释5
	q $$$OK
}
  • 注释1代码,初始化colinfo数组,将obj赋值qHandle.%GetIterator()迭代器对象。
  • 注释2代码,遍历Json对象获取Key,并通过$licolinfo赋值。
  • 注释3代码,初始化parminfo,否则报错。
  • 注释4代码,初始化idinfo,否则报错。
  • 注释5代码,获取的迭代器对象

定义QueryNameFetch

ClassMethod Json2QueryFetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ PlaceAfter = Json2QueryExecute ]
{
  	s iter = qHandle
  	q:($g(iter) = "") $$$OK
    if iter.%GetNext(.key, .value) { // 注释1
		s row = ""
  		s obj = value.%GetIterator()
  		while obj.%GetNext(.objKey, .objValue) { // 注释2
			if ( $g(row) = "" ) {
				s row = $lb(objValue)
			} else {
				s row = row _ $lb(objValue)
			}
  		}
	  	s end = 0
    } else {
		s row = "" 
		s end = 1 // 注释3
	}
    q $$$OK
}
  • 注释1代码,获取当前迭代器Json数据行。
  • 注释2代码,遍历当前Json对象并把valuerow进行$lb串联。
  • 注释3代码,如果没有数据设置end1表示遍历结束。

定义QueryNameClose

ClassMethod Json2QueryClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = Json2QueryFetch ]
{
    s qHandle = "" // 注释1
    q $$$OK
}
  • 注释1代码,将对象qHandle清空。

注:其实M有相关回收机制,实际上Close方法不声明也可以。


调用Json2Query方法

USER>d ##class(%ResultSet).RunQuery("M.Query","Json2Query","M.Query","QueryDrives","0","D")
 
type:drive:
D:D::
D:E::
D:F::
D:G::

USER>d ##class(%ResultSet).RunQuery("M.Query","Json2Query","M.Query","QueryDrives","1","D")
 
type:drive:
D:D:\:
D:E:\:
D:F:\:
D:G:\:

通过Select Sql语句动态生成Query


定义QueryName

Query Sql2Query(sql As %String, mode As %String = 1) As %Query
{
}
  • sql - 表述需要写入SQL语句的变量。
  • mode - 显示数据格式类型。
    • 0 - 逻辑格式
    • 1 - OBDC 格式
    • 2 - 显示格式

定义QueryNameExecute

ClassMethod Sql2QueryExecute(ByRef qHandle As %Binary, sql As %String, mode As %String = 1) As %Status
{
 	s sqlStatement = ##class(%SQL.Statement).%New()
    s sqlStatement.%SelectMode = mode // 注释1
    s sqlStatus = sqlStatement.%Prepare(.sql) // 注释2
    q:$$$ISERR(sqlStatus) sqlStatus
    s sqlResult = sqlStatement.%Execute() 
	s stateType = sqlStatement.%Metadata.statementType
	q:('stateType = 1 ) $$$ERROR($$$GeneralError, "不是select语句") // 注释3
	s qHandle = {}
	s qHandle.sqlResult = sqlResult // 注释4
	s qHandle.sqlStatement = sqlStatement 
    q $$$OK
}
  • 注释1代码,设置SQL的数据显示格式。
  • 注释2代码,传入SQL语句得到sqlStatementsqlResult对象。
  • 注释3代码,传入的SQLSelect语句,抛出错误信息。
  • 注释4代码,将qHandle传入两个对象分别是sqlResultsqlStatement
    • sqlResult用于遍历数据使用。
    • sqlStatement用于得到数据列头信息。

定义QueryNameGetInfo

ClassMethod Sql2QueryGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, extinfo As %List) As %Status
{
	s colinfo = $lb()
	s sqlStatement = qHandle.sqlStatement // 注释1
	s count = 1
   	for i = 1 : 1 : sqlStatement.%Metadata.columnCount {	
		s data = sqlStatement.%Metadata.columns.GetAt(i).label
		s $li(colinfo, count) = $lb(data) // 注释2
		s count = $i(count)
	}
	s parminfo = ""
	s idinfo = ""
	q $$$OK
}
  • 注释1代码,通过qHandle得到sqlStatement对象。
  • 注释2代码,给colinfo列表进行循环赋值列头信息,

定义QueryNameFetch

ClassMethod Sql2QueryFetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ PlaceAfter = Sql2QueryExecute ]
{
	s sqlStatement = qHandle.sqlStatement // 注释1
	s sqlResult =  qHandle.sqlResult 
	s colCount = sqlResult.%ResultColumnCount // 注释2
    if (sqlResult.%Next()) {
        for i = 1 : 1 : colCount{
            s val = sqlResult.%GetData(i)
			if ( $g(row) = "" ) { // 注释3
				s row = $lb(val)
			} else {
				s row = row _ $lb(val)
			}
        }
        s end = 0 
    } else {
	   s row = ""
	   s end = 1
	}
	s qHandle.sqlResult = sqlResult // 注释4
    q $$$OK
}
  • 注释1代码,通过qHandle得到sqlStatementsqlResult对象。
  • 注释2代码,得到列数,相当于得到一行数据有多少项。
  • 注释3代码,遍历数据给row赋值。
  • 注释4代码,将qHandle.sqlResult对象,赋值给循环当前对象。

定义QueryNameClose

此处省略。

注:其实M有相关回收机制,实际上Close方法不声明也可以。


调用Sql2Query方法

USER>d ##class(%ResultSet).RunQuery("M.Query","Sql2Query","select * from M_T.Person", 1)
 
id:MT_Age:MT_Name:MT_No:
1:21:yaoxin:314629:
2:29:yx:685381:
3:18:Umansky,Josephine Q.:419268:
4:27:Pape,Ted F.:241661:
5:25:Russell,Howard T.:873214:
6:30:Xenia,Ashley U.:420471:
7:24:Rotterman,Martin O.:578867:
8:18:Drabek,Hannah X.:662167:
9:19:Eno,Mark U.:913628:
...
100:24:Nathanson,Jocelyn A.:147578:
USER>d ##class(%ResultSet).RunQuery("M.Query","Sql2Query","select ID,MT_Name from M_T.Person")
 
id:MT_Name:
1:yaoxin:
2:yx:
3:Umansky,Josephine Q.:
4:Pape,Ted F.:
5:Russell,Howard T.:
6:Xenia,Ashley U.:
7:Rotterman,Martin O.:
...
100:Nathanson,Jocelyn A.:
USER>d ##class(%ResultSet).RunQuery("M.Query","Sql2Query","select top 10 ID as id from M_T.Person")
 
id:
1:
2:
3:
4:
5:
6:
7:
8:
9:
11:

通过Query生成动态Query


定义QueryName

Query Query2Query(className As %String, queryName As %String, arg...) As %Query
{
}
  • className - 类名。
  • queryName - 需要执行的Query方法名称。
  • arg... - 需要执行的Query方法参数。

定义QueryNameExecute

ClassMethod Query2QueryExecute(ByRef qHandle As %Binary, className As %String, queryName As %String, arg...) As %Status
{
 	s sqlStatement = ##class(%SQL.Statement).%New()
 	s sqlStatus = sqlStatement.%PrepareClassQuery(className, queryName)
    q:$$$ISERR(sqlStatus) sqlStatus
    s sqlResult = sqlStatement.%Execute() 
	s qHandle = {}
	s qHandle.sqlResult = sqlResult
	s qHandle.sqlStatement = sqlStatement
    q $$$OK
}
  • Sql2Query类似。

定义QueryNameGetInfo

ClassMethod Query2QueryGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, extinfo As %List) As %Status
{
	s colinfo = $lb()
	s sqlStatement = qHandle.sqlStatement
	s count = 1
    s column = ""
   	for {
		s column = $o(sqlStatement.%Metadata.columnIndex(column)) 
		q:(column = "")
		s data = sqlStatement.%Metadata.columnIndex(column)
		s $li(colinfo, count) = $lb($lg(data, 2))
		s count = $i(count)
	}
	s parminfo = ""
	s idinfo = ""
	q $$$OK
}
  • Sql2Query类似。

定义QueryNameFetch

ClassMethod Query2QueryFetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ PlaceAfter = Query2QueryExecute ]
{
	s sqlStatement = qHandle.sqlStatement
	s sqlResult =  qHandle.sqlResult
	s colCount = sqlResult.%ResultColumnCount
    if (sqlResult.%Next()) {
        for i = 1 : 1 : colCount{
            s val = sqlResult.%GetData(i)
			if ( $g(row) = "" ) {
				s row = $lb(val)
			} else {
				s row = row _ $lb(val)
			}
        }
        s end = 0 
    } else {
	   s row = ""
	   s end = 1
	}
	s qHandle.sqlResult = sqlResult
    q $$$OK
}
  • Sql2Query类似。

调用Query2Query

USER>d ##class(%ResultSet).RunQuery("M.Query","Query2Query","M.Query","QueryPersonByName")
 
age:id:MT_Name:no:
1:21:yaoxin:314629:
2:29:yx:685381:
3:18:Umansky,Josephine Q.:419268:
4:27:Pape,Ted F.:241661:
5:25:Russell,Howard T.:873214:
6:30:Xenia,Ashley U.:420471:
7:24:Rotterman,Martin O.:578867:
8:18:Drabek,Hannah X.:662167:
9:19:Eno,Mark U.:913628:
11:18:Tsatsulin,Dan Z.:920134:

支持传统的Query并通过参数形式生成Query

  • 支持传统的Query形式。
  • 支持通过参数形式定义列,不需要指定ROWSPEC参数。
  • 优化将^CacheTemp^||CacheTemp。

定义M.CommonQuery

Class M.CommonQuery Extends %Query
{

ClassMethod Close(ByRef qHandle As %Binary) As %Status [ CodeMode = generator, PlaceAfter = Execute, ProcedureBlock = 1, ServerOnly = 1 ]
{
	s %code($i(%code))= (" s pid = $li(qHandle, 2)")
	s %code($i(%code))= (" k ^||GlobalTemp(pid)")
	s %code($i(%code))= (" q $$$OK")
	q $$$OK
}

ClassMethod Fetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ CodeMode = generator, PlaceAfter = Execute, ProcedureBlock = 1, ServerOnly = 1 ]
{
	s %code($i(%code))= (" s end = $li(qHandle, 1)")
	s %code($i(%code))= (" s pid = $li(qHandle, 2)")
	s %code($i(%code))= (" s ind = $li(qHandle, 3)")
	s %code($i(%code))= (" s ind = $o(^||GlobalTemp(pid, ind))")
	s %code($i(%code))= (" if (ind = """") { ")
	s %code($i(%code))= (" 	s end = 1")
	s %code($i(%code))= (" 	s row = """"")
	s %code($i(%code))= (" } else { ")
	s %code($i(%code))= (" 	s row = ^||GlobalTemp(pid, ind)")
	s %code($i(%code))= (" }")
	s %code($i(%code))= (" s qHandle = $lb(end, pid, ind)")
	s %code($i(%code))= (" q $$$OK")
	q $$$OK
}

ClassMethod GetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, ByRef extinfo As %List) As %Status [ CodeMode = generator, ServerOnly = 1 ]
{
	s %code($i(%code))= (" s colinfo = $lb()")
	s %code($i(%code))= (" s column = $lg(qHandle, 4)")
	s %code($i(%code))= (" if ($lv(column)) {")
	s %code($i(%code))= (" 	for i = 1 : 1 : $ll(column) {")
	s %code($i(%code))= (" 		s $li(colinfo, i) = $lb(""Column"" _ i )")
	s %code($i(%code))= (" 	}	")
	s %code($i(%code))= (" } else {")
	s %code($i(%code))= (" 	s len = $l(column, "","")")
	s %code($i(%code))= (" 	for i = 1 : 1 : len {")
	s %code($i(%code))= (" 		s $li(colinfo, i) = $lb($p(column, "","", i))")
	s %code($i(%code))= (" 	}")
	s %code($i(%code))= (" }")
	s %code($i(%code))= (" s parminfo = """"")
	s %code($i(%code))= (" s idinfo = """"")
	s %code($i(%code))= (" q $$$OK")
	q $$$OK
}

}



定义QueryName

Query CustomColumnQuery(column As %String = "") As M.CommonQuery
{
}
  • column - 表示要自定义参数列的变量。
  • M.CommonQuery - 自定义Query类型,不需要写GetInfoFetchClose方法。

定义QueryNameExecute

QueryNameExecute 支持三种定义列头方式:

  1. 通过column参数传入列头,实现如下:
ClassMethod CustomColumnQueryExecute(ByRef qHandle As %Binary, column As %List) As %Status
{
	s pid = $i(^||GlobalTemp)
	s qHandle = $lb(0, pid, 0)
	s $li(qHandle, 4) = column // 方式1此位置必填
	s ind = 1
	
	s id = ""
	for {
		s id = $o(^M.T.PersonD(id))
		q:(id = "")
		s data = ^M.T.PersonD(id)
		s i = 1
		s name = $lg(data, $i(i))
		s age = $lg(data, $i(i))
		s no = $lg(data, $i(i))
		d output
	}	
	
    q $$$OK
    
output
	s data = $lb(id, name)
	s ^||GlobalTemp(pid, ind)=data	
	s ind = ind + 1
}
USER> d ##class(%ResultSet).RunQuery("M.Query","CustomColumnQuery","ID,Name")
 
ID:Name:
1:yaoxin:
2:yx:
3:Umansky,Josephine Q.:
4:Pape,Ted F.:
5:Russell,Howard T.:
  1. 不传入column参数,自动根据列表数据数量生成列头,实现如下:
ClassMethod CustomColumnQueryExecute(ByRef qHandle As %Binary, column As %String = "") As %Status
{
	s pid = $i(^||GlobalTemp)
	s qHandle = $lb(0, pid, 0)
	s ind = 1
	s id = ""
	for {
		s id = $o(^M.T.PersonD(id))
		q:(id = "")
		s data = ^M.T.PersonD(id)
		s i = 1
		s name = $lg(data, $i(i))
		s age = $lg(data, $i(i))
		s no = $lg(data, $i(i))
		s data = $lb(id, name, no)
		q:(id > 5)
		d output
	}	
	s $li(qHandle, 4) = data // 方式2此位置必填
    q $$$OK
    
output
	s ^||GlobalTemp(pid, ind)=data	
	s ind = ind + 1
}
USER>d ##class(%ResultSet).RunQuery("M.Query","CustomColumnQuery")
 
Column1:Column2:Column3:
1:yaoxin:314629:
2:yx:685381:
3:Umansky,Josephine Q.:419268:
4:Pape,Ted F.:241661:
5:Russell,Howard T.:873214:
  1. 不传入column参数,通过Execute方法自定义列头信息,实现如下:
ClassMethod CustomColumnQueryExecute0(ByRef qHandle As %Binary, column As %String = "") As %Status
{
	s pid = $i(^||GlobalTemp)
	s qHandle = $lb(0, pid, 0)
	s ind = 1
	
	s id = ""
	for {
		s id = $o(^M.T.PersonD(id))
		q:(id = "")
		s data = ^M.T.PersonD(id)
		s i = 1
		s name = $lg(data, $i(i))
		s age = $lg(data, $i(i))
		s no = $lg(data, $i(i))
		s data = $lb(id, name, no)
		q:(id > 5)
		d output
	}	 
	s $li(qHandle, 4) = "id,name,age" // 方式3此位置必填
    q $$$OK 
    
output
	s ^||GlobalTemp(pid, ind)=data	
	s ind = ind + 1
}
USER>d ##class(%ResultSet).RunQuery("M.Query","CustomColumnQuery")
 
id:name:age:
1:yaoxin:314629:
2:yx:685381:
3:Umansky,Josephine Q.:419268:
4:Pape,Ted F.:241661:
5:Russell,Howard T.:873214:

定义通用Query,只需要实现Exceute方法

实现通用Query,需要通过抽象方法,子类去重写的方式去实现。所以首先定义父类。

定义M.CommonQuery

Class M.BaseQuery Extends %RegisteredObject
{

/// d ##class(%ResultSet).RunQuery("M.BaseQuery","CustomQuery","id,name")
Query CustomQuery(column As %List, arg...) As %Query
{
}

ClassMethod CustomQueryExecute(ByRef qHandle As %Binary, column As %List, arg...) As %Status
{
	s qHandle = $lb(0, 0) // 注释1
	s $li(qHandle, 3) = column // 注释2
	d ..QueryLogic(arg...) // 注释3
    q $$$OK
}

ClassMethod CustomQueryGetInfo(ByRef colinfo As %List, ByRef parminfo As %List, ByRef idinfo As %List, ByRef qHandle As %Binary, extoption As %Integer = 0, ByRef extinfo As %List) As %Status
{
	s colinfo = $lb()
	s column = $lg(qHandle ,3)
	s len = $l(column, ",") 
	for i = 1 : 1 : len {
	 	s $li(colinfo, i) = $lb($p(column, ",", i)) // 注释5
	}
	s parminfo = ""
	s idinfo = ""
	q $$$OK
}

ClassMethod CustomQueryClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = CustomQueryExecute ]
{
	k %zQueryList // 注释7
	q $$$OK
}

ClassMethod CustomQueryFetch(ByRef qHandle As %Binary, ByRef row As %List, ByRef end As %Integer = 0) As %Status [ PlaceAfter = CustomQueryExecute ]
{
	s end = $li(qHandle,1)
	s index = $li(qHandle,2)
	s index = $o(%zQueryList(index))
	if index = "" {  // 注释6
		s end = 1
		s row = ""
	} else      { 
		s row = %zQueryList(index)
	}
	s qHandle = $lb(end, index)
	Quit $$$OK
}

ClassMethod QueryLogic(arg...) [ Abstract ]
{
	// 注释4
}

}

  • column - 表示要自定义参数列的变量。
  • arg... - 传入的参数。
  • 注释1代码,这里做了一些改变,qHandle只记录了endindex。因为这里如果是全局变量或者进程私有Global只针对当前进程有效,所以pid可省略。
  • 注释2代码,将qHandle第三个位置传入列头名称。
  • 注释3代码,调用待实现的业务逻辑方法,此方法为抽象方法,需要子类去实现。
  • 注释4代码,子类需要实现的具体业务逻辑,得到数据集。
  • 注释5代码,获取到column动态设置列头。
  • 注释6代码,遍历全局变量。
  • 注释7代码,遍历结束后,将全局变量清空。

定义子类M.PersonQuery继承M.BaseQuery实现QueryLogic方法

  • 这里只需要给%zQueryList($i(count))全局变量赋值即可。固定模版已经抽象到父类。
ClassMethod QueryLogic(arg...)
{
	s pName = arg(1)
	s id = ""
	for {
		s id = $o(^M.T.PersonD(id))
		q:(id = "")
		s data = ^M.T.PersonD(id)
		s i = 1
		s name = $lg(data, $i(i))
		continue:(pName '= "")&&(name '= pName)
		s age = $lg(data, $i(i))
		s no = $lg(data, $i(i))
		s %zQueryList($i(count)) = $lb(id, name, age)
	}
}

调用CustomQuery方法

USER>d ##class(%ResultSet).RunQuery("M.PersonQuery","CustomQuery","ID,Name,Age", "yaoxin")
 
ID:Name:Age:
1:yaoxin:21:

注:这里是用的是全局变量作为数据传递,如果数据过大,则可能会出现内存泄漏问题。改成进程私有Global即可。由读者基于此逻辑自行实现。

注:这种方式一个类只能声明一个Query,如果想一个类声明多个Query,则考虑换成支持传统的Query方式。


通过Query生成Json

ClassMethod Query2Json(className, queryName, arg...)
{
	s array = []
	s rs = ##class(%ResultSet).%New()
	s rs.ClassName = className
	s rs.QueryName = queryName
	d rs.Execute(arg...)

	s array = []
	#; 属性值
	while (rs.Next()) {
		s valStr = ""
		s obj = {}
		for i = 1 : 1 : rs.GetColumnCount(){
			s columnName = rs.GetColumnName(i)
			s val = rs.Data(columnName)
			d obj.%Set(columnName, val)
		}
		d array.%Push(obj)
	}
	
	q array.%ToJSON()
}

USER>w ##class(Util.JsonUtils).Query2Json("%SYSTEM.License","Summary")
[{"LicenseUnitUse":"当前使用的软件许可单元 ","Local":"1","Distributed":"1"},{"Li           censeUnitUse":"使用的最大软件许可单元数 ","Local":"15","Distributed":"15"},{"Lic            enseUnitUse":"授权的软件许可单元 ","Local":"300","Distributed":"300"},{"LicenseU         nitUse":"当前连接 ","Local":"3","Distributed":"3"},{"LicenseUnitUse":"最大连接数          ","Local":"17","Distributed":"17"}]

通过Query生成Csv

ClassMethod Query2Csv(className, queryName, filePath, arg...)
{
	s file = ##class(%FileCharacterStream).%New()
	s file.Filename = filePath
	
	s array = []
	s rs = ##class(%ResultSet).%New()
	s rs.ClassName = className
	s rs.QueryName = queryName
	d rs.Execute(arg...)

	#; 列名
	s colStr = ""
	for i = 1 : 1 : rs.GetColumnCount(){
		s columnName = rs.GetColumnName(i)
		s colStr = $s(colStr = "" : columnName, 1 : colStr _ "," _ columnName)
	}
	d file.Write(colStr)
	
	#; 属性值
	while (rs.Next()) {
		s valStr = ""
		for i = 1 : 1 : rs.GetColumnCount(){
			s columnName = rs.GetColumnName(i)
			s val = rs.Data(columnName)
			s valStr = $s(valStr = "" : val, 1 : valStr _ "," _ val)	
		}
		d file.Write($c(10) _ valStr)
	}
	
	d file.%Save()
	q $$$OK
}
USER>w ##class(Util.FileUtils).Query2Csv("%SYSTEM.License","Summary","E:\m\CsvFile2.csv")
1

请添加图片描述

请添加图片描述


总结

  • 理解qHandle参数与GetInfo方法是实现通用Query的关键。
  • 使用通用Query可以提升开发效率。
  • 使用通用Query可以解决数据适配问题。

以上是个人对基于Query的一些理解,由于个人能力有限,欢迎大家提出意见,共同交流。

如果一个好点子,只是因为某个人先到想到就禁止后人使用,这会让整个人类社会多走很多弯路,这也是自由软件精神一直以来所表达的内容。                                                                                                                                                                                                     - 理查德·马修·斯托曼

0
0 270
文章 姚 鑫 · 三月 20, 2023 4m read

第三章 高可用性的故障转移策略

随着组织越来越依赖基于网络的应用程序,使数据库尽可能可用和可靠变得至关重要。本指南解释了 IRIS 数据平台如何提供高度可用和可靠的数据存储,并描述了从中断和故障中快速恢复并同时保持数据完整性的策略。

IRIS® 数据平台提供多种高可用性 (HA) 解决方案,并可轻松与操作系统供应商提供的所有常见 HA 配置集成。

维持系统高可用性的主要机制称为故障转移。在这种方法下,一个失败的主系统被一个备份系统取代;也就是说,处理故障转移到备份系统。许多 HA 配置还提供灾难恢复机制,即在故障转移机制无法保持系统可用时恢复系统可用性。

IRIS 实例故障转移有五种通用方法以实现 HA(包括不实施 HA 策略)。本章概述了这些方法,而本指南的其余部分提供了实施这些方法的过程。

重要的是要记住,在除镜像之外的所有这些方法中,单个存储故障都可能是灾难性的。因此,磁盘冗余、数据完整性指南“日志记录”一章中描述的数据库日志记录以及数据完整性指南“备份和还原”一章中描述的良好备份过程必须始终是您的一部分方法,因为它们对于减轻磁盘故障的后果至关重要。

如果需要详细信息来帮助制定适合环境的故障转移和灾难恢复策略。

无故障转移策略

IRIS 数据库的完整性始终受到数据完整性指南中描述的功能的保护,免受生产系统故障的影响。结构数据库完整性由 写图像日志 (WIJ) 技术维护,而逻辑完整性则通过日志记录和事务处理维护。自动 WIJ 和日志恢复是 “bulletproof”数据库架构的基本组成部分。

但是,如果没有适当的故障转移策略,故障可能会导致大量停机,具体取决于故障原因以及您隔离和解决故障的能力。对于许多非关键业务的应用程序,这种风险可能是可以接受的。

采用这种方法的客户具有以下特点:

  • 清晰详细的操作恢复程序,包括日志记录、备份和恢复
  • 磁盘冗余(RAID 和/或磁盘镜像)
  • 能够快速更换硬件
  • 24x7 维护合同与所有供应商签订
  • 管理接受度和应用程序用户对故障导致的中等停机时间的容忍度

故障转移集群

实现 HA 的一种常见方法是故障转移集群,其中主要生产系统由(通常相同的)备用系统补充,具有共享存储和跟随活动成员的集群 IP 地址。如果生产系统出现故障,备用系统将承担生产工作负载,接管之前在故障主系统上运行的程序和服务,包括 IRIS

IRIS 旨在与操作系统级别提供的故障转移解决方案轻松集成,特别是 IBM PowerHA SystemMirrorRed Hat Enterprise Linux HAIRIS 的单个实例安装在共享存储设备上,以便两个集群成员都能识别该实例,然后将其添加到故障转移集群配置中,以便它作为故障转移的一部分自动启动。如果活动节点在指定的时间段内变得不可用,故障转移技术会将集群 IP 地址和共享存储的控制权转移到备用节点,并在新的主节点上重新启动 IRIS。重新启动时,系统会自动执行正常的启动恢复,WIJ、日志记录和事务处理会保持结构和数据的完整性,就好像 IRIS 在故障系统上重新启动一样。

备用服务器必须能够处理正常的生产工作负载,只要它可能需要恢复发生故障的主服务器。可选地,备用数据库可以成为主要数据库,故障的主要数据库在恢复后成为备用数据库。

在这种方法下,共享存储设备的故障是灾难性的。因此,磁盘冗余、日志记录以及良好的备份和恢复过程对于提供足够的恢复能力至关重要。

虚拟化高可用

虚拟化平台通常提供 HA 功能,通常会监控来宾操作系统及其运行的硬件的状态。在任何一个失败时,虚拟化平台都会根据需要在备用硬件上自动重启失败的虚拟机。当 IRIS 实例重新启动时,它会自动执行正常的启动恢复,WIJ、全局日志记录和事务处理保持结构和数据完整性,就好像 IRIS 在物理服务器上重新启动一样。

此外,虚拟化平台允许将虚拟机重新定位到备用硬件以进行维护,从而实现物理服务器的升级,例如,无需任何停机时间。虚拟化 HA 共享故障转移集群和并发集群的主要缺点,但是:共享存储的故障是灾难性的。

0
0 224
文章 Kate Lau · 三月 17, 2023 1m read


添加凭据以登录 FHIR REST 接口 - 在这例子仅考虑基本身份验证

 

添加服务注册表 - 在这例子仅考虑基本身份验证

- 设置 HTTP 服务

- 输入 FHIR 服务器的路径

- 输入 FHIR 服务的 URL

- 使用配置的凭证

 


添加 "HS.FHIRServer.Interop.HTTPOperation"

选择服务名称

测试 FHIR 客户端

跟踪测试结果

1
0 142
文章 姚 鑫 · 三月 19, 2023 4m read

第二章 高可用性解决方案 - 虚拟化平台高可用

虚拟化平台高可用

虚拟化平台通常提供 HA 功能,通常会监控来宾操作系统及其运行的硬件的状态。在任何一个失败时,虚拟化平台都会根据需要在备用硬件上自动重启失败的虚拟机。当 IRIS 实例重新启动时,它会自动执行正常的启动恢复,保持结构和逻辑的完整性,就好像 IRIS 在物理服务器上重新启动一样。

虚拟环境中的故障转移

虚拟化 HA 具有内置于虚拟化平台基础架构中的优势,因此只需很少的配置工作,在某些情况下根本不需要。此外,虚拟化平台允许有计划地将虚拟机重新定位到备用硬件以进行维护,从而实现物理服务器的升级,例如,无需任何停机时间。

IRIS 镜像

具有自动故障转移功能的 IRIS 镜像采用不同的 HA 方法,依靠完全独立系统之间的逻辑数据复制来避免共享存储的单点故障风险,并确保生产可以立即故障转移到几乎所有的备用 IRIS 实例故障场景——系统、存储和网络。

IRIS 镜像中,一个称为主要故障转移成员的 IRIS 实例提供对生产数据库的访问。单独主机上的另一个实例,称为备份故障转移成员,与主实例同步通信,检索其日志记录,确认收到它们,并将它们应用于同一数据库的自己的副本。通过这种方式,主备都始终知道备份是否有来自主的最新日志文件,因此可以将其数据库与主上的数据库精确同步。

在这种情况下,镜像可以在主要中断的情况下快速自动故障转移到备份,而不会丢失数据。第三个系统,仲裁器,帮助备份确定当主系统变得无响应时它是否应该接管。故障转移成员共享的虚拟 IP 地址或分布式缓存集群等机制将应用程序连接重定向到新的主服务器。故障转移过程只需几秒钟;许多用户甚至不会注意到它的发生。而且因为备份有自己的数据库副本,所以即使主数据库及其存储完全失败也不会导致数据库不可用。事实上,即使备份丢失了最新的日志数据,备份的镜像代理也可以从主主机检索它,如果它仍然在线的话。

一旦将之前的主数据库恢复到故障转移后的运行状态,它就会成为备份数据库,并且其数据库会迅速赶上新主数据库上的数据库,从而使镜像恢复到完整的 HA 功能。然后,可以将系统恢复到原来的角色或保持新的安排。

镜像还可以包括灾难恢复 (DR) 异步成员,它们是主服务器的异步维护副本; DR 异步可以提升为故障转移成员,例如,当发生故障的主数据库无法快速恢复运行时成为备用成员,或者(如果物理上分开)在数据中心故障等中断导致两个故障转移都停止时进行灾难恢复成员。最后,镜像可以包含报告异步成员,它们维护生产数据库的异步副本,用于商业智能和数据仓库目的。

IRIS 镜像

镜像还可以与虚拟化平台 HA 一起使用以创建混合 HA 方法,在这种方法下,虚拟化平台响应计划外系统或操作系统级中断,而镜像处理所有计划内中断和计划外数据库中断(包括 IRIS 中断和存储故障) ) 通过自动故障转移。

HA 解决方案功能比较

下表提供了镜像、集群和虚拟化作为 HA 解决方案的非常一般的特性比较。

特色IRIS 镜像操作系统级集群虚拟化平台高可用
机器断电或崩溃后的故障转移无缝处理机器故障。无缝处理机器故障。无缝处理物理和虚拟机故障。
防止存储故障和数据损坏内置复制可防止存储故障;逻辑复制避免了大多数类型的损坏。依赖于共享存储设备,因此故障是灾难性的;存储级冗余是可选的,但可能会带来某些类型的损坏。依赖于共享存储设备,因此故障是灾难性的;存储级冗余是可选的,但可能会带来某些类型的损坏。
IRIS关闭、挂起或崩溃后的故障切换内置快速检测和故障转移。可以配置为在 IRIS 中断后进行故障转移。可以配置为在 IRIS 中断后进行故障转移。
IRIS 升级允许最短的停机时间 IRIS 升级。*IRIS 升级需要停机。IRIS 升级需要停机。
应用程序平均恢复时间故障切换时间通常为几秒钟。故障转移时间可以是几分钟。故障转移时间可以是几分钟。
外部文件同步仅复制数据库;外部文件需要外部解决方案。所有文件对两个节点都可用。故障转移后所有文件可用。

* 需要一种配置,其中应用程序代码、类和例程保存在与包含应用程序数据的数据库分开的数据库中

将分布式缓存与故障转移策略结合使用

无论您采用何种方法实现 HA,都可以使用基于企业缓存协议 (ECP) 的分布式缓存集群在用户和数据库服务器之间提供一层隔离。当数据服务器出现故障时,用户仍然连接到集群的应用服务器;在中断期间主动访问数据的用户会话和自动事务会暂停,直到数据服务器通过完成故障转移或重新启动故障系统再次可用。

但是将分布式缓存添加到 HA 策略会增加复杂性并引入更多的故障点。

0
0 246
文章 姚 鑫 · 三月 18, 2023 3m read

第一章 高可用性解决方案 - HA 解决方案中的问题

高可用性(HA)指的是使系统或应用程序在长时间内保持正常运行并可供用户使用的目标,从而最大限度地减少计划内和计划外停机时间。 IRIS提供自己的HA解决方案,并轻松与操作系统提供商提供的常见HA解决方案集成。

维护高系统可用性的主要机制称为故障转移。在这种方法下,故障的主系统由备用系统代替;也就是说,生产故障转移到备份系统。许多HA配置还提供了灾难恢复(DR)机制,即当HA机制无法保持系统可用时,恢复系统可用性。

本页简要讨论可与基于 IRIS 的应用程序一起使用的一般 HA 策略,然后涵盖 IRIS HA 解决方案中的问题,提供 HA 解决方案功能比较,并讨论使用分布式缓存和故障转移策略

IRIS HA 解决方案中的问题

在为IRIS 系统评估潜在的 HA 解决方案时,请牢记以下两个重要问题:

  • 共享存储

HA 架构的一个重要原则是避免单点故障。大多数 HA 解决方案依赖于共享存储组件;这代表了这样的风险;如果存储出现故障,就不可能保持系统可用。存储级冗余可以在一定程度上减轻这种风险,但也可以延续某些类型的数据损坏。

另一方面,IRIS 镜像在完全独立的主存储和备份存储之间使用逻辑数据复制,完全消除了单点故障问题并避免了大多数类型的损坏。

  • IRIS 升级

许多 HA 解决方案允许在不中断整体可用性的情况下对给定组件系统进行计划内停机。然而,大多数都需要大量的停机时间来升级生产 IRIS 实例。

然而,当应用程序代码、类和例程与应用程序数据保存在不同的数据库中时,IRIS 镜像允许将停机时间降至最低。另一方面,镜像以外的 HA 解决方案需要为 IRIS 升级或任何其他需要关闭 IRIS 的维护仔细规划停机时间窗口。

无 HA 解决方案

IRIS 数据库的结构和逻辑完整性始终受到数据完整性指南“数据完整性简介”一章中描述的内置功能的保护,免受生产系统故障的影响:写入图像日志记录、数据库日志记录和事务处理。但是,如果没有 HA 解决方案,故障可能会导致大量停机,具体取决于故障原因以您隔离和解决故障的能力。对于许多非关键业务的应用程序,这种风险可能是可以接受的。

采用这种方法的客户具有以下特点:

  • 清晰详细的操作恢复程序,包括日志记录、备份和恢复。
  • 磁盘冗余(RAID 和/或磁盘镜像)。
  • 能够快速更换硬件。
  • 与所有供应商签订 24/7 维护合同。
  • 管理接受度和应用程序用户对故障导致的中等停机时间的容忍度。

操作系统级集群 HA

在操作系统级别提供的常见 HA 解决方案是故障转移集群,其中主要生产系统由(通常相同的)备用系统补充,具有共享存储和跟随活动成员的集群 IP 地址。如果生产系统出现故障,备用系统将承担生产工作负载,接管之前在故障主系统上运行的程序和服务。备用数据库必须能够处理正常的生产工作负载,只要它可能需要恢复发生故障的主数据库。可选地,备用数据库可以成为主要数据库,故障的主要数据库在恢复后成为备用数据库。

IRIS 旨在轻松与支持平台的故障转移集群技术集成(如 支持的平台中所述)。一个 IRIS 实例安装在集群的共享存储设备上,以便两个集群成员都能识别它,然后将其添加到集群配置中,以便作为故障转移的一部分在备用数据库上自动重启。故障转移后重新启动时,系统会自动执行正常的启动恢复,保持结构和逻辑完整性,就好像 IRIS 在故障系统上重新启动一样。如果需要,可以在单个集群上安装多个 IRIS 实例。

0
0 178
文章 Claire Zheng · 三月 14, 2023 10m read

本文根据InterSystems中国技术总监乔鹏( @Qiao Peng )的演讲“互联互通套件赋能数据利用与应用创新”整理而成。

IRIS医疗版互联互通套件的缘起与发展演进
 

来源HL7:正在到来的挑战 

http://hl7.org/fhir/change.html

这是来自HL7官网上的一张图,描述了我们在医疗卫生行业面临的一些挑战,以及信息化建设在应对挑战中发挥的作用。当今,医疗卫生、生物学、信息技术有很强的融合趋势,加之社会变革带来的经济方面的需求,同时构成颠覆传统医疗卫生行业的因素。

这张图显示了从“被动医疗”转向“主动医疗”过程中信息的爆炸式增长,信息共享交换推动了我们对信息的利用,在这一进程中,医疗卫生信息化起着核心作用——而让信息更具价值,赋予信息标准化和互操作能力的过程,这也是InterSystems一直努力的方向,我们在国内支持大量医院实现了互联互通建设。在建设过程中,我们注意到项目的定量部分的建设成本占比是比较高的,很多的工作都花在了合规性和相关管理工具的开发上——应用标准的实施是有成本的,而对于标准的理解在各个项目上水平不尽相同,这就进一步影响了互联互通项目的建设成果。

0
0 317
文章 王喆 👀 · 三月 12, 2023 8m read

SSH框架是Java一个的比较有代表性的框架,是多年前比较流行的一个。有struts+spring+hibernatespringMVC+spring+hibernate两种,当然我大学时候使用的是第二种。倘使我能把IRIS作为一个库,联结到Hibernate那么理论上是不是也代表IRIS也可以使用SSH框架开发呢?

工具及环境

JDK 1.8

       Maven

       Hibernate 5.X.X

       IRISHealth-2022.1.3

       intellij idea

       Windows 10 64

1、创建数据库

       用代码的方式创建几个表

Class BKIP.SOA.MonitorScreen.CacheTable.logError Extends %Persistent

{

/// 服务概况缓存表

Property SucNum As %String(MAXLEN = "");

Property failNum As %String(MAXLEN = "");

Property fdateTime As %String(MAXLEN = "");

}

如图所示:

2、创建一个Spring的项目

       File—New—Project....

next

给项目起名,选择jdk版本

Next

0
0 134
文章 Jingwei Wang · 三月 7, 2023 2m read

自定义更多的Production组件的设置

本示例会在Production的组件BO(业务操作)中增加自定义的配置信息,例如添加目标系统信息,当然此类信息也可以放在已有的配置”注释“中。

为production组件新增设置信息

  1. 为组件创建”目标服务器“设置信息。 现在想要为一个组件SoapDemo.BO.Operation.MyWebServiceSoap新增一个名为”目标服务器“的设置信息。
  2. 在SoapDemo.BO.Operation.MyWebServiceSoap类中增加以下代码,然后编译此类。其中Info为,您想将此设置放在哪一种设置中,Info代表”信息设置“,Basic代表”基本设置“,详细信息请参考设置类型
    Property 目标服务器 As %String(MAXLEN = 2000, XMLPROJECTION = "ATTRIBUTE");
    Parameter SETTINGS = "目标服务器:Info";
  3. 进入Production页面,刷新改页面,如下图所示,此时已经成功为组件增加 ”目标服务器“ 设置。 

 

获取组件设置信息

0
0 170
文章 Nicky Zhu · 八月 10, 2022 3m read

在互操作功能运行过程中,IRIS可以识别异常情况的发生,自动生成告警事件并通过预制的互操作组件将告警转发给干系人;另外,IRIS也内建了告警工作台,可供运维团队基于工作流实现对告警的管理。

告警设置与转发

Production告警设置

Production级别可以设置对连接异常的告警设置,及告警组件设置

组件告警设置

  • 控制何时触发警报。
    • 队列计数警报。
      • 队列中的信息数量
    • 队列等待警报。
      • 消息在队列中停留的秒数
    • 不活动超时。
      • 业务组件不活动的秒数
      • v2012.1之前,不会触发告警

警报宽限期

    • 与外部连接有关的错误可以有宽限期设置
      • 设置重新建立连接的时间,超时且连接不成功才会发出告警
    • 业务服务
      • 警报宽限期
    • 业务操作
      • 再次发出警报宽限期

转发告警

    • EnsLib.EMail.AlertOperation
      • 内置告警转发组件
        • 通过Email转发
    • 路由到其他接口转发
      • REST - 短信
      • REST - 钉钉
    • 用户需要执行的规划
      • 什么样的告警需要即时通知
      • 告警的通知是否应该成为一个应用

包含告警组件的Production

包含告警组件与业务组件的Production形态如下

其中,用于告警的业务组件包括

告警托管

3
0 148
公告 Michael Lei · 二月 28, 2023

我们很高兴地宣布我们的抢先体验计划的最新成员 - Health Data De-ID工具。

该工具将根据 *HIPAA 安全港* 对结构化临床数据进行去标识化处理,并允许在需要时进行重新标识。它使用 InterSystems 的 *SDA* 规范格式,该格式用于我们从一种格式到另一种格式的所有标准转换,例如 *HL7* 到 *CDA*、*HL7* 到 *FHIR* 等。虽然该工具使用 XSLT 转换来提高性能原因,它提供了辅助方法,您不需要成为 XSLT 专家的情况下更轻松地调整开箱即用的去标识化规则。

Health Data De-ID 适用于任何 InterSystems 产品,从 InterSystems IRIS for Health 到 Health Connect 再到 Unified Care Record。

您可以在此处加入抢先体验计划 - [如何注册抢先体验计划 (EAP)]?我们需要您的反馈,因此请在您完成整个过程或遇到任何问题时与我们联系。

0
0 140
文章 Lilian Huang · 二月 28, 2023 3m read

嗨,InterSystems 开发人员!

最近我更新了FHIR 开发模板,它发布了一个 IPM 包fhir-server ,使 InterSystems FHIR 服务器的设置成为一个微不足道的手动或自动或编程的程序,只需一条命令。

请参阅下文,了解如何从中受益。

TLDR

USER>zpm "install fhir-server"

以下所有详细信息。

0
0 149
文章 Qiao Peng · 二月 22, 2023 12m read

面向对象编程的优势

在应用程序开发时,我们使用的大多数开发语言都是面向对象编程 object-oriented programming (OOP)语言,例如大家熟悉的Java、.NET。而TIOBE的2023年2月的最新开发语言流行排行榜上,前5大语言都是面向对象编程语言,连排名第六的Visual Basic都有了越来越多的OO特性:

为什么使用面向对象编程这么流行?因为它有诸多优势:

  • 封装:将数据和操作数据的代码封装在一个单元中,在确定范围的数据上进行编程。方便代码的开发、管理与分享。
  • 抽象:将业务数据概括为不同的对象类型,从而进行业务分组开发、简化程序。
  • 继承:一个类可以从另一个类继承它属性和行为,从而实现更大范围的代码复用。
  • 多态:多个对象可以创建自一个类,且可以有不同的行为。一段灵活的代码能实现多种形态的业务,它进一步降低了代码开发量和调用难度。

而这4个优势正是面向对象编程的核心特征。

关系型数据库的对象/关系错配

虽然面向对象编程是绝对的主流,但数据通常被保存到关系型数据库中。关系型数据库的行和列二维关系与复杂的对象并不匹配:

0
0 597
文章 Lele Yang · 二月 21, 2023 4m read

** 2018 年 2 月 12 日修订

虽然本文是关于 InterSystems IRIS 的,但它也适用于 Caché、Ensemble 和 HealthShare 发行版。

介绍

内存以页为单位进行管理。 Linux 系统上的默认页面大小为 4KB。 Red Hat Enterprise Linux 6、SUSE Linux Enterprise Server 11 和 Oracle Linux 6 引入了一种根据系统配置提供 2MB 或 1GB 大小的增加页面大小的方法,称为 HugePages。

起初 HugePages 需要在启动时分配,如果管理或计算不当可能会导致资源浪费。因此,各种 Linux 发行版引入了默认启用 2.6.38 内核的Transparent HugePages。这是一种自动创建、管理和使用 HugePages 的方法。以前的内核版本也可能具有此功能,但可能未标记为 [always] 而是设置为 [madvise]。

Transparent Huge Pages (THP) 是一种 Linux 内存管理系统,它通过使用更大的内存页面来减少在具有大量内存的机器上进行Translation Lookaside Buffer (TLB) 查找的开销。然而,在当前的 Linux 版本中,THP 只能映射单个进程的堆栈空间。

0
0 178
文章 Claire Zheng · 二月 21, 2023 1m read

速度、规模和安全性是我们真正热衷的领域,这也正是优秀的E级方程式赛车手所追求的。在InterSystems 2022年全球峰会上,InterSystems 数据平台产品管理总监Jeffery Fried 及其团队分享了InterSystems IRIS数据平台的新功能与产品规划。基于InterSystems IRIS数据平台,我们为用户构建优秀的“赛车”,内置的一系列非凡功能和高效周全的“维修技师”,确保“赛车手”能够在竞争激烈的赛道中心无旁骛地飞速向前!

0
0 193
文章 Tete Zhang · 二月 20, 2023 11m read

开发者您好!

这里向介绍@Lorenzo ScaleseOpenAPI-Suite (一个用于从OpenAPI 3.0生成 ObjectScript 代码的工具集)。这个工具集在 2023 年 InterSystems 开发工具大赛的21 个参赛作品中获得专家提名第三名

目前 IRIS 最高支持OpenAPI 2.0 ,但是这个工具支持 OpenAPI 3.0

提供的功能如下。

  1. 在服务器端创建类(与 OpenAPI 2.0相同,首先创建使用 API First理念创建的 REST 调度类所需的类。)
  2. 创建客户端 HTTP 类
  3. 创建客户端Production(业务服务、业务流程、业务操作、Ens.Request、Ens.Response)
  4. 一个 Web 界面,允许您指定要从此工具生成哪些功能
  5. 从 OpenAPI 1.x、2.x 到 3.0 版的转换工具

关于各个功能的详细介绍,请参考@Lorenzo Scales撰写的文章《 OpenAPI Suite - Part 1 》和《 OpenAPI Suite - Part 2 》。

在本文中,我尝试了 1 个功能。其中,以下的应对方案非常好。

0
1 345
公告 Tete Zhang · 二月 16, 2023

InterSystems 产品支持的平台 - 版本更新 (2023 年 2 月

我们经常收到有关 InterSystems IRIS 数据平台支持的平台和框架的问题。此更新旨在分享最近的更改以及我们对即将发生的更改的当前决策。(预测未来是一项棘手的工作,不应将此视为我们对未来支持平台和框架的承诺。)

我们计划大约每 3 个月发布一次此类更新,然后在一年内重新评估。如果您觉得此更新有用,请告诉我们!我们很感激您的建议。

关于更新……

IRIS 生产操作系统和 CPU 架构

红帽企业版 Linux

0
0 209