搜索​​​​

清除过滤器
文章
姚 鑫 · 四月 9, 2021

第二十一章 导入和导出SQL数据

# 第二十一章 导入和导出SQL数据 在InterSystems IRIS®Data Platform Management Portal中,有用于导入和导出数据的工具: - 从文本文件导入数据 - 将数据导出到文本文件 这些工具使用动态SQL,这意味着查询是在运行时准备和执行的。可以导入或导出的行的最大大小为3,641,144个字符。 还可以使用%SQL.Import.Mgr类导入数据,使用%SQL.Export.Mgr类导出数据。 # 从文本文件导入数据 可以将数据从文本文件导入到合适的InterSystems IRIS类中。执行此操作时,系统将在表中为该类创建并保存新行。该类必须已经存在并且必须编译。要将数据导入到此类中,请执行以下操作: 1. 从管理门户中选择系统资源管理器,然后选择SQL。使用页面顶部的切换选项选择一个命名空间;这将显示可用命名空间的列表。 2. 在页面顶部,单击向导下拉列表,然后选择数据导入。 3. 在向导的第一页上,从指定外部文件的位置开始。对于导入文件所在的位置,请单击要使用的服务器的名称。 4. 然后输入文件的完整路径和文件名。 5. 对于选择架构名称,单击要向其中导入数据的InterSystems IRIS包。 6. 对于选择表名,单击将包含新创建的对象的类。 7. 然后单击下一步。 8. 在向导的第二页上,单击将包含导入数据的列。 9. 然后单击下一步。 10. 在向导的第三页上,描述外部文件的格式。 - 有关用什么分隔符分隔您的列?,请单击与此文件中的分隔符对应的选项。 - 单击第一行是否包含列标题?如果文件的第一行不包含数据,则选中此复选框。 - 对于字符串引号,单击指示此文件用于开始和结束字符串数据的引号分隔符字符的选项。 - 对于日期格式,请单击指示此文件中日期格式的选项。 - 对于时间格式,请单击指示此文件中的时间格式的选项。 - 对于时间戳格式,请单击指示此文件中的时间戳格式的选项。 - 单击禁用验证?如果不希望向导在导入时验证数据,请选中此复选框。 - 使用%SortBegin/%SortEnd?如果不希望向导在导入期间重新生成索引,请选中此复选框。如果选中延迟索引生成,向导将在将导入的数据插入到表中之前为该类调用%SortBegin方法。导入完成后,向导将调用%SortEnd方法。不执行任何验证(与使用%NOCHECK的INSERT相同)。这是因为当使用%SortBegin/%SortEnd时,在SQL INSERT期间不能检查索引的唯一性。如果选中延迟索引构建,则假定导入的数据有效,不会检查其有效性。 - 或者,单击“预览数据”以查看向导将如何分析此文件中的数据。 11. 单击“下一步”。 12. 检查条目,然后单击Finish。向导将显示“数据导入结果”对话框。 13. 单击关闭。或者单击给定的链接以查看后台任务页面。 在任何一种情况下,向导都会启动一个后台任务来完成工作。 # 将数据导出到文本文件 可以将给定类的数据导出到文本文件。为此: 1. 从管理门户中选择系统资源管理器,然后选择SQL。使用页面顶部的切换选项选择一个命名空间;这将显示可用命名空间的列表。 2. 在页面顶部,单击向导下拉列表,然后选择数据导出。 3. 在向导的第一页上: - 输入要创建以保存导出数据的文件的完整路径和文件名。 - 从下拉列表中,选择要从中导出数据的命名空间、方案名和表名。 - 或者,从Charset下拉列表中选择一个字符集;默认值为Device Default。 - 然后单击下一步。 4. 在向导的第二页上,选择要导出的列。然后单击下一步。 5. 在向导的第三页上,描述外部文件的格式。 - 有关用什么分隔符分隔的列?,请单击与此文件中的分隔符对应的选项。 - 单击导出列标题?如果要将列标题导出为文件的第一行,请选中此复选框。 - 对于字符串引号,单击一个选项以指示如何开始和结束此文件中的字符串数据。 - 对于日期格式,单击一个选项以指示要在此文件中使用的日期格式。 - (可选)单击“预览数据”以查看结果的外观。然后单击下一步。 6. 检查条目,然后单击Finish。该向导将显示“数据输出结果”对话框。 7. 单击关闭。或单击给定的链接以查看后台任务页面。 在任何一种情况下,向导都启动后台任务来完成工作。
文章
姚 鑫 · 八月 24, 2022

第十一章 配置数据库(三)

# 第十一章 配置数据库(三) ## 编辑本地数据库的属性 显示的信息取决于数据库是否被镜像。本节确定以下字段: ### 部分编辑非镜像本地数据库属性 单击非镜像数据库的名称可查看以下数据库属性并更改其中一些属性。 (“创建本地数据库”部分描述了其中的许多字段。) - `Name `- `Directory` 目录(此设置必须始终反映 `IRIS.DAT` 数据库文件的位置)- `Encrypted`加密(无法更改)- `Mirrored` 已镜像 — 单击添加到 `Mirrormirror_name` 链接以将数据库添加到其中 `IRIS` 实例是主要故障转移成员的镜像。 (仅当实例是镜像中的主实例时,此选项才可用。)- `Block Size (Bytes)`块大小(字节)(无法更改)- `Size` 大小 (MB) — 共有三种大小设置,如下所示: - `Change Current` 修改数据库的当前大小。 - `Expansion` 扩展设置在需要时扩展数据库的数量;默认(和推荐)设置为零 (`0`) 表示当前大小的 `12%` 或 `10` MB,以较大者为准。使用当前大小的 `12%` 时,扩展大小不会大于 1GB。 - `Maximum` 最大值指定数据库可以增长到的最大大小,以兆字节为单位;默认设置为零 (`0`) 表示没有最大值。要修改此设置,可以输入新的 `MB` 数,也可以在数字前加上 `+` 或 `-`,例如 `+10` 或 `-20`,将最大值放大或缩小指定的量。当小数据库的最大大小时,会收到警告并且必须确认操作。 **注意:不能创建或编辑数据库,使其大小大于可用的总磁盘空间。如果指定的大小在磁盘可用空间的 `90%` 以内,会收到警告并且必须确认操作。** - `Resource Name` 资源名称 — 选择与数据库关联的资源。单击下拉列表旁边的资源图标以显示“资源”页面,以便可以创建资源。- `New Global` 新全局 - 为新`globals`指定属性。- `Global Journal State` 全局日志状态 — 选择启用日志,清除禁用。- `Preserve global attributes on delete` 删除时保留global属性——指定删除时是否应保留global的目录条目和属性;属性包括排序规则、日志状态和增长指针。当global被完全删除时,选择保留global的目录条目和属性; `clear` 删除目录条目和属性。- `Mount Read-Only` — 选择以指定数据库以只读方式安装; `clear` 指定它以读写方式挂载。- `Mount Required at Startup` — 选择以指示在 `IRIS` 启动时必须安装数据库;如果无法安装数据库, `IRIS` 不会启动。这使可以确保在崩溃后启动之前可以在数据库上执行日志恢复和事务回滚(如数据完整性指南的“日志”一章中所述)。清除让 `IRIS` 无需先安装数据库即可启动。 **注意:默认情况下,为所需的 `IRIS` 数据库(例如,`IRISLIB` 和 `IRISAUDIT`)选择此设置并且无法更改。默认值已清除,但可以为创建的数据库以及 `USER` 和 `ENSLIB` 数据库选择)。** - `Stream Location`流位置 — 单击“浏览”按钮选择与该数据库关联的流的存储目录。默认情况下,本地数据库的流位置是数据库目录中名为 `stream` 的子目录,它是上述字段之一(例如 `install-dir\mgr\DB1\stream`)。 **注意: 建议使用默认位置。** ### 编辑镜像本地数据库属性 单击镜像数据库的名称可以查看和更改以下一些数据库属性;请参阅上一节中的定义。 **注意:镜像数据库需要日记功能,因此不会出现`Global Journal State`全局日记状态设置。** - `Name`- `Mirror Name` 镜像名称——在镜像中识别数据库的名称;无法更改。- `Directory` 目录(此设置必须始终反映 `IRIS.DAT` 数据库文件的位置)- `Encrypted` 加密(无法更改)- `Stream Location` 流位置 — 单击“浏览”按钮选择与该数据库关联的流的存储目录。默认情况下,本地数据库的流位置是数据库目录中名为 `stream` 的子目录,它是上述字段之一(例如 `install-dir\mgr\DB1\stream`)。 **注意:与不包含在数据库本身中的其他数据库相关数据一样,镜像数据库的文件流不会被镜像。 建议使用默认位置。** - `Resource Name` 资源名称 — 选择与数据库关联的资源。单击下拉列表旁边的资源图标以显示“资源”页面,以便可以创建资源。- `Block Size (Bytes)`块大小(字节)(无法更改)- `Collation` 排序规则 - 在全局属性中,只有排序规则属性可以更改,仅适用于新的全局变量。- `Preserve global attributes on delete`删除时保留`global`属性——指定删除时是否应保留global的目录条目和属性;属性包括排序规则、日志状态和增长指针。当global被完全删除时,选择保留global的目录条目和属性; clear 删除目录条目和属性。- `Mount Read-Only` — 选择以指定数据库以只读方式安装; `clear` 指定它以读写方式挂载。- `Mount Required at Startup` — 选择以指示在 `IRIS` 启动或成为镜像主数据库时必须安装数据库;如果无法安装数据库, `IRIS` 不会启动或成为主数据库。这使可以确保在崩溃后启动之前可以在数据库上执行日志恢复和事务回滚,并且前主数据库上的打开事务已作为一部分回滚的故障转移。清除让 IRIS 无需先安装数据库即可启动。- `Local Properties` 本地属性 — 此区域包含三个大小设置,如下所示: - `Change Size` 修改数据库的当前大小。 - 扩展设置在需要时扩展数据库的数量(并假设可用空间);默认(和推荐)设置为零 (`0`) 表示当前大小的 `12%` 或 `10 MB`,以较大者为准。 - 最大值指定数据库可以增长到的最大大小,以兆字节为单位;默认设置为零 (`0`) 表示没有最大值。要修改此设置,您可以输入新的 MB 数,也可以在数字前加上 `+` 或 `-`,例如 `+10` 或 `-20`,将最大值放大或缩小指定的量。当减小数据库的最大大小时,您会收到警告并且必须确认操作。 该区域还包含其他系统的当前、扩展和最大大小设置 — 如果当前实例是故障转移成员,则这是另一个故障转移成员;如果当前实例是异步成员,则这是异步可以从中获取信息的第一个故障转移成员。 **注意:不能创建或编辑数据库,使其大小大于可用的总磁盘空间。如果指定的大小在磁盘可用空间的 `90%` 以内,会收到警告并且必须确认操作。**
文章
姚 鑫 · 十月 20, 2021

第五十一章 SQL命令 HAVING(二)

# 第五十一章 SQL命令 HAVING(二) ## In和%INLIST谓词 `IN`谓词用于将值与一系列非结构化的项进行匹配。 `%INLIST`谓词是 IRIS扩展,用于将值与列表结构的元素进行匹配。 使用任一谓词,都可以执行相等比较和子查询比较。 在中有两种格式。第一个用作使用与`OR运`算符链接在一起的多个相等比较的速记。例如: ```sql SELECT Name, Home_State FROM Sample.Person GROUP BY Home_State HAVING Home_State IN ('ME','NH','VT','MA','RI','CT') ``` 如果`Home_State`等于括号列表中的任意值,则计算为`TRUE`。列表元素可以是常量或表达式。排序规则适用于IN比较,因为它适用于相等性测试。默认情况下,`IN`比较使用字段定义的排序规则类型;默认情况下,字符串字段定义为`SQLUPPER`,不区分大小写。 当日期或时间用于IN谓词相等比较时,会自动执行适当的数据类型转换。如果`HAVING`子句字段是`TIMESTAMP`类型,则`DATE`或`TIME`类型的值将转换为`TIMESTAMP`。如果`HAVING`子句字段为`DATE`类型,则`TIMESTAMP`或`STRING`类型的值将转换为`DATE`。如果`HAVING`子句字段为TIME类型,则`TIMESTAMP`或`STRING`类型的值将转换为`TIME`。 下面的示例都执行相同的相等比较并返回相同的数据。 `groupby`字段指定对于每个成功的相等比较只返回一条记录。 `DOB`字段的数据类型为`Date`: ```sql SELECT Name,DOB FROM Sample.Person GROUP BY DOB HAVING DOB IN ({d '2014-01-02'},{d '1990-04-25'}) ``` ```sql SELECT Name,DOB FROM Sample.Person GROUP BY DOB HAVING DOB IN ({ts '2014-01-02 00:00:00'},{ts '1990-04-25 00:00:00'}) ``` `%INLIST`谓词可用于对列表结构的元素执行相等比较。 `%INLIST`使用`EXACT`排序。 因此,默认情况下,`%INLIST`字符串比较是区分大小写的。 下面的例子使用`%INLIST`来匹配一个字符串值到`FavoriteColors`列表字段的元素: ```sql SELECT Name,FavoriteColors FROM Sample.Person HAVING 'Red' %INLIST FavoriteColors ``` 它返回`FavoriteColors`中包含元素`“Red”`的所有记录。 下面的嵌入式SQL示例将`Home_State`列值与`northne`(新英格兰北部各州)列表中的元素匹配: ```java ClassMethod Having() { s northne = $lb("VT","NH","ME") &sql( DECLARE StateCursor CURSOR FOR SELECT Name,Home_State INTO :name,:state FROM Sample.Person HAVING Home_State %INLIST :northne ) &sql(OPEN StateCursor) q:(SQLCODE'=0) n %ROWCOUNT,%ROWID for { &sql(FETCH StateCursor) q:SQLCODE w !,"#",%ROWCOUNT," Name=",name," State=",state,! } w !,"Final Fetch SQLCODE: ",SQLCODE &sql(CLOSE StateCursor) } ``` ```java DHC-APP>d ##class(PHA.TEST.SQLCommand).Having() #1 Name=Lepon,Jeff Z. State=NH #2 Name=Ingleman,Terry A. State=NH #3 Name=Jung,Keith W. State=NH #4 Name=Xiang,Kirsten U. State=ME #5 Name=Jackson,Ralph V. State=VT #6 Name=Tesla,Geoffrey O. State=NH ``` 还可以在子查询中使用`IN`或`%INLIST`来测试列值(或任何其他表达式)是否等于任何子查询行值。 例如: ```sql SELECT Name,Home_State FROM Sample.Person HAVING Name IN (SELECT Name FROM Sample.Employee HAVING Salary < 50000) ``` 注意,子查询在`SELECT`列表中必须只有一个项。 ## %STARTSWITH谓词 IRIS `%STARTSWITH`比较操作符允许对字符串或数字的初始字符执行部分匹配。 下面的示例使用`%STARTSWITH`。 它根据年龄进行选择,然后为每个以“S”开头的Name返回一条记录: ```sql SELECT Name,Age FROM Sample.Person WHERE Age > 30 HAVING Name %STARTSWITH 'S' ORDER BY Name ``` 与其他字符串字段比较一样,`%STARTSWITH`比较不区分大小写。 ## Contains Operator ([) `Contains`操作符是左括号符号:`[`。 它允许将子字符串(字符串或数字)匹配到字段值的任何部分。 比较总是区分大小写的。 下面的例子在`HAVING`子句中使用`Contains`操作符选择那些`Home_State`值包含`“K”`的记录,然后对这些状态执行`%AFTERHAVING`计数: ```sql SELECT Home_State,COUNT(Home_State) AS States, COUNT(Home_State %AFTERHAVING) AS KStates FROM Sample.Person HAVING Home_State [ 'K' ``` ## FOR SOME谓词 `HAVING`子句的`FOR SOME`谓词决定是否根据一个或多个字段值的条件测试返回结果集。 该谓词的语法如下: ``` FOR SOME (table[AS t-alias]) (fieldcondition) ``` `FOR SOME`指定字段`condition`的值必须为`true`; 至少有一个字段值必须匹配指定的条件。 `Table`可以是单个表,也可以是逗号分隔的表列表,也可以是表别名。 `Fieldcondition`为指定表中的一个或多个字段指定一个或多个条件。 `table`参数和字段`condition`参数都必须用括号分隔。 下面的例子展示了`FOR SOME`谓词的用法: ```sql SELECT Name,Age FROM Sample.Person HAVING FOR SOME (Sample.Person)(Age>20) ORDER BY Age ``` 在上面的示例中,如果至少有一个字段包含大于`20`的`Age`值,则返回所有记录。 否则,不返回任何记录。 ## NULL 谓词 这将检测未定义的值。 你可以检测所有空值,或所有非空值: ```sql SELECT Name, FavoriteColors FROM Sample.Person HAVING FavoriteColors IS NULL ``` ```sql SELECT Name, FavoriteColors FROM Sample.Person HAVING FavoriteColors IS NOT NULL ORDER BY FavoriteColors ``` 使用`GROUP BY`子句,可以为指定字段的每个非空值返回一条记录: ```sql SELECT Name, FavoriteColors FROM Sample.Person GROUP BY FavoriteColors HAVING FavoriteColors IS NOT NULL ORDER BY FavoriteColors ``` ## EXISTS 谓词 它使用子查询来测试子查询是否计算为空集。 ```sql SELECT t1.disease FROM illness_tab t1 WHERE EXISTS (SELECT t2.disease FROM disease_registry t2 WHERE t1.disease = t2.disease HAVING COUNT(t2.disease) > 100) ``` ## LIKE、%MATCHES和%PATTERN谓词 这三个谓词允许执行模式匹配。 - `LIKE`允许使用文字和通配符进行模式匹配。 当希望返回包含已知字面值子字符串的数据值,或在已知序列中包含多个已知子字符串时,请使用`LIKE`。 `LIKE`使用目标的排序规则进行字母大小写比较。 - `%MATCHES`允许使用文字、通配符、列表和范围进行模式匹配。 当希望返回包含已知字面值子字符串的数据值,或包含一个或多个位于可能字符列表或范围内的字面值字符,或在已知序列中包含多个这样的子字符串时,请使用`%MATCHES`。 `%MATCHES`使用`EXACT`排序法进行字母大小写比较。 - 允许指定字符类型的模式。 例如,`'1U4L1",".A'`(`1`个大写字母,`4`个小写字母,一个逗号,后面跟着任意数量的字母字符)。 如果希望返回包含已知字符类型序列的数据值,请使用`%PATTERN`。 当数据值不重要,但这些值的字符类型格式很重要时,`%PATTERN`特别有用。 `PATTERN`还可以指定已知的文字字符。 它使用`EXACT`排序法进行文字比较,这总是区分大小写的。 要与字符串的第一个字符进行比较,请使用`%STARTSWITH`谓词。 # 示例 下面的示例为每个至少有一个`21`岁以下的人的州返回一行。 对于每一行,它返回该州所有人的平均、最小和最大年龄。 ```sql SELECT Home_State, MIN(Age) AS Youngest, AVG(Age) AS AvgAge, MAX(Age) AS Oldest FROM Sample.Person GROUP BY Home_State HAVING Age < 21 ORDER BY Youngest ``` 下面的示例为每个至少有一个`21`岁以下的人的州返回一行。 对于每一行,它返回该州所有人的平均、最小和最大年龄。 使用`%AFTERHAVING`关键字,它还返回该州`21`岁以下的人的平均年龄(`AvgYouth`),以及该州`21`岁以下最年长的人的年龄(`OldestYouth`)。 ```sql SELECT Home_State,AVG(Age) AS AvgAge, AVG(Age %AFTERHAVING) AS AvgYouth, MIN(Age) AS Youngest, MAX(Age) AS Oldest, MAX(Age %AFTERHAVING) AS OldestYouth FROM Sample.Person GROUP BY Home_State HAVING Age < 21 ORDER BY AvgAge ```
文章
Hao Ma · 五月 4, 2023

IRIS, Caché监控指导 - 警告和告警

当系统发生严重危机,一般错误,以及其他需要管理员关注的其他事件发生时, 管理员必须及时的收到系统发出的警告信息。 上篇文章中介绍了控制台日志。这是个文本文件,包含几乎所有的系统级别的错误信息,并且有严重级别的标识,那么使用工具来监控控制台日志文件,并给管理员发送通知是可行的方案吗? 当然。实际上,有些用户正是这样来管理Caceh'/IRIS的。他们有自己熟悉的监控工具,实时读取控制台日志,分析内容,并完成警告通知的发送。 这篇文章我来介绍的是Cache'/IRIS内置的处理警告的机制和配置方法。 通过把严重的错误和事件写入另一个文本文件alert.log,并配置邮件,SNMP, API接口发送通知,可以基本的实现caceh'/IRIS系统的警告通知工作。 ### Alerts.log 控制台日志中最严重的两个级别(级别2和3), 的记录会被写入另一个文本文件,名字是Alert.log。 默认存储于\installDir\mgr\路径。 - 在操作门户中“系统>系统日志>控制台日志” ,你会发现 Alerts.log的内容会以红色的字体显示在最上方。比如下图中有2个严重级别的Alert。 另外, 告警的条目数量会显示在系统仪表板的“错误和告警”模块中的‘严重告警’(Serious Alerts)栏。 注意,**这里的数值只显示30分钟内非启动过程中的Alert条目数**。比如上面例子中产生了一条“Failed to allocate…”的告警,因为是启动过程中的记录,因此仪表板中的严重告警数值为1。而如果30分钟内不再发生 “Winter is coming”的告警,此数值会减为0. **关于alerts.log, 您还需要知道:** - 每次系统重启该文件也会清除重置。 - 对alerts.log的内容,用户可以使用系统工具^MONMGR配置发送告警邮件 - 除了来自控制台日志,Alerts.log的记录还可以是来自其他来源 是的。底层的系统内核以及开发工具可以直接写告警信息到Alert.log, 而不通过控制台日志,应用监视器%Monitor.Adaptor就是这么一个开发工具,有些开发者使用它来向alert.log直接写入记录。 细心的读者可能注意到上面图中有个告警内容是*“Winter is coming”*,它是为了测试用以下命令产生的。 `do ##class(%SYS.System).WriteToConsoleLog("winter is coming",,2)` 这也是为什么要单独保留一个Alert日志文件,在设计者的想法里, alert.log不是只从控制台日志拿记录,它还可以有多个可能的来源。 **可以把控制台日志中的“警示性错误(Warning)”写入Alert.log吗?** 可以,但管理员必须清楚的知道这样会收到很多不严重的警示信息。通常这样做的理由是用户有自己的监控手段监控Alert.log这个文件,那么先把Warning写入Alert.log,随后用自己的方式去过滤,这是个合理的解决方案。 读取控制台日志到Alert.log的工具在早期版本中被称为Caché Monitor,这是个非常恼人的名字,好在新版本里改称为“Log Monitor”。默认的配置中,Log Monitor每10秒扫描一下控制台日志,将级别2,3的记录写入Alert.log。配置和修改log monitor使用终端工具^MONMGR,它可以修改写入错误的级别选择和采样的间隔(Monitor Interval)。 ``` %SYS>do ^MONMGR 1) Start/Stop/Update MONITOR 2) Manage MONITOR Options 3) Exit Option? 2 1) Set Monitor Interval 2) Set Alert Level 3) Manage Email Options 4) Exit Option? 2 Alert on Severity (1=warning,2=severe,3=fatal)? 2 => 1) Set Monitor Interval 2) Set Alert Level 3) Manage Email Options 4) Exit Option? 4 ``` ### Alert.log内容的发送 系统内嵌的发送通知的方式有以下几种: #### 发送告警邮件 这是系统默认的Alert的通知方式。使用^MONMGR配置配置您的邮件地址和服务器。 ``` SYS>do ^MONMGR 1) Start/Stop/Update MONITOR 2) Manage MONITOR Options 3) Exit Option? 2 1) Set Monitor Interval 2) Set Alert Level 3) Manage Email Options 4) Exit Option? 3 1) Enable/Disable Email 2) Set Sender 3) Set Server 4) Manage Recipients 5) Set Authentication 6) Test Email 7) Exit Option? ``` #### SNMP Trap 通过SNMP Trap发送通知/警告也是非常常用的方案。无论是Cache'还是IRIS, 您都可以在SNMP客户端使用SNMP收集采样指标,接收alert消息。具体的操作内容,我会在单独的文章介绍。这里只是简单的描述下步骤: - 确认操作系统的SNMP服务已启动 - 在Cache'/IRIS管理门户“System>Security Management >Services”启动%Service_Monitor服务. - 到“Monitor Setting"页面确认“the monitor service is Enabled" - 重启实例。 以下是使用snmptrapd命令接收的alert的例子: ``` Received 113 byte packet from UDP: [172.16.58.200]:60620->[0.0.0.0]:0 0000: 30 6F 02 01 00 04 06 70 75 62 6C 69 63 A4 62 06 0o.....public�b. 0016: 0A 2B 06 01 04 01 81 81 33 01 02 40 04 AC 10 3A .+......3..@.�.: 0032: C8 02 01 06 02 01 0F 43 02 5D 33 30 44 30 1A 06 �......C.]30D0.. 0048: 12 2B 06 01 04 01 81 81 33 01 01 01 01 01 04 48 .+......3......H 0064: 53 41 50 04 04 48 53 41 50 30 26 06 12 2B 06 01 SAP..HSAP0&..+.. 0080: 04 01 81 81 33 01 01 01 01 08 04 48 53 41 50 04 ....3......HSAP. 0096: 10 77 69 6E 74 65 72 20 69 73 20 63 6F 6D 69 6E .winter is comin 0112: 67 ``` 注意: snmp trap的发送发生在alerts.log的写入时。重复消息写入Alert的时限是30分钟。切换执行的进程,比如重新打开另一个terminal进程会重新计数。 #### REST API 通过REST接口查看alert内容是IRIS的新特性。下面的查看结果中有4个alert记录。原始输出的json是给机器读的,堆在一起很难看,我做了格式化,更方便人读。 注意的一点: 通过REST读取alert的内容每次只能读到新的alerts。也就是说,当再次GET的时候, 如果和上次查看的间隔中没有新alert记录出现,GET的结果是空,虽然这时候alert.log的文件里还是有很多记录。 ```json hma@CNMBPHMA ~ % curl http://localhost:52773/api/monitor/alerts [ { "time": "2023-05-04T02:12:05.647Z", "severity": "2", "message": "Preserving journal files /usr/irissys/mgr/journal/20230421.002 and later for journal recovery and transaction rollback" }, { "time": "2023-05-04T02:13:35.671Z", "severity": "2", "message": "[SYSTEM MONITOR] DiskPercentFull(/external/demo/) Alert: DiskPercentFull = 91.34, 91.34, 91.34 (Max value is 90)." }, { "time": "2023-05-04T02:13:35.671Z", "severity": "2", "message": "[SYSTEM MONITOR] DiskPercentFull(/external/oeesp/) Alert: DiskPercentFull = 91.34, 91.34, 91.34 (Max value is 90)." }, { "time": "2023-05-04T02:13:35.672Z", "severity": "2", "message": "[SYSTEM MONITOR] DiskPercentFull(/external/smart/) Alert: DiskPercentFull = 91.34, 91.34, 91.34 (Max value is 90)." } ] ``` #### Monitoring Web Service Caché Monitoring Web Service是一个提供系统采样值的Web服务,简单的说,您在用户界面上看到的所有指标,都可以从这个Web服务得到。 它的其中一个服务是EventSubscribe,用户调用这个服务,订阅Caché Event, 提供用户自己的Web服务调用地址,服务规范拷贝Monitoring Web Service中的EventSink服务,这是服务就是专门提供给客户的一个WSDL模板。当Caché产生Warning或者Alert时,Caché会调用用户的Web服务发送通知。这是一个不太常用的方案,感兴趣的用户可以查看产品手册中“Monitoing Caché Using Web Services”部分。
文章
Jingwei Wang · 六月 23, 2024

用不到150 行代码创建 IRIS 功能齐全的现代后端应用程序

低代码挑战 想象一下那个场景。您正在 Widgets Direct 愉快地工作,这是互联网上首屈一指的小部件和小部件配件零售商。您的老板有一些毁灭性的消息,一些客户可能对他们的小部件不太满意,我们需要一个帮助台应用程序来跟踪这些投诉。为了让事情变得有趣,他希望代码占用非常小,并挑战您使用 InterSystems IRIS 以少于 150 行代码交付应用程序。这可能吗? 免责声明:本文记录了一个非常基本的应用程序的构建,为了简洁起见,省略了安全性和错误处理等细节。该应用程序仅供参考,不得用于任何生产应用。本文使用IRIS 2023.1作为数据平台,并非所描述的所有功能在早期版本中都可用 第 1 步 - 定义数据模型 我们首先定义一个新的干净的命名空间 - 带有代码和数据数据库。虽然所有内容都可以位于 1 个数据库中,但将它们拆分以便于数据刷新。 我们的帮助台系统需要 3 个基本类:一个 Ticket 对象,它可以包含用于记录员工顾问 UserAccount 和客户联系人 UserAccount 之间交互的操作。让我们用一些基本属性来定义它们: 19 行代码,我们就有了完整的数据模型!我们将 2 个类设置为 Persistent,以便将它们保存到数据库中,并且还继承自 %JSON.Adapter,这使我们能够非常轻松地以 JSON 格式导入和导出对象。作为测试,我们在终端中设置第一个用户,保存它,然后验证 JSONExport 是否正常工作 这一切看起来都不错。老板给我们留下了一个 csv 文件,其中包含员工和客户的列表。我们可以编写一些代码来解析它并加载它,但是有更简单的方法吗? 第 2 步 - 加载数据 InterSystems IRIS 在 SQL 中提供了一个简单易用的 LOAD DATA 语句,可以轻松地从 CSV 文件加载数据,包括解析标题和重命名字段的选项。让我们用它来加载我们的用户表: 我们可以使用标题标签来提取这些数据并将其加载到数据库中,如下所示: 所有 300 行均已使用单个命令导入。这 4 行代码使我们的运行代码编写数量达到 23 行。我们可以使用基本的 SQL select 查询脚本来快速验证这些记录是否正确 现在我们有了起始数据,让我们构建一些基本的 API 来和前端连接。我们将把 API 构建可以接受 JSON 的 REST 服务。 第 3 步 - 构建 REST API InterSystems IRIS 通过继承 %CSP.REST 类提供 REST 支持,因此我们将创建一个继承 %CSP.REST 的REST.Dispatch 类。该类由 2 个部分组成:一个 XData UrlMap,它将 URL 和 Call 映射到Method,通过 Url 调用Method。 我们的最小可行产品需要 4 项操作:检索员工或客户的用户列表、检索最后一张票、通过 ID 号检索单个票,最后创建新票。我们定义Call,然后定义Method。 GetUserList 是一个基本的嵌入式 SQL 游标,它直接以 JSON 形式输出数据。然后,我们可以使用 JSON 功能解析它,将其推送到 JSON 数组中并将其作为响应正文。我们将 Staff 变量从 URL 直接传递到方法中使用。 TicketSummary 几乎相同,但改为访问票证表 TicketSummary 是最简单的服务。我们通过 ID 获取对象,并使用内置的 %JSONExport 输出JSON。如果对象加载失败,我们会写出一个错误数据包 最后,我们的 UploadTicket 方法是最复杂的。我们必须从请求对象中读取有效票证数据,将其解析为 JSON,然后使用 %JSONImport 将其导入到 Ticket 的新实例。我们还从当前时间设置 OpenDate 和 OpenTime,而不是将日期作为输入。成功保存后,我们会显示新票证对象的 JSON 表示形式,或者如果对象保存失败,我们会返回错误。 通过这些服务,我们在总数中又添加了 60 行代码。我们现在这个应用程序总共有 89 行代码 现在,我们需要在“安全”>“应用程序”下创建一个 Web 应用程序。这应该设置为 REST 类型的应用程序,并且 Dispatch Classname 应设置为我们刚刚创建的 REST.Dispatch 类(注意,您需要向该应用程序授予适当的安全角色,以便它可以访问代码和数据)。保存后,现在可以从您定义的 URL 调用 REST 服务 我们可以调用UserList来检查 我们现在准备创建一些数据。让我们使用 REST 客户端将有效数据发送到票证创建服务。我们提供关键字、描述、顾问和联系人,然后返回我们创建的票证的 JSON,包括 OpenDate 和 TicketId 我们现在有了最小可行产品。使用我们选择的前端表单生成器,我们现在可以通过 REST 服务发送和接收票证信息。 第 4 步 - 互操作性要求 您现在仅用 89 行书面代码就构建了一个基本的票务应用程序。你的老板一定印象深刻吧?是的,但他有一些坏消息。你错过了一个要求。 Widgets Direct 在法语地区有一份特殊合同,所有用法语书写的门票必须交由 Bettie Francis 女士进行初步审核。幸运的是,您有一位同事关注了 Robert Luman 关于Python Natural Language Support 的优秀文章,并创建了一个可以接受文本示例并识别语言的 REST 服务。当文本为法语时,我们可以使用 InterSystems IRIS Interoperability 来调用此服务并自动将顾问更新为 Mme Francis 吗? 我们需要首先创建一些 Message 类,以便我们有一种方法来发送和接收我们的请求。我们需要一个包含票证 ID 和示例文本的请求,以及一个返回语言代码和描述的响应。这些将继承Ens.Request 和 Ens.Response 再写 6 行代码,我们就可以得到 95 个 LOC。我们现在需要创建我们的业务操作,它将请求发送到您同事的服务并检索答案。我们定义一个出站业务操作,其中包含服务器和 URL 的属性,并通过将它们包含在 SETTINGS 参数中来将它们公开给用户配置。如果服务器路径发生变化,这将使我们能够轻松更新请求。我们创建一个辅助方法来设置 HTTPRequest,然后使用它来调用服务并填充我们的响应 又增加了 27 行代码,代码数量就超过了 100 行,现在我们已经编写了 122 行代码。我们现在需要在 Ensemble 中设置此类。转到互操作性下的Production配置,然后按操作标题下的添加。使用类名称和显示名称设置您的业务操作 然后我们可以单击它进入“设置”,输入服务器名称和 URL 并启用该业务操作。 我们现在需要第二个业务操作,该业务操作获取票证 ID 并将顾问设置为提供的用户帐户 ID。我们需要消息和业务操作类,但在这种情况下,我们不会返回响应,业务操作将在没有反馈的情况下执行任务 另外 12 行代码使我们编写了 134 行代码。我们可以按照添加服务的相同方式将此业务操作添加到Production中,但在这种情况下,我们无需设置任何配置。 接下来,我们需要一个可以调用服务、评估响应并可以选择调用 French Advisor Operations 的路由器。我们转到互操作性>构建>业务流程并访问可视化规则生成器。我们首先设置请求和响应的上下文并添加一个调用项。我们将输入和输出设置为我们创建的消息类,然后使用请求生成器映射输入。确保未选中“异步”标志,我们希望在继续之前等待响应。 然后,我们添加一个“If”项来评估返回的语言代码。如果是“fr”,那么我们要调用 FrenchAdvisor 操作 Mme Francis 的用户 ID 为 11,因此我们设置 Call 对象以向 FrenchAdvisor 服务提供 AdvisorUpdate 消息,并使用请求构建器传递 TicketID 和 Advisor 参数的固定值 11 现在,我们可以通过单击“进程”标题下的“添加”,选择该类并为其指定一个 DisplayName“FrenchRouter”,将其添加到 Production 中。 现在我们的路由就位了。我们只需要一个服务来扫描新票并将其发送到路由器进行处理。我们基于 SQL 适配器定义一个 Service 类,如下所示(在我们的计数中添加另外 8 行代码): 然后,我们将其添加到Production中,就像我们对业务操作和业务流程对象所做的那样。我们需要对 SQL 适配器进行一些设置。我们通过 ODBC DSN 向本地数据库提供连接详细信息,以及基本 SQL 查询,服务将使用该查询来查询 CallInterval 设置中设置的计时器上的票证。此查询与键字段名称设置配对,该设置定义查询的唯一键,并防止重新发送已发送的记录 完成此业务操作后,我们现在有了完整的Production,它将扫描新票证,将文本传递到外部服务以解析语言,并可选择根据响应重置顾问程序。让我们试试这个吧!我们首先发送一个英语请求,该请求返回为 TicketId 70。我们稍等一下,然后通过 GetTicket REST 服务访问此记录,并且顾问与原始请求相比没有变化 让我们用法语文本重复一遍 当我们请求 71 票证时,我们的顾问已更改为弗朗西斯女士,正如我们所料!我们可以通过在 Visual Trace 中查找消息并验证操作是否按预期调用来在互操作性中检查这一点。 我们现在编写了 142 行代码,并且拥有一个 InterSystems IRIS 应用程序,该应用程序可以保存数据,使用 LOAD DATA 加载数据,提供用于查看和编辑数据的基本 REST API,以及基于调用提供决策支持的高级集成引擎到外部 REST 服务。当然,没有人能要求更多吗? 第 5 步 - 要求更多:分析 您的应用程序取得了巨大成功,数据源源不断地涌入。这些宝贵的数据需要专业知识才能访问,而 Widgets Direct 的管理人员希望获得一些见解。是否可以提供对数据的交互式访问? 使用 InterSystems IRIS Analytics,我们可以轻松快速地访问高级数据操作工具。我们首先需要在我们的内部 Web 应用程序上启用分析支持: 这使我们能够针对我们的命名空间使用分析部分。首先打开 Analytics>Architect。选择新建,然后填写表单以为工单类创建分析多维数据集。 接下来,我们可以使用 Visual Builder 设置维度和基本的向下钻取列表。该视图是拖放式的。还可以使用简单的可视化构建器创建列表,以自定义用户在询问数据点时看到的内容 一旦我们有了一些基本设置,我们就可以保存、编译和构建Cube。这将设置所有索引并启用多维数据集以在分析器中进行分析。打开分析器开始处理数据。在示例中,我们可以轻松地将顾问与按所服务的联系人过滤的年度和季度层次结构进行比较。单击单元格后,可以按下双筒望远镜图标来调用您创建的深入分析列表,以进行进一步分析和导出 结论 只需 142 行代码,我们就拥有了一个基本但功能齐全的现代后端应用程序,并提供支持工具以允许应用程序间通信和高级分析。这是一个过于简化的实现,只能用作在 IRIS 中构建数据库应用程序所需的基本框架的示例。正如本文开头提到的,此代码尚未准备好投入生产,在实际使用中,开发人员应参考 InterSystems IRIS 文档和最佳实践,以确保其代码稳健、安全和可扩展(这些都不适用于此代码库) )。快乐编码!该文章 作者 @Chris Stewart 来源于 https://community.intersystems.com/node/553856
文章
姚 鑫 · 三月 29, 2021

第十三章 使用动态SQL(七)

# 第十三章 使用动态SQL(七) # SQL元数据 动态SQL提供以下类型的元数据: - 在“准备”之后,描述查询类型的元数据。 - 在“准备”之后,描述查询中选择项的元数据(“列”和“扩展列信息”)。 - 在准备之后,描述查询参数的元数据:参数,`:var`参数和常量。 (语句参数,形式参数和对象) - 执行之后,描述查询结果集的元数据。在执行Prepare操作(`%Prepare()`,`%PrepareClassQuery()`或`%ExecDirect()`)之后,可以使用`%SQL.StatementMetadata`属性值。 - 可以直接为最新的`%Prepare()`返回`%SQL.Statement`元数据属性。 - 可以返回包含`%SQL.StatementMetadata`属性的oref的`%SQL.Statement%Metadata`属性。这使可以返回多个准备操作的元数据。 `SELECT`或`CALL`语句返回所有这些元数据。 `INSERT`,`UPDATE`或`DELETE`返回语句类型元数据和形式参数。 ## 语句类型元数据 使用`%SQL.Statement`类进行`Prepare`之后,可以使用`%SQL.StatementMetadata statementType`属性来确定准备哪种类型的SQL语句,如以下示例所示。本示例使用`%SQL.Statement%Metadata`属性来保存和比较两`个Prepare`操作的元数据: ```java /// d ##class(PHA.TEST.SQL).MetaData() ClassMethod MetaData() { SET tStatement = ##class(%SQL.Statement).%New() SET myquery1 = "SELECT TOP ? Name,Age,AVG(Age),CURRENT_DATE FROM Sample.Person" SET myquery2 = "CALL Sample.SP_Sample_By_Name(?)" SET qStatus = tStatement.%Prepare(myquery1) IF qStatus'=1 { WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT } SET meta1 = tStatement.%Metadata SET qStatus = tStatement.%Prepare(myquery2) IF qStatus'=1 { WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT } SET meta2 = tStatement.%Metadata WRITE "语句类型query 1: ",meta1.statementType,! WRITE "语句类型query 2: ",meta2.statementType,! WRITE "End of metadata" } ``` ```java DHC-APP>d ##class(PHA.TEST.SQL).MetaData() 语句类型query 1: 1 语句类型query 2: 45 End of metadata ``` `statementType`属性的“类引用”条目列出了语句类型整数代码。最常见的代码是1(`SELECT`查询)和45(`CALL`到存储的查询)。 可以使用`%GetImplementationDetails()`实例方法返回相同的信息,如成功准备的结果中所述。 执行查询后,可以从结果集中返回语句类型名称(例如`SELECT`)。 ## 选择项目Select-item元数据 使用`%SQL.Statement`类准备`SELECT`或`CALL`语句之后,可以通过显示所有元数据或指定各个元数据项来返回有关查询中指定的每个选择项列的元数据。此列元数据包括ODBC数据类型信息,以及客户端类型和InterSystems Objects属性的起源以及类类型信息。 以下示例返回最近准备的查询中指定的列数: ```java /// d ##class(PHA.TEST.SQL).MetaData1() ClassMethod MetaData1() { SET myquery = "SELECT %ID AS id,Name,DOB,Age,AVG(Age),CURRENT_DATE,Home_State FROM Sample.Person" SET tStatement = ##class(%SQL.Statement).%New() SET qStatus = tStatement.%Prepare(myquery) IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT} WRITE "Number of columns=",tStatement.%Metadata.columnCount,! WRITE "End of metadata" } ``` ```java DHC-APP>d ##class(PHA.TEST.SQL).MetaData1() Number of columns=7 End of metadata ``` 以下示例返回列名称(或列别名),ODBC数据类型,最大数据长度(精度),以及每个`SELECT`项目字段的比例: ```java /// d ##class(PHA.TEST.SQL).MetaData2() ClassMethod MetaData2() { SET $NAMESPACE="SAMPLES" SET myquery=2 SET myquery(1)="SELECT Name AS VendorName,LastPayDate,MinPayment,NetDays," SET myquery(2)="AVG(MinPayment),$HOROLOG,%TABLENAME FROM Sample.Vendor" SET rset = ##class(%SQL.Statement).%New() SET qStatus = rset.%Prepare(.myquery) IF qStatus'=1 { WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT } SET x=rset.%Metadata.columns.Count() SET x=1 WHILE rset.%Metadata.columns.GetAt(x) { SET column=rset.%Metadata.columns.GetAt(x) WRITE !,x," ",column.colName," 是数据类型 ",column.ODBCType WRITE " 大小为 ",column.precision," 规模 = ",column.scale SET x=x+1 } WRITE !,"End of metadata" } ``` ```java DHC-APP>d ##class(PHA.TEST.SQL).MetaData2() 1 VendorName 是数据类型 12 大小为 50 规模 = 0 2 LastPayDate 是数据类型 9 大小为 10 规模 = 0 3 MinPayment 是数据类型 8 大小为 6 规模 = 0 4 NetDays 是数据类型 4 大小为 3 规模 = 0 5 Aggregate_5 是数据类型 8 大小为 20 规模 = 0 6 Expression_6 是数据类型 12 大小为 255 规模 = 0 7 Literal_7 是数据类型 12 大小为 13 规模 = 0 End of metadata ``` 下面的示例使用`%SQL.StatementMetadata%Display()`实例方法显示所有列元数据: ```java /// d ##class(PHA.TEST.SQL).MetaData3() ClassMethod MetaData3() { SET tStatement = ##class(%SQL.Statement).%New() SET qStatus = tStatement.%Prepare("SELECT %ID AS id,Name,DOB,Age,AVG(Age),CURRENT_DATE,Home_State FROM Sample.Person") IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT} DO tStatement.%Metadata.%Display() WRITE !,"End of metadata" } ``` ```java DHC-APP>d ##class(PHA.TEST.SQL).MetaData3() Columns (SQLRESULTCOL, property 'columns'): Column Name Type Prec Scale Null Label Table Schema CType ----------- ---- ---- ----- ---- ------------ ------------ ------------ ----- id 4 10 0 0 id Person Sample 5 Name 12 50 0 0 Name Person Sample 10 DOB 9 10 0 1 DOB Person Sample 2 Age 4 10 0 1 Age Person Sample 5 Aggregate_5 2 20 8 1 Aggregate_5 14 Expression_6 9 11 0 2 Expression_6 2 Home_State 12 2 0 1 Home_State Person Sample 10 Extended Column Info (SQLRESULTCOL) Flags: 1:AutoIncrement,2:CaseSensitive,3:Currency,4:ReadOnly,5:RowVersion, 6:Unique,7:Aliased,8:Expression,9:Hidden,10:Identity,11:KeyColumn, 12:RowId Column Name Linked Prop Type Class Flags ------------ --------------------- --------------------- ----------------------- id Sample.Person Y,N,N,Y,N,Y,Y,N,N,Y,Y,Y Name Sample.Person.Name %Library.String N,N,N,N,N,N,N,N,N,N,N,N DOB Sample.Person.DOB %Library.Date N,N,N,N,N,N,N,N,N,N,N,N Age Sample.Person.Age %Library.Integer N,N,N,N,N,N,N,N,N,N,N,N Aggregate_5 %Library.Numeric N,N,N,Y,N,N,Y,N,N,N,N,N Expression_6 %Library.Date N,N,N,Y,N,N,Y,Y,N,N,N,N Home_State Sample.Address.State %Library.String N,N,N,N,N,N,N,N,N,N,N,N Statement Parameters (property 'parameters'): Nbr. Type precision scale nullable colName columntype ---- ---- --------- ----- -------- ------------ ---------- Formal Parameters (property 'formalParameters'): Nbr. Type precision scale nullable colName columntype ---- ---- --------- ----- -------- ------------ ---------- Objects: Col Column Name Extent ExportCall --- ----------- ----------------- ----------------------------- 1 id Sample.Person ##class(Sample.Person).%SQLQuickLoad ``` 这将返回所选字段的两个表列表。第一列元数据表列出了列定义信息: 显示标题 | `%SQL.StatementColumn`属性 | 描述 ---|---|--- Column Name | colName |列的SQL名称。如果为该列提供了别名,则会在此处列出该列的别名,而不是字段名称。名称和别名将被截断为12个字符。对于表达式,聚合,文字,主机变量或子查询,列出了分配的`“ Expression_n”`,`“ Aggregate_n”`,`“ Literal_n”`,`“ HostVar_n”`或`“ Subquery_n”`标签(`n`为`SELECT`项序列号)。如果为表达式,聚合,文字,主机变量或子查询分配了别名,则在此处列出该别名。 Type| ODBCType |ODBC数据类型的整数代码。请注意,这些ODBC数据类型代码与CType数据类型代码不同。 Prec| precision|精度或最大长度(以字符为单位)。日期,时间,PosixTime和TimeStamp数据类型中描述了TIME数据类型的精度和小数位元数据。 Scale| scale| 小数位数的最大数目。对于整数或非数值返回0。日期,时间,PosixTime和TimeStamp数据类型中描述了`TIME`数据类型的精度和小数位元数据。 Null| isNullable| 一个整数值,指示是否将列定义为`Non-NULL(0)`,或者是否允许`NULL(1)`。 RowID返回0。如果`SELECT`项是可能导致`NULL`的聚合或子查询,或者如果它指定`NULL`文字,则该项设置为1。如果`SELECT`项是表达式或主机变量,则设置此项到2(无法确定)。 Label| label| 列名或列别名(与列名相同)。 Table| tableName| SQL表名称。即使为表指定了别名,也始终在此处列出实际的表名。如果`SELECT`项是表达式或聚合,则不会列出任何表名。如果`SELECT`项是子查询,则列出子查询表名称。 Schema| schemaName|表的架构名称。如果未指定架构名称,则返回系统范围的默认架构。如果`SELECT`项是表达式或聚合,则不会列出任何模式名称。如果SELECT项是子查询,则不会列出任何架构名称。 CType| clientType| 客户端数据类型的整数代码。 第二列元数据表列出了扩展列信息。扩展列信息表列出了具有十二个布尔标志(SQLRESULTCOL)的每一列,这些标志被指定为Y(是)或N(否): 显示标题 | `%SQL.StatementColumn`属性 | 描述 ---|---|--- 1: AutoIncrement| isAutoIncrement| TRowID和IDENTITY字段返回Y。 2: CaseSensitive| isCaseSensitive |具有`%EXACT`归类的字符串数据类型字段返回Y。引用`%SerialObject`嵌入式对象的属性返回Y。 3: Currency| isCurrency| 使用%Library.Currency数据类型定义的字段,例如`MONEY`数据类型。 4: ReadOnly |isReadOnly|表达式,聚合,文字,`HostVar`或子查询返回Y。RowID,IDENTITY和RowVersion字段返回Y。 5: RowVersion| isRowVersion|RowVersion字段返回Y。 6: Unique| isUnique| 定义为具有唯一值约束的字段。 RowID和IDENTITY字段返回Y。 7: Aliased| isAliased| 系统为非字段选择项提供别名。因此,无论用户是否通过指定列别名替换了系统别名,表达式,聚合,文字,HostVar或子查询都将返回Y。此标志不受用户指定的列别名的影响。 8: Expression| isExpression| 表达式返回Y。 9: Hidden| isHidden| 如果使用`%PUBLICROWID`或`SqlRowIdPrivate = 0`(默认值)定义表,则RowID字段返回N。否则,RowID字段返回Y。引用`%SerialObject`嵌入式对象的属性返回Y。 10: Identity| isIdentity| 定义为IDENTITY字段的字段返回Y。如果未隐藏RowID,则RowID字段返回Y。 11: KeyColumn| isKeyColumn| 定义为主键字段或外键约束目标的字段。 RowID字段返回Y。 12: RowID| isRowId |ROWID和Identity字段返回Y. 扩展列信息元数据表列出了每个选定字段的列名称(SQL名称或列别名),链接属性(链接的持久性类属性)和类型类(数据类型类)。请注意,链接属性列出了持久性类名(不是SQL表名)和属性名(不是列别名)。 - 对于普通表字段(`SELECT Name FROM Sample.Person`): `Linked Prop=Sample.Person.Name, Type Class=%Library.String`. - 对于表格的RowID (`SELECT %ID FROM Sample.Person`): `Linked Prop= [none], Type Class=Sample.Person`. - 对于表达式,聚合,文字,`HostVar`或子查询 (`SELECT COUNT(Name) FROM Sample.Person`): `Linked Prop= [none], Type Class=%Library.BigInt`. - 供参考`%Serial Object`嵌入式对象属性 (`SELECT Home_State FROM Sample.Person`). `Linked Prop=Sample.Address.State, Type Class=%Library.String.` - 对于引用`%SerialObject`嵌入式对象的字段(`SELECT Home FROM Sample.Person`). `Linked Prop=Sample.Person.Home, Type Class=Sample.Address`. 在此示例中,`Sample.Person`中的`Home_State`字段引用`%SerialObject`类`Sample.Address`的`State`属性。 下面的示例返回带有一个形式参数(也就是语句参数)的被调用存储过程的元数据: ```java /// d ##class(PHA.TEST.SQL).MetaData4() ClassMethod MetaData4() { SET $NAMESPACE="SAMPLES" SET mysql = "CALL Sample.SP_Sample_By_Name(?)" SET tStatement = ##class(%SQL.Statement).%New() SET qStatus = tStatement.%Prepare(.mysql) IF qStatus'=1 { WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT } DO tStatement.%Metadata.%Display() WRITE !,"End of metadata" } ``` 它不仅返回列(字段)信息,还返回语句参数,形式参数和对象的值。 以下示例返回具有三个形式参数的的元数据。这三个参数之一用问号(`?`)指定,使其成为语句参数: ```java /// d ##class(PHA.TEST.SQL).MetaData5() ClassMethod MetaData5() { SET $NAMESPACE="SAMPLES" SET mycall = "CALL personsets(?,'MA')" SET tStatement = ##class(%SQL.Statement).%New(0,"sample") SET qStatus = tStatement.%Prepare(mycall) IF qStatus'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT} DO tStatement.%Metadata.%Display() WRITE !,"End of metadata" } ``` ```java DHC-APP>d ##class(PHA.TEST.SQL).MetaData5() Columns (SQLRESULTCOL, property 'columns'): Column Name Type Prec Scale Null Label Table Schema CType ----------- ---- ---- ----- ---- ------------ ------------ ------------ ----- Extended Column Info (SQLRESULTCOL) Flags: 1:AutoIncrement,2:CaseSensitive,3:Currency,4:ReadOnly,5:RowVersion, 6:Unique,7:Aliased,8:Expression,9:Hidden,10:Identity,11:KeyColumn, 12:RowId Column Name Linked Prop Type Class Flags ------------ --------------------- --------------------- ----------------------- Statement Parameters (property 'parameters'): Nbr. Type precision scale nullable colName columntype ---- ---- --------- ----- -------- ------------ ---------- 1 12 50 0 2 name 1 Formal Parameters (property 'formalParameters'): Nbr. Type precision scale nullable colName columntype ---- ---- --------- ----- -------- ------------ ---------- 1 4 4 0 2 _isc_sp_ret_val 5 2 12 50 0 2 name 1 3 12 50 0 2 state 1 Objects: Col Column Name Extent ExportCall --- ----------- ----------------- ----------------------------- End of metadata ``` 请注意,此元数据不返回任何列信息,但是“语句参数”,“形式参数”列表包含列名称和数据类型。 ## Query参数元数据 使用`%SQL.Statement`类进行`Prepare`之后,您可以返回有关查询参数的元数据:输入参数(指定为问号(`?`)),输入主机变量(指定为`:varname`)和常量(文字值)。可以返回以下元数据: - `?`参数`:parameterCount`属性 - ODBC数据类型为`?`参数`:%SQL.StatementMetadata%Display()`实例方法“语句参数”列表。 - ?,v(:var)和c(常量)参数的列表:`%GetImplementationDetails()`实例方法,如成功准备的结果中所述。 - ?,v(:var)和c(常量)参数的ODBC数据类型:`formalParameters`属性。 `%SQL.StatementMetadata%Display()`实例方法“形式参数”列表。 - 查询文本,其中显示以下参数:`%GetImplementationDetails()`实例方法,如成功准备结果中所述。 语句元数据`%Display()`方法列出了“语句参数”和“形式参数”。对于每个参数,它列出了顺序参数号,ODBC数据类型,精度,小数位数,该参数是否可为空(2表示始终提供一个值)及其对应的属性名称(colName)和列类型。 请注意,某些ODBC数据类型以负整数形式返回。 下面的示例按顺序返回每个查询参数(`?`,`:var`和常量)的ODBC数据类型。请注意,`TOP`参数以数据类型12(`VARCHAR`)而不是数据类型4(`INTEGER`)返回,因为可以指定`TOP ALL`: ```java /// d ##class(PHA.TEST.SQL).MetaData6() ClassMethod MetaData6() { SET myquery = 4 SET myquery(1) = "SELECT TOP ? Name,DOB,Age+10 " SET myquery(2) = "FROM Sample.Person" SET myquery(3) = "WHERE %ID BETWEEN :startid :endid AND DOB=?" SET myquery(4) = "ORDER BY $PIECE(Name,',',?)" SET tStatement = ##class(%SQL.Statement).%New() SET qStatus = tStatement.%Prepare(.myquery) IF qStatus'=1 { WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT } SET prepmeta = tStatement.%Metadata WRITE "Number of ? parameters=",prepmeta.parameterCount,! SET formalobj = prepmeta.formalParameters SET i=1 WHILE formalobj.GetAt(i) { SET prop=formalobj.GetAt(i) WRITE prop.colName," type= ",prop.ODBCType,! SET i=i+1 } WRITE "End of metadata" } ``` 执行`Execute`之后,无法从查询结果集元数据中获取参数元数据。在结果集中,所有参数均已解析。因此`parameterCount = 0`,`formalParameters`不包含任何数据。 ## Query结果集元数据 使用`%SQL.Statemen`t类执行`Execute`之后,可以通过调用返回结果集元数据: - `%SQL.StatementResult`类的属性。 - `%SQL.StatementResult%GetMetadata()`方法,访问`%SQL.StatementMetadata`类属性。 ### %SQL.StatementResult属性 执行查询操作后,`%SQL.StatementResult`返回: - `%StatementType`属性返回与最近执行的SQL语句相对应的整数代码。以下是这些整数代码的部分列表:`1 = SELECT; 2 = INSERT; 3 = UPDATE; 4 = DELETE or TRUNCATE TABLE; 9 = CREATE TABLE; 15 = CREATE INDEX; 45 = CALL`. - `%StatementTypeName`计算的属性基于`%StatementType`返回最近执行的SQL语句的命令名称。此名称以大写字母返回。请注意,`TRUNCATE TABLE`操作将作为`DELETE`返回。即使执行了更新操作,`INSERT OR UPDATE`也将作为`INSERT`返回。 - `%ResultColumnCount`属性返回结果集行中的列数。 下面的示例显示这些属性: ```java /// d ##class(PHA.TEST.SQL).MetaData7() ClassMethod MetaData7() { SET myquery = "SELECT TOP ? Name,DOB,Age FROM Sample.Person WHERE Age > ?" 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(10,55) IF rset.%SQLCODE=0 { WRITE "Statement type=",rset.%StatementType,! WRITE "Statement name=",rset.%StatementTypeName,! WRITE "Column count=",rset.%ResultColumnCount,! WRITE "End of metadata" } ELSE { WRITE !,"SQLCODE=",rset.%SQLCODE," ",rset.%Message } } ``` ### %SQL.StatementResult %GetMetadata() 执行之后,可以使用`%SQL.StatementResult %GetMetadata()`方法访问`%SQL.StatementMetadata`类属性。这些是在Prepare之后由`%SQL.Statement%Metadata`属性访问的相同属性。 以下示例显示了属性: ```java /// d ##class(PHA.TEST.SQL).MetaData8() ClassMethod MetaData8() { SET myquery=2 SET myquery(1)="SELECT Name AS VendorName,LastPayDate,MinPayment,NetDays," SET myquery(2)="AVG(MinPayment),$HOROLOG,%TABLENAME FROM Sample.Vendor" 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() IF rset.%SQLCODE=0 { SET rsmeta=rset.%GetMetadata() SET x=rsmeta.columns.Count() SET x=1 WHILE rsmeta.columns.GetAt(x) { SET column=rsmeta.columns.GetAt(x) WRITE !,x," ",column.colName," is data type ",column.ODBCType WRITE " with a size of ",column.precision," and scale = ",column.scale SET x=x+1 } } ELSE { WRITE !,"SQLCODE=",rset.%SQLCODE," ",rset.%Message } WRITE !,"End of metadata" } ``` 请注意,结果集元数据不提供参数元数据。这是因为`Execute`操作会解析所有参数。因此,在结果集中,`parameterCount = 0`,而`formalParameters`不包含任何数据。 # 审核动态SQL InterSystems IRIS支持动态SQL语句的可选审核。启用%System /%SQL / DynamicStatement系统审核事件时,将执行动态SQL审核。默认情况下,未启用此系统审核事件。 如果启用%System /%SQL / DynamicStatement,则系统将自动审核在系统范围内执行的每个`%SQL.Statement`动态语句。审核将信息记录在审核数据库中。 要查看审核数据库,请依次转到管理门户,系统管理,选择安全性,审核,然后查看审核数据库。可以将“事件名称”过滤器设置为DynamicStatement,以将View Audit Database限制为Dynamic SQL语句。审核数据库列出了时间(本地时间戳),用户,PID(进程ID)和事件的描述。说明指定动态SQL语句的类型。例如,SQL SELECT语句(`%SQL.Statement`)或SQL CREATE VIEW语句(`%SQL.Statement`)。 通过选择事件的详细信息链接,可以列出其他信息,包括事件数据。事件数据包括执行的SQL语句和该语句的任何参数的值。例如: ```java SELECT TOP ? Name , Age FROM Sample . MyTest WHERE Name %STARTSWITH ? /*#OPTIONS {"DynamicSQLTypeList":",1"} */ Parameter values: %CallArgs(1)=5 %CallArgs(2)="Fred" ``` 事件数据的总长度(包括语句和参数)为3,632,952个字符。如果该语句和参数长于3632952,则事件数据将被截断。 InterSystems IRIS还支持ODBC和JDBC语句的审核(事件名称= XDBCStatement),以及嵌入式SQL语句的审核(事件名称= EmbeddedStatement)。
文章
Michael Lei · 七月 4, 2021

Gartner DBMS 魔力象限中的主要领先数据库产品功能对比

大家好, 在本文中,我比较了 Gartner 最新DBMS 魔力象限中的主要领先数据库产品的功能。 请见按现有功能数量排序的列表。 1. InterSystems IRIS 2020.3 - 60 个功能 (https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls) 2. Oracle Database 21c - 54 个功能 (https://docs.oracle.com/en/database/oracle/oracle-database/index.html) 3. Microsoft SQL Server - 45 个功能 (https://docs.microsoft.com/en-us/sql/sql-server/?view=sql-server-ver15) 4. AWS Aurora - PostgreSQL - 34 个功能 (https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/CHAP_AuroraOverview.html) 我只比较了功能,未进行任何性能比较(关于此内容,请参见性能测试:https://cn.community.intersystems.com/post/intersystems-iris%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%EF%BC%9A%E6%95%B0%E6%8D%AE%E6%8E%A5%E6%94%B6%E9%80%9F%E5%BA%A6%E6%B5%8B%E8%AF%95)。 我使用了上面的产品官方文档链接。 请见比较表格: <colgroup><col style="width:245pt" width="327"><col style="width:88pt" width="118"><col style="width:122pt" width="162"><col style="width:150pt" width="200"><col style="width:146pt" width="195"></colgroup> 功能 InterSystemsIRIS 2020.3 OracleDatabase 21c MicrosoftSQL Server 2020 AWS Aurora -PostgreSQL 故障转移集群 有 有 有 有 镜像/数据复制 有 有 有 有 分布式缓存/内存中支持 有 有 有 有 备份/恢复 - 增量和完整 有 有 有 有 纵向缩放 有 有 有 有 针对 Insert、Update 和 Delete 进行横向缩放 有 有 无 无 针对 Select 进行横向缩放 有 有 有 有 分片集群 有 有 无 有 云支持和云管理器 有 有 有 有 Kubernetes 支持和 Kubernetes 管理器 有 有 有 有 Docker 支持 有 有 有 无 AWS 托管 有 有 有 有 Azure 托管 有 有 有 无 Google Cloud 托管 有 有 有 无 托管云 有 有 有 有 内部部署支持 有 有 有 无 多模型 - OO 有 无 无 无 多模型 - 文档 - JSON 有 有 有 有 多模型 - XML 有 有 有 有 多模型 - 键/值 有 无 无 无 多模型 - SQL 有 有 有 有 多模型 - 空间 无 有 有 有 多模型 - 图表 无 有 有 无 多模型 - OLAP 多维数据集 有 有 有 无 GIS 平台 无 有 无 无 本机 OO 编程语言 有 有 无 无 Java、.Net、Python、C/C++ 和 PHP 支持 有 有 有 有 Node.js 支持 有 有 有 有 ODBC/JDBC 支持 有 有 有 有 后端应用程序开发 有 有 无 无 前端应用程序开发 有 有 无 无 低代码 Web 应用程序开发 无 有 有 无 数据库应用程序开发 有 有 有 有 OData 支持 无 有 有 无 REST 服务 有 有 有 有 SOAP 服务 有 无 无 无 终端工具 有 有 有 有 IDE 支持 有 有 有 有 Web 管理/IDE 支持 有 有 有 有 嵌入 NLP 有 无 无 无 嵌入 AutoML 有 无 无 无 R/机器学习支持 有 有 有 有 PMML 有 无 无 无 业务报表服务器/开发 有 有 有 无 自主 AI 操作 无 有 无 无 非结构化文本注释/类似 Apache UIMA 有 有 无 无 Spark 支持 有 有 有 有 BI 工具 有 无 有 无 MDX 支持 有 有 有 无 互操作性连接器 有 无 无 无 BPEL/集成编排/工作流 有 无 无 无 ETL - 提取、转换和加载数据 有 无 无 无 IoT/MQTT 支持 有 无 无 无 EDI 支持 有 无 无 无 ESB 有 无 无 无 CDC - 变更数据捕获 有 有 有 有 RBAC 模型 有 有 有 有 LDAP 支持 有 有 有 有 双因素授权/身份验证支持 有 有 有 有 加密 有 有 有 有 标记 有 有 有 无 审计和跟踪 有 有 有 有 SAM 有 有 有 有 多操作系统支持 有 有 有 有 SAML/Oauth/OpenID 支持 有 有 有 有 性能调整 IDE/包 无 有 无 无 特权用户访问管理 无 有 无 无 API 管理 有 无 无 无 功能总数 61 54 45 34
文章
Michael Lei · 九月 15, 2022

示例:使用 Java + SpringBoot + Hibernate 和 IRIS 数据库创建 REST API

Spring Boot 是最常用来创建 REST API 和微服务的 Java 框架。 它可用于部署 Web 应用程序、可执行 Web 应用程序或桌面自包含应用程序,其中应用程序和其他依赖项打包在一起。 Spring Boot 允许执行许多功能,请参见: 注:要了解有关 SpringBoot 的信息,请参见官方网站 - https://spring.io/quickstart 要创建具有一个或多个微服务的 Web api 应用程序,可以使用 Spring IDE for Eclipse/VSCode,并使用向导配置上述将在应用程序中使用的技术,请参见: ![](/sites/default/files/inline/images/images/image(1388).png) 您可以选择技术并创建项目。 所有技术都将通过 maven 导入。 它就像一个可视化的 zpm。 所创建的项目有一个类用于为应用程序提供支持,其中包含所有所需内容(Web 和应用程序服务器,以及所有依赖项、微服务概念)。 此项目的完整源代码在以下 open exchange 项目中: https://openexchange.intersystems.com/package/springboot-iris-crud。 首先要配置 IRIS JDBC 驱动程序和 IRIS Hibernate 支持,为此,请将 jar 文件复制到 resources 文件夹,请参见: ![](/sites/default/files/inline/images/images/image(1390).png) 打开 pom.xml 以配置这些 jar 文件的依赖项,请参见: <dependency> <groupId>com.intersystems</groupId> <artifactId>intersystems-jdbc</artifactId> <version>3.2.0</version> <scope>system</scope> <systemPath>${project.basedir}/src/main/resources/intersystems-jdbc-3.2.0.jar</systemPath> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-iris</artifactId> <version>1.0.0</version> <scope>system</scope> <systemPath>${project.basedir}/src/main/resources/hibernate-iris-1.0.0.jar</systemPath> </dependency>   现在可以在 application.properties 中配置与 IRIS 数据库的连接,请参见: spring.datasource.username=_SYSTEM spring.datasource.url=jdbc:IRIS://iris:1972/USER spring.datasource.password=SYS spring.jpa.properties.hibernate.default_schema=dc_Sample #spring.jpa.hibernate.ddl-auto=update spring.datasource.driver-class-name=com.intersystems.jdbc.IRISDriver spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults = false spring.jpa.database-platform=org.hibernate.dialect.InterSystemsIRISDialect spring.datasource.sql-script-encoding=utf-8 server.tomcat.relaxed-query-chars=|,{,},[,] spring.jpa.show-sql=false spring.jpa.properties.hibernate.format_sql=true 下一步是创建一个映射到 IRIS 表的持久化 Java 类,请参见: package community.intersystems.springboot.app.model; import java.io.Serializable; import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import com.fasterxml.jackson.annotation.JsonFormat; @Entity @Table(name = "Product") public class Product implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Long id; private String name; private String description; private Double height; private Double width; private Double weight; @Column(name="releasedate") @Temporal(TemporalType.DATE) @JsonFormat(pattern = "dd/MM/yyyy") private Date releaseDate; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Double getHeight() { return height; } public void setHeight(Double height) { this.height = height; } public Double getWidth() { return width; } public void setWidth(Double width) { this.width = width; } public Double getWeight() { return weight; } public void setWeight(Double weight) { this.weight = weight; } public Date getReleaseDate() { return releaseDate; } public void setReleaseDate(Date releaseDate) { this.releaseDate = releaseDate; } } 最后一步是创建一个仓库接口,以将您的持久化类作为 REST 服务公开(以 HATEOAS 模式),您无需执​​行 crud 操作,只需编写以下代码: package community.intersystems.springboot.app.repository; import org.springframework.data.jpa.repository.JpaRepository; import community.intersystems.springboot.app.model.Product; public interface ProductRepository extends JpaRepository<Product, Long> { } 现在,将您的应用程序作为 Spring Boot 应用程序运行: ![](/sites/default/files/inline/images/images/image(1389).png) 等待内部服务器启动,然后打开 localhost:8080。 Spring boot 将打开一个 API REST HAL 浏览器。 请参见以下图像记录: ![IRIS 与 Hibernate 配合运行](https://github.com/yurimarx/springboot-iris-crud/raw/master/iris-hibernate.gif) 更多详细信息,请参见我的应用程序示例。 我将所有内容一起打包成一个 Docker 项目,其中包含 2 个服务:IRIS 和 SpringBoot。 HATEOAS 模式非常适合 api url、路径和导航,更多详细信息,请自行百度。 祝使用愉快!
文章
Hao Ma · 十一月 22, 2022

ObjectScript的命名规范

命名规范,英文叫"name convention", 是对写代码取名字的一些”共识“。也就是说, 你可以不遵守,但大家都选择了遵守,照者一个规范来。为什么呢?因为有社区,大家要共享代码, 你不照着规矩来, 别人会鄙视你, 懒的用你的代码。 ObjectScript以前没什么社区,大家各写各的, 使用ObjectScript的大公司也没谁把自己的代码拿出来共享。因此,个人开发者基本就是参考官方InterSystems的命名规范。举个例子, 比如以下的代码: ```java Class Ens.Util.ResponseBodyMethods { property NoFailWhileDisconnected as %Boolean; property FirstName as %String; parameter SETTINGS = "ReplyCodeActions"; method OnKeepalive(pAdapterStatus As %Status) as %String { return "okay" } } ``` 我来简单总结一下: - **包名和类名**:Pascal格式,也就是首字母大写。其中%开头的是官方独占, - **Property名字**:首字母大写 - **method Name**:同上。但出入参用的是驼峰格式,也就是第一个单词小写,后面单词大写开头。 - **Parameter Name**:常量。 官方的代码都是这个规范吗? 大体上是,但也有例外, 比如%xsd包,包名类名全小写, 像这样: %xsd.string。这个从Caché年代就这样,我是不明白为什么这个包这么特殊。 到了IRIS上,更多瞎写了, 比如这个: [%SYSTEM.external.SQL](https://docs.intersystems.com/iris20222/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25SYSTEM.external.SQL)。不光是它,我发现很多和“external“相关的包名,都开始用小写了,写在代码里是这样的: %system.external.help(), %system.dotnet.SQL.addPath()。后面一个为什么SQL大小,dotnet小写,我也是没想明白。 然后,重要的来了。 InterSystems Developer Community贴出了这样的[community代码命名规范](https://community.intersystems.com/post/objectscript-package-manager-naming-convention)。它是怎么被推广的呢? 通过zpm,也就是社区里的代码ObjectScript的包管理工具。曾经一度你要参加社区的代码比赛,就必须使用zpm打包,而zpm对使用这个代码规范有硬要求,所以这个convention得到了一定程度的接受,能看到越来越多的开发者这么写代码了。 原文上面有链接,这里说最重要的: **package名字全小写,class名字用首字母大写**。比如class会定义成: ```java class company.project.subpackage.TheClass{ ... } ``` 这基本上是借鉴Java。作者也说了:*The approach borrowed from the naming convention for java classes.* 作者还做了个解释:全小写的Package名字避免了和class或者interface的名字冲突。这句话我不是很明白。 我觉得网友只所以接受它,纯粹就是java和python用惯了,烦大写的类名。 如果都是c++, c#的程序员,估计这个命名规范还得有得吵。 我这里只说了包名类名, 其他的method, property, 变量的命名什么的,还包括github上repo起名字的规范, 有兴趣去原文看看吧。 我是挺喜欢这个规范的。 唯一有点不满意的是:Java中定义Package name 和class name不是连着写在一起的,比如下面: ```java package org.springframework.asm; public abstract class AnnotationVisitor { ... } ``` 所以你很少能看到包名和类名连写在一起的情况,所以小写的小写, 大写的大写,都不挨着。而ObjectScript的类定义一定要写全了, 上面的定义如果在ObjectScript里就是: ```java public class org.springframework.asm.AnnotationVisitor { ... } ``` 因此这样的写法会有个视觉上的大小写的落差。 我属于没事找事的。 结论: 社区有了命名规范,矮骡子们纷纷表示支持。 按这个convention,Tony老师如果去写一个示例代码贡献给社区的话,可能是这样的 ```java class com.dhc.tony.demo.xml.TestUtils { property noFailWhileDisconnected as %Boolean; property firstname as %String; parameter SETTINGS = "ReplyCodeActions"; method onKeepalive(pAdapterStatus As %Status) as %String { return "okay" } } ``` 我们目前还都是首字母大写的驼峰 之后是要统一改用zpm的命名规范吗。。。
文章
Louis Lu · 十月 8, 2024

FHIR Object Model 简介

挑战 在使用FHIR进行开发的过程中,我们会面对海量的FHIR规范中定义的数据结构,具体来说在FHIR规范中定义了超过150个资源、700多个资源内元素。每个定义里都包括了对自身结构的描述以及数据约束、数据绑定值集等。对于一个开发人员要记住这些内容非常困难。 同时FHIR数据,特别是Json格式的FHIR数据是典型的“有向图”结构,它的资源中嵌套元素定义、集合以及复杂的资源间“关系”,在这些复杂结构的数据间导航并操作,非常困难。 解决方案 在InterSystems IRIS for Health 2024.1之前,我们会将FHIR数据以Json文档的方式载入 %DynamicAbstractObject,例如下面的代码 set dynObject1 = ##class(%DynamicObject).%New() set dynObject1.SomeNumber = 42 set dynObject1.SomeString = "a string" set dynObject1.SomeArray = ##class(%DynamicArray).%New() set dynObject1.SomeArray."0" = "an array element" set dynObject1.SomeArray."1" = 123 set dynObject2 = {"SomeNumber":42,"SomeString":"a string"} set dynObject2.SomeArray = ["an array element",123] write "object 1: "_dynObject1.%ToJSON(),!,"object 2: "_dynObject2.%ToJSON() 可以看到这样的处理有下面不方便的地方: 只能使用自己的方式解析、导航FHIR结构 不能从IDE中获取提示信息 没有数据类型安全 没有上下文敏感的文档 没有调试debug的支持 在InterSystems IRIS for Health 2024.1版本之后,引入了FHIR 对象模型。 FHIR 对象模型概览 在 HS.FHIRModel.R4 包下,每一个FHIR R4资源都对应一个ObjectScript类。比如 HS.FHIRModel.R4.AllergyIntolerance类对应于 AllergyIntolerance 资源。这些类通过为资源和组成元素提供一个共享的、可预测的数据结构和方法框架,简化了开发过程。 这样的结构,在VS Code环境下,会给出很好的提示 下面具体举例如何使用FHIR 对象模型: 1. 获取数据 按索引获取有序序列中的值 Set patientName = patient.name.get(0) 获取嵌套定义中的值 Set patientIdValue = patient.identifier.get(0).get(“value”) Set patientCity = patient.address.get(0).city 在获取数据前判断键key是否存在 If patient.name.get(0).contains("family") { Set familyName = patient.name.get(0).get(“family”) } 2. 更新数据 添加新的键值对 Do patient.name.get(0).put("use","official") 更新存在的值 Do observation.put("status","final") 3. 添加对象的引用 Set claim = ##class(HS.FHIRModel.R4.Claim).%New() Do claim.IncludeType() Do claim.type.IncludeCoding() 4. 导航对象 Set contactItr = patient.contact.get(0).telecom.iterator() While contactItr.hasNext() { Set contact = contactItr.next().value if contact.system = "phone" { Write !, contact.value } } 5 读取JSON格式 FHIR 数据 Set rType = dao.resourceType Set cls = $CLASSMETHOD("I4H.FhirR4."_rType,"fromDao",dao) 6. 将FHIR 数据输出为 JSON 格式 Set newDao = cls.toDao() 转为字符串 Set payload = cls.toString() 7. 创建新的 FHIR 元素 Do claim.IncludeType() Do claim.type.IncludeCoding() Set coding = ..MakeCoding("http://terminology.hl7.org/CodeSystem/claim-type","institutional") Do claim.type.coding.add(coding) 8. “图” 结构查询引擎 Set key = "system" Set value = "email" Set query = "$[*]?(@."_key_"=='"_value_"')" Set email = doctor.telecom.apply(query) 更多资料 FHIR Object Model 在线文档:https://docs.intersystems.com/irisforhealth20242/csp/docbook/DocBook.UI.Page.cls?KEY=HXFHIR_data#HXFHIR_data_classes 下面是一个完整的创建患者资源的例子 ClassMethod MakeCoding(system As %String, code As %String, display As %String = "") As HS.FHIRModel.R4.Coding { Set rec = ##class(HS.FHIRModel.R4.Coding).%New() Set rec.system = system Set rec.code = code If (display'="") Set rec.display = display Return rec } // Create a Patient using FHIR Object Model classes from R4 and then post // this patient on the FHIR server ClassMethod CreatePatient(ident As %String) As %Status { #dim patient As HS.FHIRModel.R4.Patient Set patient = ##class(HS.FHIRModel.R4.Patient).%New() Do patient.IncludeName() Set name = patient.name.MakeEntry() Set firstName = "John" Do name.IncludeGiven() Do name.given.add(firstName) Set name.family = "Doe" Do patient.name.add(name) If patient.name.get(0).contains("family") { Set fName = patient.name.get(0).get("family") } Do patient.name.get(0).put("use","official") Set patient.gender = "male" Set patient.birthDate = "1985-05-15" Do patient.IncludeAddress() Set addr = patient.address.MakeEntry() Do addr.IncludeLine() Set line1 = "123 Main Street" Do addr.line.add(line1) Set addr.city = "Boston" Set addr.state = "MA" Set addr.postalCode = "02111" Do patient.address.add(addr) Set address = ##class(HS.FHIRModel.R4.Address).%New() Do patient.put("address", address) Do patient.IncludeContact() Set contact = patient.contact.MakeEntry() Do contact.IncludeTelecom() Set telecom = contact.telecom.MakeEntry() Set telecom.system = "phone" Set telecom.value = "555-555-5555" Set telecom.use = "mobile" Do contact.telecom.add(telecom) Do patient.contact.add(contact) Set contactItr = patient.contact.get(0).telecom.iterator() While contactItr.hasNext() { Set contact = contactItr.next().value if contact.system = "phone" { Write !, contact.value } } Do patient.IncludeIdentifier() Set id = patient.identifier.MakeEntry() Set coding = ..MakeCoding("http://terminology.hl7.org/CodeSystem/v2-0203", "MR") Set id.type = coding Set id.value = ident Do patient.identifier.add(id) Set patientJSON = patient.toDao() Set service = ##class(HS.FHIRServer.Service).EnsureInstance(1) Do service.interactions.Add(patientJSON) Return $$$OK }
文章
姚 鑫 · 六月 30, 2021

第二十三章 执行XSLT转换

# 第二十三章 执行XSLT转换概述 XSLT(Extensible StyleSheet Language Transformations,可扩展样式表语言转换)是一种基于XML的语言,用于描述如何将给定的XML文档转换为另一个XML或其他“人类可读”的文档。可以使用`%XML.XSLT`和`%XML.XSLT2`包中的类来执行`XSLT 1.0`和`2.0`转换。 注意:使用的任何XML文档的XML声明都应该指明该文档的字符编码,并且文档应该按照声明的方式进行编码。如果未声明字符编码, IRIS将使用本书前面的“输入和输出的字符编码”中描述的默认值。如果这些默认值不正确,请修改XML声明,使其指定实际使用的字符集。 # 在IRIS中执行XSLT转换概述 IRIS提供两个XSLT处理器,每个处理器都有自己的API: - `Xalan`处理器支持`XSLT 1.0`。`XML.XSLT`包为该处理器提供API。 - `Saxon`处理器支持`XSLT 2.0`。`%XML.XSLT2`程序包为该处理器提供API。 `XML.XSLT2` API通过到`XSLT 2.0`网关的连接向`Saxon`发送请求。网关允许多个连接。这意味着,例如,可以将两个独立的 IRIS进程连接到网关,每个进程都有自己的一组编译样式表,同时发送转换请求。 使用Saxon处理器,编译的样式表和`isc:Evaluate`缓存是特定于连接的;必须管理自己的连接才能利用这两个特性。如果打开连接并创建编译样式表或计算填充`isc:Evaluate`缓存的转换,则在该连接上计算的所有其他转换都将访问编译样式表和`isc:Evaluate`缓存条目。如果打开新连接,其他连接(及其编译的样式表和缓存)将被忽略。 这两个处理器的API相似,不同之处在于`%XML.XSLT2`中的方法使用另一个参数来指定要使用的网关连接。 要执行`XSLT`转换,请执行以下操作: 1. 如果使用的是`Saxon`处理器,请按照下一节所述配置`XSLT`网关服务器。或使用默认配置。 如果使用的是`Xalan`处理器,则不需要网关。 系统会在需要时自动启动网关。或者也可以手动启动它。 2. 如果使用的是`Saxon`处理器,则可以选择创建`%Net.Remote.Gateway`的实例,表示到`XSLT`网关的单个连接。 请注意,当使用`Saxon`处理器时,要利用已编译的样式表和`isc:Evaluate`缓存,这一步是必需的。 3. 可以选择创建已编译的样式表并将其加载到内存中。请参阅本章后面的“创建编译样式表”。如果使用的是`Saxon`处理器,请确保在创建编译后的样式表时指定网关参数。 如果打算重复使用同一样式表,则此步骤非常有用。然而,此步骤也会消耗内存。当不再需要编译的样式表时,请务必将其删除。 4. 调用适用API的转换方法之一。如果使用的是`Saxon`处理器,则在调用`Transform`方法时可以选择指定网关参数。 5. 可以选择调用其他转换方法。如果使用的是`Saxon`处理器,则在调用`Transform`方法时可以选择指定网关参数;这使能够使用相同的连接计算另一个转换。此转换将访问与此连接相关联的所有编译样式表和`isc:Evaluate`缓存条目。如果打开新连接,其他连接(及其编译的样式表和缓存)将被忽略。 Studio还提供了一个向导,可以使用该向导测试XSLT转换;本章稍后将对此进行介绍。 # 配置、启动和停止XSLT 2.0网关 当使用`Saxon`处理器(执行`XSLT 2.0`转换)时, IRIS使用`XSLT 2.0`网关(后者使用Java)。要配置此网关,请执行以下操作: 1. 在管理门户中,选择 System Administration > Configuration > Connectivity > XSLT 2.0 Gateway Server. 2. 选择Go。 ![image](/sites/default/files/inline/images/1_49.png) 系统将显示XSLT网关服务器页面。 左侧区域显示配置详细信息,右侧区域显示最近的活动。 ![image](/sites/default/files/inline/images/2_29.png) 3. 在左侧区域中,可以选择指定以下设置: - Port Number -`XSLT 2.0`网关独占使用的TCP端口号。此端口号不得与服务器上的任何其他本地TCP端口冲突。 默认值为 IRIS SuperServer端口号加`3000`。如果此数字大于`65535`,则系统使用`54773`。 - Java Version - 使用的Java版本。 - Log File - 日志文件的路径名。如果忽略此设置,则不执行日志记录。如果指定了文件名但忽略了目录,则将日志文件写入系统管理器的目录。 - Java Home Directory -包含Java bin目录的目录路径。如果服务器上没有默认Java,或者如果想使用不同的Java,请指定此选项。 要查看默认Java,请在服务器上的Shell中执行以下命令: ``` java -version ``` - JVM Arguments - Java虚拟机要使用的任何其他参数。 此区域还显示`JAVA_HOME`环境变量的当前值。 请注意,在网关运行时,不能编辑这些值中的任何一个。 4. 如果已进行更改,请选择保存以保存更改。或选择重置以。 5. (可选)选择测试以测试更改。 在此页面上,还可以执行以下操作: - 启动网关。要执行此操作,请选择右侧区域中的Start。 请注意, IRIS会在需要时自动启动网关。不需要手动启动网关。 - 关闭网关。要执行此操作,请选择右侧区域中的Stop(停止)。 # 重用XSLT网关服务器连接(XSLT 2.0) 如果使用的是`Saxon`处理器,InterSystems IRIS将使用之前配置的`XSLT 2.0`网关。为了与此网关通信,InterSystems IRIS在内部创建一个`XSLT`网关连接(`%Net.Remote.Gateway`的实例)。默认情况下,系统创建一个连接,将其用于转换,然后丢弃该连接。打开新连接会产生开销,因此为多个转换维护一个连接可提供最佳性能。此外,必须维护自己的连接,以便利用已编译的样式表和`isc:Evaluate`缓存。 要重用XSLT网关连接,请执行以下操作: 1. 调用`%XML.XSLT2.Transformer`的`StartGateway()`方法: ``` set status=##class(%XML.XSLT2.Transformer).StartGateway(.gateway) ``` 此方法启动XSLT 2.0网关(如果它尚未运行),并返回`%Net.Remote.Gateway`的实例作为输出。请注意,该方法还返回状态。 在`%Net.Remote.Gateway`实例表示与网关的连接。 `StartGateway()`有一个可选的第二个参数`useSharedMemory`。如果此参数为真(缺省值),则与`localhost`或`127.0.0.1`的连接将使用共享内存(如果可能)。要强制连接仅使用`TCP/IP`,请将此参数设置为False。 2. 检查上一步返回的状态: ```java if $$$ISERR(status) { quit } ``` 3. 创建任何已编译的样式表。执行此操作时,请将网关参数指定为`%Net.Remote.GatewayInstance`的实例在步骤1中创建。 4. 根据需要调用`%XML.XSLT2.Transformer`的`Transform`方法(`TransformFile()`、`TransformFileWithCompiledXSL()`、`TransformStream()`和`TransformStreamWithCompiledXSL()`)。执行此操作时,请将网关参数指定为在步骤1中创建的`%Net.Remote.Gateway`的实例。 5. 如果不再需要给定的编译样式表,请在调用`%XML.XSLT2.CompiledStyleSheet`的`ReleaseFromServer()`方法: ```java Set status=##class(%XML.XSLT2.CompiledStyleSheet).ReleaseFromServer(compiledStyleSheet,,gateway) ``` 重要提示:当不再需要已编译的样式表时,请务必使用此方法。 6. 当不再需要XSLT网关连接时,调用`%XML.XSLT2.Transformer`的`StopGateway()`方法,并将网关连接作为参数传递: ```java set status=##class(%XML.XSLT2.Transformer).StopGateway(gateway) ``` 此方法丢弃连接并重置当前设备。它不会停止`XSLT 2.0`网关。 重要提示:当不再需要连接时,请务必使用此方法。 有关示例,请参见XSLT2中的`Example10()`方法。`Samples`命名空间中的`Examples`。 # 排除XSLT 2.0网关服务器连接故障 当XSLT 2.0网关打开时,InterSystems IRIS和网关服务器之间的连接可能会变得无效。例如,如果出现网络错误或在InterSystems IRIS连接到网关服务器后重新启动网关服务器,则连接可能无法正常关闭。因此,可能会遇到错误。 可以通过连续调用XSLT网关连接对象的`%LostConnectionCleanup()`方法和`%reconnect`方法,尝试将InterSystems IRIS重新连接到网关服务器。 如果希望在断开连接时自动重新连接到网关服务器,请将网关连接对象的`AttemptReconnect`属性设置为true。 # 创建编译的样式表 如果打算重复使用同一样式表,则可能需要编译该样式表以提高速度。请注意,此步骤会消耗内存。当不再需要编译的样式表时,请务必将其删除。 要创建编译的样式表,请执行以下操作: - 如果使用的是`Xalan`处理器(对于`XSLT 1.0`),请使用`%XML.XSLT.CompiledStyleSheet`的以下类方法之一: - `CreateFromFile()` - `CreateFromStream()` - 如果使用的是`Saxon`处理器(用于`XSLT 2.0`),请在使用`%XML.XSLT2.CompiledStyleSheet`的以下类方法之一: - `CreateFromFile()` - `CreateFromStream()` 另请注意,将需要创建一个XSLT网关连接;请参阅“重用XSLT网关服务器连接(`XSLT 2.0`)”。 对于所有这些方法,完整的参数列表按顺序如下: 1. source - 样式表。 对于`CreateFromFile()`,此参数是文件名。对于`CreateFromStream()`,此参数是一个流。 2. compiledStyleSheet - 编译后的样式表,作为输出参数返回。 这是样式表类(`%XML.XSLT.CompiledStyleSheet`或`%XL.XSLT2.CompiledStyleSheet`,视情况而定)的实例。 3. errorHandler - 编译样式表时使用的可选自定义错误处理程序。 对于这两个类中的方法,这是`%XML.XSLT.ErrorHandler`实例。 4. (仅适用于`%XML.XSLT2.CompiledStyleSheet`)网关-`%Net.Remote.Gateway`的实例 ``` //将tXSL设置为等于适当流的OREF Set tSC=##class(%XML.XSLT.CompiledStyleSheet).CreateFromStream(tXSL,.tCompiledStyleSheet) If $$$ISERR(tSC) Quit ```
文章
姚 鑫 · 七月 28, 2021

类关键字SoapBodyUse,SqlCategory,SqlRowIdName,SqlRowIdPrivate

# 第三十三章 类关键字 - SoapBodyUse 指定此类中定义的任何`web method`的编码。此关键字仅适用于`web服务`和`web客户端`类。 # 用法 要指定此类的`web method`的输入和输出所使用的编码,请使用以下语法: ```java Class MyApp.MyClass [ SoapBodyUse = soapbodyuse ] { //class members } ``` 其中`soapbodyuse`是下列之一: - `literal` 文字(默认)—默认情况下,此类中的`web method`使用文字数据。也就是说,`SOAP`消息的``中的`XML`与`WSDL`中给出的模式完全匹配。 - `encoded` 编码—默认情况下,此类中的`web method`使用`SOAP`编码的数据。也就是说,`SOAP`消息的``中的`XML`使用了适合所使用的`SOAP`版本的`SOAP`编码,如以下规范所要求的: - `SOAP 1.1` (https://www.w3.org/TR/2000/NOTE-SOAP-20000508/Opens in a new window) - `SOAP 1.2` (https://www.w3.org/TR/soap12-part2/Opens in a new window) 重要提示:对于手动创建的`web服务`,该关键字的默认值通常是合适的。当使用`SOAP`向导从WSDL生成`web`客户端或服务时,InterSystems IRIS会将此关键字设置为适合该`WSDL`;如果修改该值,web客户端或服务可能不再工作。 # 详解 此关键字指定此类中定义的任何`web method`使用的默认编码。它还控制这个类的`ELEMENTQUALIFIED`和`XMLELEMENT`参数的默认值,这将在本主题的一个小节中讨论。 可以通过使用`SoapBodyUse`方法关键字或`SoapBodyUse`查询关键字,为单个方法重写此关键字。 # 对子类的影响 此关键字不是继承的。 # 默认 默认值为文字。(`SOAP`标准V1.1指定`web method`应该使用`SOAP`编码。但是,大多数`SOAP`客户端(包括`.NET`)都使用文字样式。) # WSDL的关系 `SoapBodyUse`关键字指定了`WSDL`的``部分中``元素的`Use`属性的值。例如,如果`SoapBodyUse`是字面意思,则`WSDL`可能如下所示: ```xml ...
文章
Louis Lu · 四月 9, 2022

使用SQL查询返回结果集数量有偏差的问题解答 --- 检查索引是否有效

此文章也是对问题 在不重建的情况下插入索引Inserting an index without reconstruction 的一种解释 在使用SQL语言对 InterSystems IRIS 中的表进行查询时,有时候会发现返回的结果与实际有出入,特别是使用count() 函数,或者select 查询时,返回的结果少于实际应返回的值。这种情况往往是由于数据表格的索引值出了问题。 索引出问题的主要原因可能是: 表中先有数据,后创建的索引定义,并且定义之后没有重新生成索引。 对保存数据的global直接操作。如果是使用对象或者sql的方式操作数据,相应的索引都会自动更新,但是如果直接对global操作,则不会自动更新索引。 对保存索引的global直接操作。 要解决或者要检查是不是索引引发的问题,可以使用%ValidateIndices()函数,它有两种方式使用 $SYSTEM.OBJ.ValidateIndices(classname,idxList,autoCorrect,lockOption,multiProcess) 或者 ##class(classname).%ValidateIndices(idxList,autoCorrect,lockOption,multiProcess) 两种使用方法最大的差别是:$SYSTEM.OBJ.ValidateIndices()方法会同时检查继承于该表的子表的索引,而##class(classname).%ValidateIndices()不检查子表的索引。 函数参数: idxList, ""代表检查所有索引,或者设定检查的索引项。默认为检查所有索引。 autoCorrect, 是否自动纠正错误。默认为0,不自动纠正。 lockOption, 执行过程中执行的锁的操作,0 - 完全不进行锁定 1 - 在检查每一行时进行共享锁定 2 - 对整个表进行独占锁定。默认=1 multiProcess, 在允许的情况下使用多进程执行。$SYSTEM.OBJ.ValidateIndices() 默认为 0,##class(classname).%ValidateIndices() 默认为 1. 例子: Do $SYSTEM.OBJ.ValidateIndices("Sample.Company",$lb("NameIdx"),1,1) Do ##class(Sample.Company).%ValidateIndices()
公告
Claire Zheng · 八月 26, 2022

开发者社区2022年7月发布

欢迎了解2022年7月社区的最新动态! 最近我们进行了很多有趣的提升,以优化你在InterSystems开发者社区的体验: 📌 社交网络通知功能 📌 改进了订阅设置 📌 全新的“关于我们”页面 📌 更友好的“会员”页面 我们来详细看看这些改进! 通知 从现在开始,你会在你页面右上角(靠近头像照片)看到新的通知,点击小铃铛,你就可以看到所有最新的通知,点击即可获得更详细的信息: 点击“查看全部”即可抵达“通知”页面,在那里你可以看到所有通知信息。 在此页面中,您可以“将所有内容标记为已读(Mark all as read)”或通过链接进入生成通知的页面。并管理订阅设置。 订阅 我们改进了订阅页面,希望你们会觉得这个页面变得更加友好 您可以选择从开发者社区接收哪些通知,无论您是通过电子邮件还是通过网站接收。你可以在你账户的“订阅”部分找到它。 关于我们 我们 已经提到 过,我们创建了一些全新的“关于我们”页面。但是这些按钮太可爱了,值得我再提一次🥰 你可以在顶部菜单的About部分找到它——> 关于我们: 会员 另一个我们已经调整的是“会员”页,希望可以让你感到更便利。我们添加了一个新的“最新活动”列。您可以通过单击任何列的名称对其进行排序。 暂时就这些吧!希望您赞成我们所有的改进! 下次见!
文章
Chang Liu · 九月 22, 2022

在国产系统上安装Healthconnect2021

1,准备 本次安装环境:Kylin-Server-10-SP2-Release-Build09-20210524-x86_64.iso 安装系统适配的对应版本:HealthConnect-2021.1.2.338.0-lnxubuntux64.tar.gz;ISCAgent-2021.1.2.338.0-lnxubuntux64.tar.gz 系统语言选择:English(必要) 2,安装HealthConnect+webgateway 2.1 新建所需用户组 groupadd iscagent 2.2 解压文件 2.3 安装 本次安装,使用root用户及root用户组安装,超级端口改为51773 3,安装ISCAgent 3.1安装说明 ISCAgent是healthconnect的镜像服务,若是单机使用,则不需要安装。 3.2 解压文件 3.3 安装 3.3 加入服务 3.3.1 新建文件 在/etc/systemd/system 下创建文件,ISCAgent.service,内容如下: [Unit] Description=InterSystems Agent After=syslog.target network-online.target [Service] Environment=LD_LIBRARY_PATH=/usr/local/etc/irissys Environment=COMLIB=/usr/local/etc/irissys ExecStart=/usr/local/etc/irissys/ISCAgent Type=forking PIDFile=/var/run/ISCAgent.pid KillMode=process [Install] WantedBy=multi-user.target 其中,/usr/local/etc/irissys为ISCAgent服务安装默认目录。 3.3.2 修改文件属性 chmod 755 ISCAgent.service3.3.3 重新启动systemctl systemctl daemon-reload 3.3.4 启动服务 systemctl start ISCAgent.service 3.3.5 查看服务状态 systemctl status ISCAgent.service 如下: 4, 防火墙配置 firewall-cmd --zone=public --add-port=51773/tcp --permanent firewall-cmd --zone=public --add-port=52773/tcp --permanent firewall-cmd --zone=public --add-port=2188/tcp --permanent (--permanent永久生效,没有此参数重启后失效) 重载生效: firewall-cmd --reload 5,镜像配置 5.1 主机配置 创建镜像 5.2 备机配置 加入故障转移 hc为HealthConnect的安装实例名称 6 安装过程中出现的问题 6.1 描述 添加虚拟ip失败: 6.2 问题解决 发现virtualIP.sh中部分代码在中文系统中执行时有问题,则将系统换成英文系统则可以解决这个问题。 The End 可以,很实用 找了好久,国产系统适配先行者 很棒的分享! 友友写的真好 优秀 非常不错,总结到位 真的很不错 加精了!