クリアフィルター
記事
Tomoko Furuzono · 2023年7月2日
これは、InterSystems FAQサイトの記事です。
以下の様な方法で、グローバルのインポート/エクスポート処理をプログラムに組み込むことができます。
1. グローバルエクスポート方法1.1 XML形式でのエクスポートグローバルをXML形式のファイルにエクスポートする場合、$system.OBJ.Export() を使用します。 1.1.1. 指定したグローバルをエクスポートする場合エクスポート対象グローバルを グローバル名.gbl で指定します(先頭の ^ は不要)。例:
Do $system.OBJ.Export("a.gbl,b.gbl","c:\temp\globals.xml",,.errors)
結果については、errors に格納されます。
$system.OBJ.Export() の詳細は%SYSTEM.OBJクラスのクラスリファレンスをご確認ください。クラスリファレンス:%SYSTEM.OBJ1.1.2. ネームスペース内の全グローバルをエクスポートする場合%SYS.GlobalQueryクラスでグローバル一覧を取得し、それを $system.OBJ.Export() に渡してエクスポートを実行します。
例:
Set rs=##class(%ResultSet).%New("%SYS.GlobalQuery:NameSpaceList")
Do rs.Execute()
Kill globals
While rs.Next() {
Set globals(rs.Get("Name")_".gbl")=""
}
Do $system.OBJ.Export(.globals,"c:\temp\allglobal.xml",,.errors)
(ここでは簡略化のためエラーチェックは省略しています)
1.2. ブロック形式でのエクスポート(%GOFユーティリティと同等)ブロック形式でグローバルをエクスポートするには、クラス %Library.Global のメソッドExport() を使用します。エクスポート対象グローバルは、1.1.1と同じ形式で指定します。各引数の詳細はクラスリファレンスをご確認ください。クラスリファレンス:%Library.Global
例:
USER>Set status=##class(%Library.Global).Export(,"a.gbl,b.gbl","c:\test.gof",7)
GO/GOF形式でエクスポートの開始 07/23/2008 17:01:03
グローバルをエクスポート中: ^a
グローバルをエクスポート中: ^b
エクスポートが正常に完了しました。
USER>
2. グローバルインポート方法2.1 XML形式ファイルのインポート2.1.1 ファイルに含まれる全グローバルをインポートするXMLファイルに含まれる全グローバルをインポートするには $system.OBJ.Load() を使用します。
例:
Do $system.OBJ.Load("c:\temp\globals.xml",,.errors)
2.1.2 ファイルに含まれるグローバルのうち一部のみをインポートするXMLファイルに含まれる一部のグローバルのみ選択してインポートする場合、一旦$system.OBJ.Load() で 第5引数の listonly を 1 に設定してXMLファイルを読み込み、第4引数(出力引数)で得られたリストからインポート対象を選択して第6引数で指定します。例:
Set file="c:\temp\globals.xml"
// まずXMLに含まれるアイテム一覧を取得
Do $system.OBJ.Load(file,,.errors,.list,1 /* listonly */)
Set item=$Order(list(""))
Kill loaditem
While item'="" {
If item["Sample" { // Sample を含むもののみインポート
Set loaditem(item)=""
Set item=$Order(list(item))
}
}
// 作成されたリストでインポート処理実行
Do $system.OBJ.Load(file,,.errors,,,.loaditem)
2.2 ブロック形式でのインポート(%GIFユーティリティと同等)ブロック形式でエクスポートされたグローバルをインポートするには、$system.OBJ.Load() またはクラス %Library.Global の Import() メソッドを使用します。 ファイル中の特定のグローバルのみインポートする場合は、2.1.2と同じ方法が使用できます。例1:
USER>Do $system.OBJ.Load("c:\test.gof")
ロード開始 07/23/2008 17:01:49
ファイル c:\test.gof を gbl としてロード中
インポートしたグローバル: ^a
インポートしたグローバル: ^b
ロードが正常に完了しました。
USER>
例2:
USER>Set status=##class(%Library.Global).Import(,"*","c:\test.gof",7)
記事
Toshihiko Minamoto · 2021年9月1日
記事で使用されているすべてのソースコード: https://github.com/antonum/ha-iris-k8s
[前の記事](https://jp.community.intersystems.com/node/490971)では、従来型のミラーリングではなく分散ストレージに基づいて、高可用性のあるk8sでIRISをセットアップする方法について説明しました。 その記事では例としてAzure AKSクラスタを使用しました。 この記事では引き続き、k8sで可用性の高い構成を詳しく見ていきますが、 今回は、Amazon EKS(AWSが管理するKubernetesサービス)に基づき、Kubernetes Snapshotに基づいてデータベースのバックアップと復元を行うためのオプションが含まれます。
## インストール
早速作業に取り掛かりましょう。 まず、AWSアカウントが必要です。[AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html)、[kubectl](https://kubernetes.io/docs/tasks/tools/)、および[eksctl](https://docs.aws.amazon.com/eks/latest/userguide/eksctl.html)ツールがインストールされている必要があります。 新しいクラスタを作成するために、次のコマンドを実行します。
eksctl create cluster \
--name my-cluster \
--node-type m5.2xlarge \
--nodes 3 \
--node-volume-size 500 \
--region us-east-1
このコマンドは約15分掛けてEKSクラスタをデプロイし、それをkubectlツールのデフォルトのフォルダに設定します。 デプロイを確認するには、次のコードを実行します。
kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-192-168-19-7.ca-central-1.compute.internal Ready <none> 18d v1.18.9-eks-d1db3c
ip-192-168-37-96.ca-central-1.compute.internal Ready <none> 18d v1.18.9-eks-d1db3c
ip-192-168-76-18.ca-central-1.compute.internal Ready <none> 18d v1.18.9-eks-d1db3c
次に、Longhorn分散ストレージエンジンをインストールします。
kubectl create namespace longhorn-system
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.1.0/deploy/iscsi/longhorn-iscsi-installation.yaml --namespace longhorn-system
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml --namespace longhorn-system
そして最後に、IRIS自体をインストールします。
kubectl apply -f https://github.com/antonum/ha-iris-k8s/raw/main/tldr.yaml
この時点で、Longhorn分散ストレージとIRISデプロイがインストールされたEKSクラスタが完全に機能できる状態になります。 前の記事に戻って、クラスタとIRISデプロイにあらゆるダメージを与えて、システムがどのように修復するのかを確認するとよいでしょう。 「[障害をシミュレートする](https://community.intersystems.com/post/highly-available-iris-deployment-kubernetes-without-mirroring)」セクションをご覧ください。
## 特典1 ARM上のIRIS
IRIS EKSとLonghornはARMアーキテクチャをサポートしているため、ARMアーキテクチャに基づき、AWS Gravition 2インスタンスを使用して同じ構成をデプロイできます。
EKSノードのインスタンスタイプを 'm6g' ファミリーに変更し、IRISイメージをARMベースに変更するだけです。
eksctl create cluster \
--name my-cluster-arm \
--node-type **m6g.2xlarge** \
--nodes 3 \
--node-volume-size 500 \
--region us-east-1
tldr.yaml
containers:
#- image: store/intersystems/iris-community:2020.4.0.524.0
- image: store/intersystems/irishealth-community-arm64:2020.4.0.524.0
name: iris
または、単に以下を使用します。
kubectl apply -f https://github.com/antonum/ha-iris-k8s/raw/main/tldr-iris4h-arm.yaml
以上です! ARMプラットフォームで実行するIRIS Kubernetesクラスタが出来上がりました。
## 特典2 - バックアップと復元
本番環境グレードのアーキテクチャでよく見過ごされがちな部分に、データベースのバックアップを作成して必要なときに素早く復元するか複製する機能があります。
Kubernetesでは、一般的にPersistent Volume Snapshots(永続ボリュームスナップショット)を使用してこれを行います。
まず、必要なすべてのk8sコンポーネントをインストールする必要があります。
#Install CSI Snapshotter and CRDs
kubectl apply -n kube-system -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml
kubectl apply -n kube-system -f https://github.com/kubernetes-csi/external-snapshotter/raw/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotclasses.yaml
kubectl apply -n kube-system -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml
kubectl apply -n kube-system -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml
kubectl apply -n kube-system -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/rbac-snapshot-controller.yaml
次に、LonghornのS3バケット資格情報を構成します([詳細な手順](https://longhorn.io/docs/1.1.0/snapshots-and-backups/backup-and-restore/set-backup-target/)を参照)。
#Longhorn backup target s3 bucket and credentials longhorn would use to access that bucket
#See https://longhorn.io/docs/1.1.0/snapshots-and-backups/backup-and-restore/set-backup-target/ for manual setup instructions
longhorn_s3_bucket=longhorn-backup-123xx #bucket name should be globally unique, unless you want to reuse existing backups and credentials
longhorn_s3_region=us-east-1
longhorn_aws_key=AKIAVHCUNTEXAMPLE
longhorn_aws_secret=g2q2+5DVXk5p3AHIB5m/Tk6U6dXrEXAMPLE
以下のコマンドは、前の手順から環境変数を拾い、それを使ってLonghorn Backupを構成します。
#configure Longhorn backup target and credentials
cat <<EOF | kubectl apply -f -
apiVersion: longhorn.io/v1beta1
kind: Setting
metadata:
name: backup-target
namespace: longhorn-system
value: "s3://$longhorn_s3_bucket@$longhorn_s3_region/" # backup target here
---
apiVersion: v1
kind: Secret
metadata:
name: "aws-secret"
namespace: "longhorn-system"
labels:
data:
# echo -n '<secret>' | base64
AWS_ACCESS_KEY_ID: $(echo -n $longhorn_aws_key | base64)
AWS_SECRET_ACCESS_KEY: $(echo -n $longhorn_aws_secret | base64)
---
apiVersion: longhorn.io/v1beta1
kind: Setting
metadata:
name: backup-target-credential-secret
namespace: longhorn-system
value: "aws-secret" # backup secret name here
EOF
たくさんの作業に見えるかもしれませんが、基本的にLonghornに対し、指定された資格情報で特定のS3バケットを使用し、バックアップのコンテンツを保存するように指示しています。
以上です! Longhorn UIに移動すると、バックアップを作成して復元などを行えるようになっています。

Longhorn UIに接続する方法を簡単におさらいしましょう。
kubectl get pods -n longhorn-system
# note the full pod name for 'longhorn-ui-...' pod
kubectl port-forward longhorn-ui-df95bdf85-469sz 9000:8000 -n longhorn-system
これによって、Longhorn UIへのトラフィックはhttp://localhost:9000に転送されるようになります。
## プログラムによるバックアップ/復元
Longhorn UIを介して行うバックアップと復元は、最初のステップとしては十分かもしれませんが、もう一歩先に進み、k8s Snapshot APIを使用して、プログラムでバックアップと復元を実行してみましょう。
まず、スナップショットそのものが必要です。 iris-volume-snapshot.yaml
apiVersion: snapshot.storage.k8s.io/v1beta1
kind: VolumeSnapshot
metadata:
name: iris-longhorn-snapshot
spec:
volumeSnapshotClassName: longhorn
source:
persistentVolumeClaimName: iris-pvc
このボリュームスナップショットは、IRISデプロイに使用するソースボリュームである 'iris-pvc' を参照しています。 そのため、これを適用するだけですぐにバックアッププロセスが開始します。
IRIS書き込みデーモンの凍結と解凍をスナップショットの前後に実行することをお勧めします。
#Freeze Write Daemon
echo "Freezing IRIS Write Daemon"
kubectl exec -it -n $namespace $pod_name -- iris session iris -U%SYS "##Class(Backup.General).ExternalFreeze()"
status=$?
if [[ $status -eq 5 ]]; then
echo "IRIS WD IS FROZEN, Performing backup"
kubectl apply -f backup/iris-volume-snapshot.yaml -n $namespace
elif [[ $status -eq 3 ]]; then
echo "IRIS WD FREEZE FAILED"
fi
#Thaw Write Daemon
kubectl exec -it -n $namespace $pod_name -- iris session iris -U%SYS "##Class(Backup.General).ExternalThaw()"
復元プロセスは非常に簡単です。 基本的には、新しいPVCを作成して、スナップショットをソースとして指定しています。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: iris-pvc-restored
spec:
storageClassName: longhorn
dataSource:
name: iris-longhorn-snapshot
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
次に、このPVCに基づいて新しいデプロイを作成するだけです。 これを順に行うこちらの[GitHubリポジトリにあるテストスクリプト](https://github.com/antonum/ha-iris-k8s/blob/main/backup/test.sh)をご覧ください。
* まったく新しいIRISデプロイを作成します。
* IRISにデータを追加します。
* 書き込みデーモンを凍結し、スナップショットを取得して、書き込みデーモンを解凍します。
* そのスナップショットをベースに、IRISデプロイのクローンを作成します。
* すべてのデータが含まれていることを確認します。
この時点で、同一のIRISデプロイが2つ存在することになります。1つはもう片方のデプロイのclone-via-backupです。
どうぞお楽しみください!
記事
Toshihiko Minamoto · 2022年11月8日
パート 1 では、Django で新しいプロジェクトを開始する方法を紹介し、新しいモデルの定義方法と既存のモデルの追加方法を説明しました。 今回は、初期状態で利用可能な管理者パネルとどのように役立つかについて説明します。 _重要な注意事項: この記事のアクションを繰り返しても、動作しません。 記事の途中で、django-iris プロジェクトにいくつか修正を行い、InterSystems が作成した DB-API ドライバーの課題もいくつか修正しました。このドライバーは現在の開発中であり、将来的に、より安定したドライバーが提供されると思います。 この記事では、すべてを実行した場合にどのようになるかを説明しているにすぎません。_ コードに戻り、すべてのウェブリクエストのメインのエントリポイントである urls.py にある内容を確認しましょう。
"""main URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/4.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]
すでに URL **_admin_** がそこに定義されているのが分かります。 開発サーバーをコマンドで起動しましょう。
python manage.py runserver
URL _http://localhost:8000/admin_ に移動すると、Django 管理のログインフォームが表示されます。 .png) ここには何らかのユーザーが必要であるため、次のコマンドでそのユーザーを作成します。
$ python manage.py createsuperuser
Username (leave blank to use 'daimor'): admin
Email address: admin@example.com
Password:
Password (again):
The password is too similar to the username.
This password is too short. It must contain at least 8 characters.
This password is too common.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.
このログインとパスワードをここで使用します。 現時点ではあまり何もありませんが、グループとユーザーにはアクセスできるようになっています。
その他のデータ
前回、zpm で post-and-tags パッケージをインストールしました。ここでも同じことを行えます。
zpm "install posts-and-tags"
次に、このパッケージでインストールされたすべてのテーブル(_community.post、community.comment、community.tag_)のモデルを取得します。
$ python manage.py inspectdb community.post community.comment community.tag > main/models.py
これにより多少長いファイルが生成されるので、スポイラーとして記載します。
main/models.py
# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
# * Rearrange models' order
# * Make sure each model has one field with primary_key=True
# * Make sure each ForeignKey and OneToOneField has `on_delete` set to the desired behavior
# * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.
from django.db import models
class CommunityPost(models.Model):
acceptedanswerts = models.DateTimeField(db_column='AcceptedAnswerTS', blank=True, null=True) # Field name made lowercase.
author = models.CharField(db_column='Author', max_length=50, blank=True, null=True) # Field name made lowercase.
avgvote = models.IntegerField(db_column='AvgVote', blank=True, null=True) # Field name made lowercase.
commentsamount = models.IntegerField(db_column='CommentsAmount', blank=True, null=True) # Field name made lowercase.
created = models.DateTimeField(db_column='Created', blank=True, null=True) # Field name made lowercase.
deleted = models.BooleanField(db_column='Deleted', blank=True, null=True) # Field name made lowercase.
favscount = models.IntegerField(db_column='FavsCount', blank=True, null=True) # Field name made lowercase.
hascorrectanswer = models.BooleanField(db_column='HasCorrectAnswer', blank=True, null=True) # Field name made lowercase.
hash = models.CharField(db_column='Hash', max_length=50, blank=True, null=True) # Field name made lowercase.
lang = models.CharField(db_column='Lang', max_length=50, blank=True, null=True) # Field name made lowercase.
name = models.CharField(db_column='Name', max_length=250, blank=True, null=True) # Field name made lowercase.
nid = models.IntegerField(db_column='Nid', primary_key=True) # Field name made lowercase.
posttype = models.CharField(db_column='PostType', max_length=50, blank=True, null=True) # Field name made lowercase.
published = models.BooleanField(db_column='Published', blank=True, null=True) # Field name made lowercase.
publisheddate = models.DateTimeField(db_column='PublishedDate', blank=True, null=True) # Field name made lowercase.
subscount = models.IntegerField(db_column='SubsCount', blank=True, null=True) # Field name made lowercase.
tags = models.CharField(db_column='Tags', max_length=350, blank=True, null=True) # Field name made lowercase.
text = models.TextField(db_column='Text', blank=True, null=True) # Field name made lowercase.
translated = models.BooleanField(db_column='Translated', blank=True, null=True) # Field name made lowercase.
type = models.CharField(db_column='Type', max_length=50, blank=True, null=True) # Field name made lowercase.
views = models.IntegerField(db_column='Views', blank=True, null=True) # Field name made lowercase.
votesamount = models.IntegerField(db_column='VotesAmount', blank=True, null=True) # Field name made lowercase.
class Meta:
managed = False
db_table = 'community.post'
class CommunityComment(models.Model):
id1 = models.CharField(db_column='ID1', primary_key=True, max_length=62) # Field name made lowercase.
acceptedanswerts = models.DateTimeField(db_column='AcceptedAnswerTS', blank=True, null=True) # Field name made lowercase.
author = models.CharField(db_column='Author', max_length=50, blank=True, null=True) # Field name made lowercase.
avgvote = models.IntegerField(db_column='AvgVote', blank=True, null=True) # Field name made lowercase.
correct = models.BooleanField(db_column='Correct', blank=True, null=True) # Field name made lowercase.
created = models.DateTimeField(db_column='Created', blank=True, null=True) # Field name made lowercase.
hash = models.CharField(db_column='Hash', max_length=50, blank=True, null=True) # Field name made lowercase.
id = models.IntegerField(db_column='Id') # Field name made lowercase.
post = models.CharField(db_column='Post', max_length=50, blank=True, null=True) # Field name made lowercase.
text = models.TextField(db_column='Text', blank=True, null=True) # Field name made lowercase.
texthash = models.CharField(db_column='TextHash', max_length=50, blank=True, null=True) # Field name made lowercase.
type = models.CharField(db_column='Type', max_length=50) # Field name made lowercase.
votesamount = models.IntegerField(db_column='VotesAmount', blank=True, null=True) # Field name made lowercase.
class Meta:
managed = False
db_table = 'community.comment'
unique_together = (('type', 'id'),)
class CommunityTag(models.Model):
description = models.TextField(db_column='Description', blank=True, null=True) # Field name made lowercase.
name = models.TextField(db_column='Name', primary_key=True) # Field name made lowercase.
class Meta:
managed = False
db_table = 'community.tag'
Django の管理ダッシュボードは、開発者が拡張できるようになっています。 また、テーブルをさらに追加することも可能です。 それには、main/admin.py という新しいファイルを追加する必要があります。コード内には、行を説明するコメントをいくつか追加しています。
from django.contrib import admin
# immport our community models for our tables in IRIS
from .models import (CommunityPost, CommunityComment, CommunityTag)
# register class which overrides default behaviour for model CommunityPost
@admin.register(CommunityPost)
class CommunityPostAdmin(admin.ModelAdmin):
# list of properties to show in table view
list_display = ('posttype', 'name', 'publisheddate')
# list of properties to show filter for on the right side of the tablee
list_filter = ('posttype', 'lang', 'published')
# default ordering, means from the latest date of PublishedDate
ordering = ['-publisheddate', ]
@admin.register(CommunityComment)
class CommunityCommentAdmin(admin.ModelAdmin):
# only this two fields show, (post is numeric by id in table post)
list_display = ('post', 'created')
# order by date of creation
ordering = ['-created', ]
@admin.register(CommunityTag)
class CommunityTagAdmin(admin.ModelAdmin):
# not so much to show
list_display = ('name', )
ポータルの拡張
Django 管理ページに戻り、そこに追加された新しい項目を確認しましょう。
右側には、フィルタパネルがあります。最も重要なのは、特定のフィールドの可能値がすべて表示されている点です。
残念ながら、InterSystems SQL は Django で期待されているまたは Django の別の方法である LIMIT, OFFSET をサポートしていません。 また、Django は TOP をサポートしていません。 そのため、ここにページ送りは表示されますが、機能しません。 また、現時点では機能できるようにもできません。残念ながら個人的には、今後も機能することはないと思っています。
オブジェクトを詳しく調べることも可能で、Django には正しいフィールド型っを使ったフォームが表示されます。(注意: このデータセットには、テキストフィールドのデータは含まれません)
コメントオブジェクト
Community Edition ではライセンスに関する課題が予想されます。
Community Edition を使用している場合は、この問題に直面する可能性があります。これは、すべての接続が占有されている場合の様子であり、非常に素早く発生する問題です。 サーバーのレスポンスが非常に遅くなっている場合は、おそらくその問題に該当しています。IRIS はこのケースでは素早く応答せず、不明な理由により、長い時間がかかっています。
IRIS が空き枠あると示す場合であってもです。
6 個以上の接続を許可しないため、動作させるには、1つ以上のプロセスを終了するか、Django サーバーを再起動しなければなりません。
開発時には、Django サーバーを非スレッドモードに制限することもできるため、1つのプロセスで動作させられます。 また、IRIS への接続をさらに取得してはいけません。
python manage.py runserver --nothreading
記事
Mihoko Iijima · 2020年11月10日
これはInterSystems FAQ サイトの記事です。
インデックスが複数定義されているクラス/テーブルへ csv 形式等のシーケンシャルファイルから大量データをデータベースに登録する際、推奨される登録方法として、データ登録時インデックスを生成させず、登録完了後に一括でインデックスを生成する 方法があります。
この方法は、新規に大量のレコードを一括登録する際に最も有効な手段となります。
<メモ>大量のデータを追加登録する際には、既存のデータ量と新規データ量のバランスにより、この手法が有効でないケースもあります。その場合は、インデックスの再構築を範囲指定で行うこともできます。
説明に使用するクラス定義例は以下の通りです。
Class ISJ.QL2 Extends %Persistent
{
Property Name As %String;
Property Title As %String;
Property Sex As %String;
Property Company As %String;
Property Phone As %String;
Property City As %String;
Property State As %String;
Property Zip As %String;
Index NameIndex On Name;
Index CompanyIndex On Company;
Index PhoneIndex On Phone;
}
データロードを行うクラスメソッド例は以下の通りです。
ClassMethod ImportFromFile(pFile As %String)
{
#dim Err As %Exception.AbstractException
//埋め込みSQLを使用してインポート
Try {
if $get(pFile)="" {
write "インポートファイルを指定してください",!
quit
}
if ##class(%File).Exists(pFile)=0 {
write "指定したファイルは存在しません。ファイル名、パスを確認してください",!
quit
}
set filestream=##class(%Stream.FileCharacter).%New()
do filestream.LinkToFile(pFile)
set tDelim=";"
while filestream.AtEnd=0 {
//改行があるところまでRead
set tLine=filestream.ReadLine()
set pCity = $Piece(tLine,tDelim,1) //City
set pCompany = $Piece(tLine,tDelim,2) //Company
set pName = $Piece(tLine,tDelim,3) //Name
set pPhone = $Piece(tLine,tDelim,4) //Phone
set pSex = $Piece(tLine,tDelim,5) //Sex
set pState = $Piece(tLine,tDelim,6) //State
set pTitle = $Piece(tLine,tDelim,7) //Title
set pZip = $Piece(tLine,tDelim,8) //Zip
&sql(INSERT %NOINDEX INTO ql2 (Name, Title, Sex, Company, Phone, City, State, Zip) values (:pName, :pTitle, :pSex, :pCompany, :pCity, :pCity, :pState, :pZip))
// SQL文でエラーがある場合の処理
if SQLCODE<0 {
throw ##class(%Exception.SQL).CreateFromSQLCODE(SQLCODE,%msg)
}
}
// 最後にテーブルのインデックス再構築を実行
set st=..%BuildIndices()
$$$THROWONERROR(Err,st) // エラーが発生した場合Catchへ移動
}
Catch Err {
write "エラーが発生しました",!
write Err.DisplayString(),!
}
}
クラスメソッドでは、以下のデータ形式で作成されたファイルを入力しながらデータ登録後にインデックスを構築しています(ランダム生成させたデータを使用しています)。
;京都市上京区;新光損保 株式会社;橋本,京子;0363-7728-2499;女;京都府;国際製品スペシャリスト;6020808;札幌市西区;NTS工業 株式会社;本田,亮;0325-6753-6990;男;北海道;アシスタント管理者;0630003;呉市;セコミ薬品 株式会社;川原,明雄;0670-9635-5468;男;広島県;副衛生士;7370145;春日部市;SESコミュニケーションズ 株式会社;大島,江美;0407-3421-5865;女;埼玉県;研究ディレクタ;3440065;上高井郡小布施町;電金証券 有限会社;松本,亮;053-3208-4665;女;長野県;副会計士;3810202;茅野市;ビーエスシ薬品 株式会社;渡部,弘明;0996-5061-8567;女;長野県;国際マーケティングマネージャ;3910212;中川郡豊頃町;電金技研 有限会社;根本,由貴;0647-5142-4961;男;北海道;国際ウェブマスタ;0895461;尼崎市;SES石油 有限会社;川口,博美;0744-3148-1523;男;兵庫県;副会計士;6610978;北松浦郡吉井町;三友製造 株式会社;阿部,陽子;0554-2270-3308;男;長崎県;副システムエンジニア;8596304
サンプルコードの以下の文で、全レコードに対してインデックスを構築しています。
Do ..%BuildIndices()
サンプルコードで使用している埋め込み SQL では、実行後に %ROWID 変数を使用して INSERT によって設定された ID 値を取得できます。
例えば、INSERT したレコードのインデックスのみを構築したい場合は、%BuildIndices() メソッドの第 2 引数(pAutPurge)に 0 を指定し、第 5 引数(pStartID)および第 6 引数(pEndID)に ID を指定することで実行できます。
インデックス構築方法について詳細は、ドキュメントもご参照ください。
記事
Mihoko Iijima · 2021年2月12日
これは InterSystems FAQ サイトの記事です。
XMLファイルの内容を格納する永続クラス定義を作成し、%XML.Adaptor を追加で継承します。
例は以下の通りです(右端の %XML.Adaptorクラスを追加で継承します)。
Class ISJ.Class1 Extends (%Persistent, %Populate, %XML.Adaptor)
次に、%XML.Reader クラスを使用して格納先のインスタンスへ、タグとクラスの関連付け(Correlate())を行い、reader.Next() でXMLを取り込みます。
set sc=reader.OpenFile(filename)
do reader.Correlate(tag,class)
while reader.Next(.x,.sc) { do x.%Save() }
サンプルコードは以下の通りです。
Class ISJ.Class1 Extends (%Persistent, %Populate, %XML.Adaptor)
{
Property a As %String;
Property b As %String;
/// 引数:入力するXMLファイルのフルパス
ClassMethod Import(filename As %String)
{
if $get(filename)="" {
write "入力ファイルを引数に指定してください",!
quit
}
// クラス名指定(..%ClassName()でクラスメソッドの存在するクラス名を返します)
set class=..%ClassName(1)
// XMLタグ指定
set tag="test"
set reader = ##class(%XML.Reader).%New()
set sc=reader.OpenFile(filename)
If $$$ISERR(sc) {
write $system.Status.GetErrorText(sc),!
Quit
}
// クラスとの関連付け
do reader.Correlate(tag,class)
while reader.Next(.x,.sc) {
set sc=x.%Save()
If $$$ISERR(sc) {
write $system.Status.GetErrorText(sc),!
Quit
}
}
quit
}
}
入力するXMLファイル例は以下の通りです。
<?xml version="1.0" encoding="UTF-8"?>
<top><test>
<a>あいうえお</a>
<b>かきくけこ</b>
</test>
<test>
<a>O8634</a>
<b>H7321</b>
</test>
<test>
<a>J5426</a>
<b>V2218</b>
</test>
<test>
<a>J7155</a>
<b>M6804</b>
</test>
<test>
<a>R7066</a>
<b>W939</b>
</test>
<test>
<a>B9731</a>
<b>I406</b>
</test>
<test>
<a>I1095</a>
<b>Z5125</b>
</test>
<test>
<a>Q1573</a>
<b>Z9241</b>
</test>
<test>
<a>T7560</a>
<b>V5693</b>
</test>
<test>
<a>J1033</a>
<b>J9616</b>
</test>
</top>
実行例は以下の通りです。
(1) ターミナルを開き(またはIRISへログインし)クラス定義を作成したネームスペースへ移動します。
ネームスペースの変更方法は以下の通りです。
set $namespace="USER"
※ Linux/Unix上の IRIS へログインする方法は、 iris session <構成名> を実行します。例は以下の通りです。
iris session IRIS
(2) 入力例のXMLをファイルに保存(UTF-8で保存)し、ファイルのフルパスを変数に設定します。
例)/ISC/data.xml に配置した例
set file="/ISC/data.xml"
(3) 例のクラス定義(ISJ.Class1)の Import() メソッドを実行します。引数に (2) で設定した変数 file を指定しています。
do ##class(ISJ.Class1).Import(file)
(4) データを確認します。
管理ポータルで確認する場合は以下の手順で画面を開きます。
管理ポータル > システムエクスプローラ > SQL > クラス定義のあるネームスペースに移動> スキーマのプルダウンから ISJ を選択 > テーブル の > をクリックし展開 > Class1を選択して、画面右側の「テーブルを開く」をクリック
または、画面右側の「クエリ実行」タブをクリックし、テーブル名をドラッグしクエリ実行タブのテキストエリアでドロップし、実行ボタン押下
ターミナルで確認する場合は、SQL 実行環境に変更します。
do $system.SQL.Shell()
プロンプトが >> に変更されたらSELECE文を記述してEnterを押下します
USER>do $system.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------
The command prefix is currently set to: <<nothing>>.
Enter <command>, 'q' to quit, '?' for help.
[SQL]USER>>select * from ISJ.Class1
1. select * from ISJ.Class1
ID a b
1 あいうえお かきくけこ
2 O8634 H7321
3 J5426 V2218
4 J7155 M6804
5 R7066 W939
6 B9731 I406
7 I1095 Z5125
8 Q1573 Z9241
9 T7560 V5693
10 J1033 J9616
10 Rows(s) Affected
statement prepare time(s)/globals/cmds/disk: 0.0822s/37396/167775/3ms
execute time(s)/globals/cmds/disk: 0.0007s/11/1370/0ms
cached query class: %sqlcq.USER.cls29
---------------------------------------------------------------------------
[SQL]USER>>quit
USER>
お知らせ
Mihoko Iijima · 2021年6月8日
これは InterSystems FAQ サイトの記事です。
現時点のサポートバージョンについては、以下のドキュメントをご参照ください。
ミニマム・サポートバージョンについて
このリストに記述されていないバージョンに関しては、該当システムの保守契約があるという前提で、
メール、お電話等の通常のお問い合わせは受け付けます。
出来うる限りの対応を努力しますが、そのバージョンでの対応が出来ず、最新バージョンへのバージョンアップによる問題解決をご提案させていただく場合がございます。
インターシステムズは、以下の理由により、出来うる限り、最新バージョンへバージョンアップしていただくことをお勧めしております。
開発部門では、定期的に内部のコードレビュを実施しており、その結果、発見された機能不全等を適宜、最新バージョンに取り込んでいます。
過去にお客様先で発見された障害への対応は、順次新バージョンに取り込んでいきます。障害の性質によっては、非常に大規模な改造となり、旧バージョンへの取り込み(バックポート)が不可能なものもございます。
古いバージョンに対し、新バージョンに追加されたバグフィックスを取り込むこと(バックポート)は、非常に困難なケースが多く、新たな副作用を生む可能性が高くなります。また、バージョンが古くなればなるほどバックポートは困難になっていく可能性が高まります。
開発をより迅速に行うための便利な機能等、新機能の恩恵を受けることができます。
新しいバージョンは以前のバージョンよりも性能が同等または高くなければならない、というインターシステムズ製品開発部門のポリシーにより、バージョンが新しくなるにつれ、アプリケーションの性能向上が期待できます。
日々品質向上に向け努力を続けております。結果として最新バージョンの品質は以前のバージョンに比べて高いことが期待できます。
新しいハードウェア対応、それに伴うオペレーティングシステムや様々なソフトウェアの新バージョンへの対応は、最新のバージョンが優先されます。
セキュリティの脆弱性等への対応は最新バージョンが優先されます。
セキュリティの脆弱性等への対応は、大規模な広範囲にわたる変更の可能性が高く、古いバージョンへの対応が大変困難となります。
サポート対象バージョンに関しては、所定のリグレッションテストを実施後出荷しますが、応急パッチ等の非定型な対応では、リグレッションテストを実施しませんので、ソフトウェアの信頼性に大きな差があります。
お使いの IT 環境は、技術の進歩、セキュリティ意識の高まり等様々な要因で変化していく可能性が高い中、古いバージョンのソフトウェアを使い続けることには想定以上のリスクがあります。
関連記事・FAQトピック
セキュリティ脆弱性に関する対応について
バグフィックス対応について
古いバージョンの購入はできますか?
お知らせ
Toshihiko Minamoto · 2023年6月20日
先週の InterSystems Global Summit にて、今年の初めにリリースしました2023.1のエクスペリメンタル機能として、新たな 外部テーブル を発表しました。現在、 外部テーブルの Early Access Program にご参加いただきご評価いただくことで、この機能がお客さまのニーズに合っているか、次に向けてどの機能を優先するべきか、お知らせいただきたいと考えています。
外部テーブルって何なの?この素晴らしい概要ビデオを見る時間やポップコーンがない場合に備えて、外部テーブルは、ファイルやリモートデータベースなど、物理的に別の場所に保存されているデータをIRIS SQLとしてアクセスするのに役立つ機能です。外部テーブルは、通常のIRISテーブルとしてSQLに表示され、他の通常テーブルや外部テーブルとのJOINなど、あらゆるSQLステートメントで使用することができます。クエリを実行する際、外部テーブルから何を検索する必要があるのかを理解し、そのサーバーがリレーショナルデータベースの場合は、ネットワーク経由で取得するデータを最小限に抑えるようなクエリを出力しています。
なぜデータをロードするだけじゃないの?それはいい質問ですね!データをロードした後、IRISでクエリを実行すると、ソース・システムですでに変更されている可能性があります。コストのかかる同期メカニズムを構築するのではなく、外部テーブルとして投影することで、クエリ時に常に最新のデータを取得することができます。その他の使用例としては、ファイルベースの大きなデータで、IRISデータベースのストレージを消費してしまうような場合、または一般的に一度しか読まないようなデータなどがあります。ところで、外部テーブルの構文と機能は、2021.2 で紹介した LOAD DATA コマンドで知っているものと完全に一致しています。
既にリンクテーブルがあるのでは?その通りです。その機能はかなり以前からあり、私たちの顧客ではよく採用されています。しかし、JDBCとODBCの実装が異なる時期に行われたため、機能や制限が微妙に異なり、メンテナンスや機能拡張が複雑になっていました。当初の実装以降、IRISのSQL内部でいくつかの改良(Table Valued Functionsを含む)が行われ、外部とIRISをより明確に分離した、より実用的なアプローチが可能になりました。このため、新しい機能として Foreign Tables を実装し、公式の ANSI SQL 標準と一致するようにしました。
現在、外部テーブルをLinked Tablesの後継とするつもりです。現在、Foreign Tablesを使用している方は、その機能のどれがあなたのユースケースにとって重要かをぜひ教えてください。そうすれば、外部テーブルの次の開発フェーズに優先順位をつけ、迅速に多くのユーザーに参加してもらい、ラボ以外からフィードバックが得られるでしょう。
どこで申し込むの?intersystems.com/early-access-programs にお申込みいただければ、チュートリアルとデモのリポジトリから開始できます。定期的にご連絡を差し上げ、ご意見を伺い、新しい機能を追加していく予定です。
皆様からのご連絡をお待ちしております!
記事
Mihoko Iijima · 2021年8月23日
これは InterSystems FAQ サイトの記事です。
※データベースファイルとは、IRIS.DAT、および、CACHE.DATのことを指します。
Caché/Ensemble 2018.1.4、IRIS 2019.1.2/2020.1.0 以降のバージョンより、データベースファイル のサイズの縮小に、データベースの「圧縮」と「未使用領域の削除」機能をあわせて使用できます。
※注意※Caché/Ensemble 2018.1.4、IRIS 2019.1.2/2020.1.0 より前のバージョンでは、データベースの「圧縮」機能は使用できません。使用された場合、データベース破損が引き起こされる可能性があります(「未使用領域の削除」機能は利用できます)。
詳細情報は以下、弊社ウェブサイト内のページをご確認ください。
製品ニュースとアラート>警告: データベース圧縮またはデフラグ後のデータベース整合性の問題
データベースサイズ縮小手順は以下の通りです。
管理ポータル: [システムオペレーション] > [データベース] を開き、操作したいデータベース名をクリックします。
(または、画面左上の[空き容量]ボタンをクリックし、データベースの空き容量を表示します。)
① 「圧縮」ボタンにより、データベースファイルの使用していないブロックを後ろに集めます
<圧縮のイメージ> ●が使用中ブロック、〇が未使用ブロックだとします。
●●○●●○○●○○ ←のようにブロックが並んでいるデータベースファイル(*.DAT)を「圧縮」すると
●●●●●○○○○○ ←このようになります (左が前方)
②「未使用領域削除」ボタンにより、後方に集めた未使用ブロックを削除してデータベースファイルのサイズを縮小します。
<未使用領域削除のイメージ> ●が使用中ブロック、〇が未使用ブロックだとします。
●●●●●○○○○○ ←のようにブロックが並んでいるデータベースファイルに「未使用領域削除」を実行すると
●●●●● ←このようになります
圧縮について詳細は、ドキュメント(データベースの圧縮(IRIS))をご参照ください(2018.1以前のドキュメント:データベースの圧縮)。
管理ポータルの操作と同様の処理を、^DATABASE ユーティリティを使用して実行することもできます。
%SYS ネームスペースに移動してから実行します。
USER>zn "%SYS"
%SYS>do ^DATABASE
1) Create a database
2) Edit a database
3) List databases
4) Delete a database
5) Mount a database
6) Dismount a database
7) Compact globals in a database
8) Show free space for a database
9) Show details for a database
10) Recreate a database
11) Manage database encryption
12) Return unused space for a database // 「未使用領域削除」
13) Compact free space in a database // 「圧縮」
14) Defragment a database // 「DB の断片化解消」
15) Show background database tasks
Option?
※「圧縮」 = 13) Compact freespace in a database 「未使用領域削除」= 12) Return unused space for a database
^DATABASE ユーティリティの 14) Defragment globals in a database(「DBの断片化解消」)について補足します。
「DBの断片化解消」では、指定したデータベースファイル内のグローバル毎にブロックが再編成され、データベースファイル内で連続領域に再配置します。
「圧縮」をより強力にしたものですが、この再配置を行う為には、データベースファイル末尾に十分な空きブロックが必要です(一般的に見て、デフラグを定期的に実行する必要はありません)。
<「DBの断片化解消」のイメージ>
^a ♦ ^b ♠ ^c♥ 空き○
断片化解消前♦♦○♠♠○♠♠♦♦♠♥♥♥○♦♦♥♥♦○○○○○○
断片化解消後♦♦♦♦♦♦♦♠♠♠♠♠♥♥♥♥♥○○○○○○○○○
※注意※「断片化解消」機能についても、2015.1.3、2015.2.2 より前のバージョンでは使用できません。使用された場合、「圧縮」機能と同様に、データベース破損が引き起こされる可能性があります。
詳細は、ドキュメント(データベースのグローバルのデフラグ(IRIS))をご参照ください(2018.1以前のドキュメント:データベースのグローバルのデフラグ)。
記事
Shintaro Kaminaka · 2020年7月30日
この記事では、RESTFormsプロジェクト(モダンなWebアプリケーション用の汎用REST APIバックエンド)を紹介します。
プロジェクトの背後にあるアイデアは単純です。私はいくつかのREST APIを書いた後、REST APIが一般的に次の2つの部分で構成されていることに気付きました。
* 永続クラスの操作
* カスタムビジネスロジック
また、独自のカスタムビジネスロジックを書く必要はありますが、RESTFormsには永続クラスの操作に関連するすべての機能を提供しています。
**使用例**
* Cachéにすでにデータモデルがあり、REST API形式で情報の一部(またはすべて)を公開したい
* 新しいCachéアプリケーションを開発しており、REST APIを提供したい
**クライアントサイド**
このプロジェクトはWebアプリケーションのバックエンドとして開発されているため、JSだけで事足ります。 形式の変換は必要ありません。
**補足:CRUD**
オブジェクトまたはコレクションに対し、次の4つの操作を実行できます。
* Create(作成)
* Read(読み込み)
* Update(更新)
* Delete(削除)
**機能**
RESTFormsを使用して以下を実行できます。
* 公開されたクラスに対するCRUD - クラスのメタデータを取得し、クラスのプロパティを作成 / 更新 / 削除できます。
* オブジェクトに対するCRUD - オブジェクトを取得 / 作成 / 更新 / 削除できます。
* オブジェクトコレクションに対するRead(SQL経由) - SQLインジェクションから保護します。
* 自己検出 – 最初に使用可能なクラスのリストを取得し、その後でクラスのメタデータを取得し、そのメタデータを基にしてオブジェクトに対するCRUDを実行できます。
**パス**
以下の表には、主なパスとRESTFormsを使用して実行できる操作を掲載しています。
URL
説明
info
利用可能なすべてのクラスを一覧表示します
info/all
すべてのクラスのメタデータを取得します
info/:class
クラスのメタデータ
field/:class
プロパティをクラスに追加します
field/:class
クラスのプロパティを変更します
field/:class/:property
クラスのプロパティを削除します
object/:class/:id
オブジェクトを取得します
object/:class/:id/:property
オブジェクトの1つのプロパティを取得します
object/:class
オブジェクトを作成します
object/:class/:id
動的オブジェクトからオブジェクトを更新します
object/:class
オブジェクトからオブジェクトを更新します
object/:class/:id
オブジェクトを削除します
objects/:class/:query
(SQL)クエリでクラスのオブジェクトを取得します
objects/:class/custom/:query
(SQL)カスタムクエリでクラスのオブジェクトを取得します
**RESTFormsを使い始めるには?**
1. GitHubからプロジェクトをインポートします(お勧めの方法は独自リポジトリにサブモジュールとして追加する方法ですが、単にリリースをダウンロードしても良いです)。
2. RESTFormsを介して公開したい各クラスについて以下を実施します。
* アダプタクラスから継承する
* 権限を指定します(一部のクラスを読み取り専用として公開する場合などに実施)。
* オブジェクトの表示値として使用されるプロパティを指定します。
* 表示したいプロパティの表示名を指定します。
**セットアップ**
1. [リリースページ](https://github.com/intersystems-ru/RESTForms/releases/tag/v1.0)で最新リリースである20161.xml( Caché 2016.1用)または201162.xml(Caché 2016.2以降用)をダウンロードして任意のネームスペースにインポートします。
2. 新しいWebアプリケーション /forms をDispatchクラス Form.REST.Main を使用して作成します。
3. http://localhost:57772/forms/test?Debug をブラウザで開き、インストールを検証します({"Status": "OK"} が出力され、場合によってはパスワードの入力が求められます)。
4. テストデータが必要な場合は、次を呼び出します:
```do ##class(Form.Util.Init).populateTestForms()```
**例**
最初に、利用可能なクラスを知る必要があります。 この情報を取得するには、次を呼び出します。
http://localhost:57772/forms/form/info
次のような応答が返されます。
[
{ "name":"Company", "class":"Form.Test.Company" },
{ "name":"Person", "class":"Form.Test.Person" },
{ "name":"Simple form", "class":"Form.Test.Simple" }
]
現在3つのサンプルクラス(RESTFormで提供)があります。Person(Form.Test.Personクラス)のメタデータを見てみましょう。 この情報を取得するには、次を呼び出します。
http://localhost:57772/forms/form/info/Form.Test.Person
次のように、クラスのメタデータが応答として返されます。
{
"name":"Person",
"class":"Form.Test.Person",
"displayProperty":"name",
"objpermissions":"CRUD",
"fields":[
{ "name":"name", "type":"%Library.String", "collection":"", "displayName":"Name", "required":0, "category":"datatype" },
{ "name":"dob", "type":"%Library.Date", "collection":"", "displayName":"Date of Birth", "required":0, "category":"datatype" },
{ "name":"ts", "type":"%Library.TimeStamp", "collection":"", "displayName":"Timestamp", "required":0, "category":"datatype" },
{ "name":"num", "type":"%Library.Numeric", "collection":"", "displayName":"Number", "required":0, "category":"datatype" },
{ "name":"аge", "type":"%Library.Integer", "collection":"", "displayName":"Age", "required":0, "category":"datatype" },
{ "name":"relative", "type":"Form.Test.Person", "collection":"", "displayName":"Relative", "required":0, "category":"form" },
{ "name":"Home", "type":"Form.Test.Address", "collection":"", "displayName":"House", "required":0, "category":"serial" },
{ "name":"company", "type":"Form.Test.Company", "collection":"", "displayName":"Company", "required":0, "category":"form" }
]
}
これらの情報は次のような意味を持ちます。
クラスのメタデータ:
* name - クラスの表示名。
* class - 基本となる永続クラス。
* displayProperty - オブジェクトを表示するときに使用するオブジェクトのプロパティ。
* objpermissions - ユーザーがオブジェクトを使用して実行できる操作。 この例では、ユーザーは新しいオブジェクトを作成し、既存のオブジェクトを変更し、既存のオブジェクトを削除し、次を取得できます。
プロパティのメタデータ:
* name - プロパティ名 - クラスの定義と同じです。
type - プロパティのクラス。
* コレクション - リスト/配列のコレクションです。
* displayName - 表示プロパティ名。
* required - このプロパティが必須であるかどうか。
* category - プロパティのタイプクラスのカテゴリ。 RESTForms対応のすべてのクラスが「form」として表示されることを除き、通常のCachéクラスのカテゴリに従います。
クラス定義では次のようになります。
/// テストフォーム: Person
Class Form.Test.Person Extends (%Persistent, Form.Adaptor, %Populate)
{
/// フォーム名。グローバルキーではないため、何でもかまいません。
/// クラスをフォームとして持たないようにするには(ここのように)空の文字列に設定します。
Parameter FORMNAME = "Person";
/// デフォルトの権限
/// このフォームのオブジェクトは、作成、読み取り、更新、削除できます。
/// すべてのユーザーの権限を変更するには、このパラメーターを再定義します。
/// このクラスのcheckPermissionメソッドを再定義します(Form.Securityを参照してください)。
/// ユーザーやロールなどに基づいて独自のセキュリティを追加します。
Parameter OBJPERMISSIONS As %String = "CRUD";
/// オブジェクトの基本情報に使用されるプロパティ
/// デフォルトでは、getObjectDisplayNameメソッドはここから値を取得します。
Parameter DISPLAYPROPERTY As %String = "name";
/// このパラメーターの値をSQLでORDER BY句の値として使用します。
Parameter FORMORDERBY As %String = "dob";
/// Personの名前。
Property name As %String(COLLATION = "TRUNCATE(250)", DISPLAYNAME = "Name", MAXLEN = 2000);
/// Personの生年月日。
Property dob As %Date(DISPLAYNAME = "Date of Birth", POPSPEC = "Date()");
Property ts As %TimeStamp(DISPLAYNAME = "Timestamp") [ InitialExpression = {$ZDATETIME($ZTIMESTAMP, 3, 1, 3)} ];
Property num As %Numeric(DISPLAYNAME = "Number") [ InitialExpression = "2.15" ];
/// Personの年齢。<br>
/// これは、 <property>DOB</property> から派生した値を持つ計算されたフィールドです。
Property аge As %Integer(DISPLAYNAME = "Age") [ Calculated, SqlComputeCode = { set {*}=##class(Form.Test.Person).currentAge({dob})}, SqlComputed, SqlComputeOnChange = dob ];
/// このクラスメソッドは、誕生日 <var>date</var> が与えられた場合に現在の年齢を計算します。
ClassMethod currentAge(date As %Date = "") As %Integer [ CodeMode = expression ]
{
$Select(date="":"",1:($ZD($H,8)-$ZD(date,8)\10000))
}
/// Personの配偶者。
/// これは別の永続オブジェクトへの参照です。
Property relative As Form.Test.Person(DISPLAYNAME = "Relative");
/// Personの自宅住所。 埋め込みオブジェクトを使用します。
Property Home As Form.Test.Address(DISPLAYNAME = "House");
/// このPersonが働いている会社。
Relationship company As Form.Test.Company(DISPLAYNAME = "Company") [ Cardinality = one, Inverse = employees ];
}
**クラスでRESTFormsを有効にする**
そして、このクラスでRESTFormsを有効にするため、通常の永続クラスから始めて次のことを行いました。
1. Form.Adaptor から拡張しました。
2. 値を含むパラメーター FORMNAME(クラス名)を追加しました。
3. OBJPERMISSIONS パラメーター(すべての権限のCRUD)を追加しました。
4. DISPLAYPROPERTY パラメーター(オブジェクト名の表示に使用されるプロパティ名)を追加しました。
5. FORMORDERBY パラメーター(RESTFormsを使用するクエリでソートするデフォルトのプロパティ)を追加しました。
6. メタデータで確認したいプロパティごとに DISPLAYNAME プロパティのパラメーターを追加しました。
以上です。 コンパイル後、RESTFormsを含むクラスを使用できるようになります。
いくつかのテストデータを生成しましたので(インストールのステップ4を参照)、IDが1のPersonを取得してみましょう。 オブジェクトを取得するには、次を呼び出します。
http://localhost:57772/forms/form/object/Form.Test.Person/1
その応答は以下のとおりです(生成されるデータは異なる場合があります)。
{
"_class":"Form.Test.Person",
"_id":1,
"name":"Klingman,Rhonda H.",
"dob":"1996-10-18",
"ts":"2016-09-20T10:51:31.375Z",
"num":2.15,
"аge":20,
"relative":null,
"Home":{
"_class":"Form.Test.Address",
"House":430,
"Street":"5337 Second Place",
"City":"Jackson"
},
"company":{
"_class":"Form.Test.Company",
"_id":60,
"name":"XenaSys.com",
"employees":[
null
]
}
}
オブジェクト(具体的にはnumプロパティ)を変更するには、次を呼び出します。
PUT http://localhost:57772/forms/form/object/Form.Test.Person
このボディを使用します。
{
"_class":"Form.Test.Person",
"_id":1,
"num":3.15
}
速度を上げるには、_class、_id、および変更対象のプロパティのみをリクエストのボディに含める必要があります。
では、新しいオブジェクトを作成しましょう。 以下を呼び出します。
POST http://localhost:57772/forms/form/object/Form.Test.Person
このボディを使用します。
{
"_class":"Form.Test.Person",
"name":"Test person",
"dob":"2000-01-18",
"ts":"2016-09-20T10:51:31.375Z",
"num":2.15,
"company":{ "_class":"Form.Test.Company", "_id":1 }
}
オブジェクトの作成が成功した場合、RESTFormsは以下のようにIDを返します。
{"Id": "101"}
成功しなかった場合、エラーがJSON形式で返されます。 すべての永続オブジェクトのプロパティは、 _class および _id プロパティによってのみ参照する必要があります。
そして最後に、新しいオブジェクトを削除しましょう。 以下を呼び出します。
DELETE http://localhost:57772/forms/form/object/Form.Test.Person/101
これがForm.Test.Personクラスに対する完全なCRUDです。
**デモ**
[現在デモ環境はお試しいただくことができません。]
こちらでRESTFormsをオンラインで試すことができます(ユーザー名:Demo、パスワード:Demo)。
また、RESTFormsUIアプリケーション(RESTFormsデータエディタ)もあります。こちらをご確認ください(ユーザー名:Demo、パスワード:Demo)。 クラスリストのスクリーンショットを以下に掲載しています。

**まとめ**
RESTFormsは永続クラスに関する、REST APIから要求されるほとんどの機能を提供します。
**次の内容**
この記事では、RESTFormsの機能について説明しました。 次回の記事では、いくつかの高度な機能(クライアントからSQLインジェクションのリスクを冒さずにデータの一部を安全に取得できるクエリなど)についてお話ししたいと思います。 [この記事のパート2でクエリに関する情報をお読みください](https://community.intersystems.com/post/restforms-rest-api-your-classes-part-2-queries)。
RESTFormsUI(RESTFormsデータエディタ)もあります。
**リンク**
* [RESTForms GitHubリポジトリ](https://github.com/intersystems-ru/RESTForms/)
* [RESTForms UI GitHubリポジトリ](https://github.com/intersystems-ru/RESTFormsUI/)
* [パート2:クエリ](https://community.intersystems.com/post/restforms-rest-api-your-classes-part-2-queries)
記事
Toshihiko Minamoto · 2021年10月19日
## はじめに
ObjectScriptで複雑な問題を解決している場合、おそらく%Status値を使用したコードがたくさんあることでしょう。 オブジェクトの観点(%Save、%OpenIdなど)から永続クラスを操作したことがある場合は、ほぼ確実にその状況に遭遇したことがあるでしょう。 %StatusはInterSystemsのプラットフォームでローカライズ可能なエラーメッセージのラッパーを提供します。 OKステータス($$$OK)は1に等しいだけであるのに対し、不良ステータス($$$ERROR(errorcode,arguments...))は0、スペース、エラーに関する構造化情報を含む$ListBuildリストとして表されます。 [$System.Status(クラスリファレンスを参照)](https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&LIBRARY=%25SYS&CLASSNAME=%25SYSTEM.Status)は、%Status値を操作するための便利なAPIをいくつか提供しています。クラスリファレンスを役立てられるので、ここでは繰り返しません。 このトピックに関する有用な記事/質問もほかにいくつかあります(最後のリンクをご覧ください)。 この記事では、コーディングのベストプラクティスではなく、いくつかのデバッグのコツや手法に焦点を当てています(ベストプラクティスについては、最後のリンクをご覧ください)。
## お題のコード例
注意: このようなコードは絶対に書かないようにしてください! 常にステータスをチェックし、それを返すか、例外としてスロー($$$ThrowStatus(someErrorStatus) など)すれば、デバッグがはるかに簡単になります。
Class DC.Demo.MaskedErrorStatus Extends %Persistent
{
Property Answer As %TinyInt;
ClassMethod Run() As %Status
{
Set instance = ..%New()
Set instance.Answer = 9000
Do instance.%Save()
Set instance = ..%OpenId(1,,.sc)
Set instance.Answer = 42
Do instance.%Save()
Quit $$$OK
}
}
ターミナルから実行すると、例外がスローされます。何かが明らかにうまく行っていません。
USER>d ##class(DC.Demo.MaskedErrorStatus).Run()
Set instance.Answer = 42
^
<INVALID OREF>zRun+5^DC.Demo.MaskedErrorStatus.1
## %Statusのデバッグのコツ#1: $System.OBJ.DisplayError()
常に $System.OBJ.DisplayError() を実行して、作成された最後のエラーステータスを出力できます。 これが機能するのは、($System.Status.Error 経由で)エラーステータスが作成されるたびに、変数 %objlasterror にそのステータスが設定されるためです。 また、同様に%objlasterrorをzwriteすることもできます。 上記の場合は、次のようになります。
USER 2d1>d $system.OBJ.DisplayError()
ERROR #5809: Object to Load not found, class 'DC.Demo.MaskedErrorStatus', ID '1'
## %Statusのデバッグのコツ#2: スタックトレース
%Statusごとに、エラーが作成された場所のスタックトレースがあります。 ステータスをzwriteすることで、そのトレースを閲覧することができます。
USER 2d1>zw %objlasterror
%objlasterror="0"_$lb($lb(5809,"DC.Demo.MaskedErrorStatus","1",,,,,,,$lb(,"USER",$lb("e^%LoadData+18^DC.Demo.MaskedErrorStatus.1^1","e^%Open+16^%Library.Persistent.1^1","e^%OpenId+1^%Library.Persistent.1^1","e^zRun+4^DC.Demo.MaskedErrorStatus.1^1","d^^^0"))))/* ERROR #5809: Object to Load not found, class 'DC.Demo.MaskedErrorStatus', ID '1' */
ステータスごとに、より分かりやすいテキスト($System.OBJ.DisplayError()または$System.Status.GetErrorText(someStatus) )でスタックトレースを確認したいですか? これは、^%oddENV("callererrorinfo",$namespace)=1 または 2 に設定することで実現できます。 以下に、その効果を示します。
USER>set ^%oddENV("callererrorinfo",$namespace)=1
USER>d $system.OBJ.DisplayError()
ERROR #5809: Object to Load not found, class 'DC.Demo.MaskedErrorStatus', ID '1' [%LoadData+18^DC.Demo.MaskedErrorStatus.1:USER]
USER>set ^%oddENV("callererrorinfo",$namespace)=2
USER>d $system.OBJ.DisplayError()
ERROR #5809: Object to Load not found, class 'DC.Demo.MaskedErrorStatus', ID '1' [e^%LoadData+18^DC.Demo.MaskedErrorStatus.1^1 e^%Open+16^%Library.Persistent.1^1 e^%OpenId+1^%Library.Persistent.1^1 e^zRun+4^DC.Demo.MaskedErrorStatus.1^1 d^^^0:USER]
USER>k ^%oddENV("callererrorinfo",$namespace)
USER>d $system.OBJ.DisplayError()
ERROR #5809: Object to Load not found, class 'DC.Demo.MaskedErrorStatus', ID '1'
これは実際には、開発環境のみで使用するのが適切であることに注意してください。ユーザーにはコードの内部を見られたくないはずです。 (ユーザーに直接%Status値を表示することを避けて、よりユーザーフレンドリーなアプリケーション固有のエラーメッセージを表示するのが最善ですが、これについては、別のトピックとしましょう。)
## %Statusのデバッグのコツ#3: 極上の zbreak
この辺からトリッキーになってきます。このコードスニペットの場合では、根本的な原因は、前述のコードスニペットの%Save() から%Statusが確認されていないことにあります。 何がうまく行かなかったのかを見つけるのが非常に難しい、はるかに複雑な例を想像するのは簡単です。特に、プラットフォームコードのずっと後の方で発生しているエラーであれば、尚更です。 これに対処するためにインタラクティブデバッガに飛びつく以外で私が好んで使用してる方法は、ターミナルで非常に極上の[zbreak](https://cedocs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=TCOS_ZBreak)コマンドを使用することです。
USER>zbreak *%objlasterror:"N":"$d(%objlasterror)#2":"set ^mtemptl($i(^mtemptl))=%objlasterror"
これはどういう意味でしょうか?
zbreak <%objlasterrorが変更される時点>:<デバッガでは何もしない>:<%objlasterrorが定義されており値がある限り(定義済みから未定義にならない場合など)>:<整数がサブスクリプトのジャーナルされていないグローバル(mtempから始めることで、トランザクション中に%Statusが作成され、ログを見るより先にロールバックされてしまうの防ぐため。また、コミットされたコードやデータベースで誰かがこれを見つけた時に私に連絡できるように、グローバルの一部に私のイニシャルを使っています)で、次のサブスクリプトのグローバルにエラーステータスをセットする処理を実行>
zbreak に関する補足: 現在定義されているブレークポイント/ウォッチポイントは、引数を指定せずに 'zbreak' を実行することで確認できます。また、ブレークポイントの使用が終わったら、break "off" などを実行して、これらのブレークポイントをオフにできますし、する必要があります。以下に例を示します。
USER>zbreak
BREAK:
No breakpoints
%objlasterror F:E S:0 C:"$d(%objlasterror)#2" E:"set ^mtemptl($i(^mtemptl))=%objlasterror"
USER>break "off"
USER>zbreak
BREAK:
No breakpoints
No watchpoints
では、問題のあるメソッドがウォッチポイントを設定して実行されるとどうなるでしょうか?
USER>zbreak *%objlasterror:"N":"$d(%objlasterror)#2":"set ^mtemptl($i(^mtemptl))=%objlasterror"
USER>d ##class(DC.Demo.MaskedErrorStatus).Run()
Set instance.Answer = 42
^
<INVALID OREF>zRun+5^DC.Demo.MaskedErrorStatus.1
USER 2d1>zw ^mtemptl
^mtemptl=6
^mtemptl(1)="0 "_$lb($lb(7203,9000,127,,,,,,,$lb(,"USER",$lb("e^zAnswerIsValid+1^DC.Demo.MaskedErrorStatus.1^1","e^%ValidateObject+3^DC.Demo.MaskedErrorStatus.1^4","e^%SerializeObject+3^%Library.Persistent.1^1","e^%Save+4^%Library.Persistent.1^2","d^zRun+3^DC.Demo.MaskedErrorStatus.1^1","d^^^0"))))/* ERROR #7203: Datatype value '9000' greater than MAXVAL allowed of 127 */
^mtemptl(2)="0 "_$lb($lb(7203,9000,127,,,,,,,$lb(,"USER",$lb("e^zAnswerIsValid+1^DC.Demo.MaskedErrorStatus.1^1","e^%ValidateObject+3^DC.Demo.MaskedErrorStatus.1^4","e^%SerializeObject+3^%Library.Persistent.1^1","e^%Save+4^%Library.Persistent.1^2","d^zRun+3^DC.Demo.MaskedErrorStatus.1^1","d^^^0")),"0 "_$lb($lb(5802,"DC.Demo.MaskedErrorStatus:Answer",9000,,,,,,,$lb(,"USER",$lb("e^EmbedErr+1^%occSystem^1"))))))/* ERROR #7203: Datatype value '9000' greater than MAXVAL allowed of 127- > ERROR #5802: Datatype validation failed on property 'DC.Demo.MaskedErrorStatus:Answer', with value equal to "9000" */
^mtemptl(3)="0 "_$lb($lb(7203,9000,127,,,,,,,$lb("zAnswerIsValid+1^DC.Demo.MaskedErrorStatus.1","USER",$lb("e^zAnswerIsValid+1^DC.Demo.MaskedErrorStatus.1^1","e^%ValidateObject+3^DC.Demo.MaskedErrorStatus.1^4","e^%SerializeObject+3^%Library.Persistent.1^1","e^%Save+4^%Library.Persistent.1^2","d^zRun+3^DC.Demo.MaskedErrorStatus.1^1","d^^^0"))))/* ERROR #7203: Datatype value '9000' greater than MAXVAL allowed of 127 */
^mtemptl(4)="0 "_$lb($lb(5802,"DC.Demo.MaskedErrorStatus:Answer",9000,,,,,,,$lb("EmbedErr+1^%occSystem","USER",$lb("e^EmbedErr+1^%occSystem^1"))))/* ERROR #5802: Datatype validation failed on property 'DC.Demo.MaskedErrorStatus:Answer', with value equal to "9000" */
^mtemptl(5)="0 "_$lb($lb(7203,9000,127,,,,,,,$lb("zAnswerIsValid+1^DC.Demo.MaskedErrorStatus.1","USER",$lb("e^zAnswerIsValid+1^DC.Demo.MaskedErrorStatus.1^1","e^%ValidateObject+3^DC.Demo.MaskedErrorStatus.1^4","e^%SerializeObject+3^%Library.Persistent.1^1","e^%Save+4^%Library.Persistent.1^2","d^zRun+3^DC.Demo.MaskedErrorStatus.1^1","d^^^0")),"0 "_$lb($lb(5802,"DC.Demo.MaskedErrorStatus:Answer",9000,,,,,,,$lb("EmbedErr+1^%occSystem","USER",$lb("e^EmbedErr+1^%occSystem^1"))))))/* ERROR #7203: Datatype value '9000' greater than MAXVAL allowed of 127- > ERROR #5802: Datatype validation failed on property 'DC.Demo.MaskedErrorStatus:Answer', with value equal to "9000" */
^mtemptl(6)="0 "_$lb($lb(5809,"DC.Demo.MaskedErrorStatus","1",,,,,,,$lb(,"USER",$lb("e^%LoadData+18^DC.Demo.MaskedErrorStatus.1^1","e^%Open+16^%Library.Persistent.1^1","e^%OpenId+1^%Library.Persistent.1^1","e^zRun+4^DC.Demo.MaskedErrorStatus.1^1","d^^^0"))))/* ERROR #5809: Object to Load not found, class 'DC.Demo.MaskedErrorStatus', ID '1' */
多少ノイズがありますが、重要な問題がすぐに現れます。
/\* ERROR #7203: Datatype value '9000' greater than MAXVAL allowed of 127 \*/
%TinyInt を使用しないことを想定すべきでした! (また、より重要なことは、呼び出すメソッドが返す%Status値を必ず確認する必要があります。)
## 関連資料
[私が気に入っている、エラー処理とレポートのコーディングパターン](https://community.intersystems.com/post/try-catch-block-i-usually-use-intersystems-objectscript#comment-7751)
[Caché ObjectScriptメソッドの%Statusとその他の戻り値](https://community.intersystems.com/post/status-vs-other-return-values-cach%C3%A9-objectscript-methods)
[%objlasterrorについて](https://community.intersystems.com/post/about-objlasterror)
[$$$envCallerErrorInfoGetの設定方法](https://community.intersystems.com/post/how-set-envcallererrorinfoget-windows-get-location-information-within-exception#comment-95586)
[ObjectScriptによるエラー処理スニペット](https://community.intersystems.com/post/objectscript-error-handling-snippets)
[ZBREAKコマンド](https://cedocs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=TCOS_ZBreak)
記事
Toshihiko Minamoto · 2021年10月20日
インターネットを使うようになってから (1990年代後半)、PythonとIRIS グローバルを使ってブログを書いていますが、常にCMS (コンテンツ管理システム) でブログ、ソーシャルメディア、さらには企業ページに情報を簡単に投稿できるようにしていました。
数年後、自分がマークダウンファイルに収めて使ってきたすべてのコードをgithubに入れました。
ネイティブAPIでデータをIntersystems IRISに入れて永続化するのはとても簡単だったので、このアプリケーションを作成して少しSQLを忘れ、キー・バリュー・データベースモデルを受け入れることにしました!

## ブログとは?
これはWEB LOGの略名で、基本的にはユーザーが書いてページに投稿、公開できるプラットフォームです。
## マークダウンフォーマットとは?
マークダウンとは、フォーマットしたテキストを作成するマークアップ言語であり、HTMLより簡単で、多くのCMSプラットフォームで人気があります。
例:
```
# Header 1
## Header 2
### Header 3
```
結果:
# Header 1
## Header 2
### Header 3
***********
## IRISで作成するメリットとは?
IRIS Globalsを使用して各投稿のデータを永続化しながら、各データコンサルタントはキー・バリュー・データベースとして動作するIRISのスピードというメリットを得ます。すでに作業でIris Instanceを使用している場合は、このようなアプリケーションを作成するために別の技術を使用する必要はありません。
## そしてPythonとは?
Pythonは最も人気の高いプログラミング言語の一つです。チューリング完全言語であり、開発課題のほとんどを楽にしてくれるオープンソースライブラリがたくさんあります。
私は、PythonとIrisを統合するために、IRISNative APIを使用しました。
## データベースモデル キー値
私のエンジンは、これでもかというほどシンプルに動作します。
各投稿を永続化するために、サブスクリプトが「post」、その次のサブスクリプトが投稿IDのグローバル「^blog」を作成します。
このグローバルに投稿コンテンツを入れて、終わりです!
これだけで、テーブル、インデックスなどを作成する必要はありません。
```
^blog("post", "1") = "# post 1 content..."
^blog("post", "2") = "# post 2..."
^blog("post", "3") = "# post 3 markdown content..."
```
## HTMLでマークダウンをレンダリングするには?
これで、オープンソースモジュール使用可能なPythonのメリットを無限に得るこquoとができます。私の選択は、たった1行のコマンドで簡単にマークダウンをレンダリングできるDashライブラリでした(^_^)
```python
import dash_core_components as dcc
import dash
import irisnative
#creating a connection with an IRIS Instance
conn = irisnative.createConnection("host","port","namespace","username","password") obj_iris = irisnative.createIris(conn)
#getting the content of one post with id 1
content = obj_iris.get("blog", "post", "1")
#creating the dash application
app = dash.Dash(__name__)
#rendering a markdown value
rendered_markdown_in_html = dcc.Markdown(content)
#showing on the page the rendender markdown
app.layout = html.Div([rendered_markdown_in_html])
```
## すべての投稿を表示するには?
サブスクリプト「^blog("post",)」繰り返して、上記と同じ方法でレンダリングされたマークダウンをページにプリントできます。
データベースをモデル化し、これよりも速く簡単に動作するフォームを作ったことがありますか?回答はコメント欄に書いてください!
## 自分でビルドしたくないけど、動作する様子は見たい!
簡単です!ここをクリックして、*御覧ください。*
http://iris-multimodel-suite.eastus.cloudapp.azure.com/blog-post
(サイトは既に閉鎖されています。ご了承ください)
# この記事とアプリケーションをお楽しみいただけましたか?
ここではマルチモデルコンテストでの私のアプリケーションの一部をご説明しています。よろしければ、私のアプリに投票してください。
記事
Toshihiko Minamoto · 2023年1月24日
ZPM は、InterSystems IRIS データプラットフォーム用のアプリケーションやモジュールと連携するように設計されています。モジュールを管理するためのCLIであるZPN Clientと、モジュールやメタ情報のデータベースであるThe Registryの2つのコンポーネントで構成されています。ZPM を使用して、モジュールの検索、インストール、アップグレード、削除、公開を行うことができます。ZPMを使用すると、ObjectScriptクラス、フロントエンドアプリケーション、Interoperabilityプロダクション、IRIS BIソリューション、IRISデータセット、またはEmbedded Pythonホイールなどのあらゆるファイルをインストールできます。
今日、このクックブックは3つのセクションについて説明します。
1. ZPMのインストール
2. モジュールの作成
3. Registry内のモジュールの検索、インストール、公開
1. ZPMのインストール
* ZPM最新バージョンのダウンロード(1つのXMLファイルです)[ダウンロードリンク](https://pm.community.intersystems.com/packages/zpm/latest/installer)
* ダウンロードしたXMLをIRISにインポートし、IRISターミナルを開き、次のようにコマンドを入力するだけでIRISにデプロイされます。
_write $SYSTEM.OBJ.Load("C:\zpm.xml", "c")_
注意:"C:\zpm.xml"は、ダウンロードしたXMLファイルのパスです。このステップはしばらく時間がかかるかもしれません。
* インストールが完了したら、_zpm_ とタイプして Enter キーを押すと、zpm シェルが表示されます。

2. モジュールの作成
モジュールの作成を始める前に、ダウンロード用のファイルを格納したフォルダを用意する必要があります。そこで、zpm というフォルダをC ドライブに 作成しました。
コマンド _generate C:/zpm_ を実行します。
必要な項目をすべて指定すると、最初のモジュールが正常に生成され、次のような画面も表示されます。

注意:
1. モジュールのバージョン(module version)はセマンティックバージョニングを使用しています
2. モジュールソースフォルダ(module source folder)は、すべてのクラスファイルが格納されているフォルダです。
3. zpm にはウェブアプリケーション(Web application)と依存関係(Dependencies)を追加するオプションもありますが、この例では空白にしておきます。
ここで、ファイルエクスプローラを開くと、以下のスクリーンショットのように、「module.xml」というファイルが表示されるはずです。

コマンド "_load C:\ZPM__" をタイプします。タイプすると、モジュールが再ロードされ、検証され、コンパイルされ、アクティブ化されます。

3. Registry内のモジュールの検索、インストール、公開
最新のRegistryで利用可能なパッケージを検索します: _zpm:USER>search_
最新のRegistry からパッケージをインストールする 例として、zpmshowというモジュールをパブリックRegistryにインストールする方法を説明します: _zpm:USER>install zpmshow _(コマンドは、 "moduleName" をインストールします)。
ロード後、モジュールを公開します: _zpm:USER>publish myFirstZPMDemo_
_zpm:USER>search_ を使って公開を確認することができます。この場合、"myfirstzpmdemo 0.1.0 "が最新のRegistryにあることがわかります。

注意:モジュールを公開しているときに、次のようなエラーが発生した場合:_ "エラー! 発行モジュールにおいて、何か問題が発生しました(ERROR! Publishing module, something went wrong)" と表示される場合、_最新のRegistry のステータスが有効で利用可能であることを確認してください。
zpm:USER>repo -list, _で最新のRegistryの状態を確認することができます。

ビデオ:[こちらをクリックしてください](https://www.loom.com/share/0ca097f0dea4476ea294841295f972b2%C2%A0%C2%A0)
記事
Mihoko Iijima · 2023年3月5日
開発者の皆さん、こんにちは!
1つ前の記事では、VSCodeのObjectScriptエクステンションに追加されたRESTサービスのデバッグツールについてご紹介しましたが、この記事では、RESTクライアントなどからHTTP要求を実行し、処理中のIRIS内プロセスにアタッチしながらデバッグを行う方法についてご紹介します。
事前準備などは、前回の記事と一緒になりますのでこの記事では割愛します。
図解のクラスでお試しいただく場合は、こちら👉https://github.com/Intersystems-jp/RESTDebug-VSCode をご利用ください。
この方法では、HTTP要求時に実行しているIRIS内プロセスを特定する必要があります。
そのため、デバッグ対象のメソッドで処理を待機させるため、hang 20のようにhangコマンドをコード中に追加して試します。
手順は以下の通りです。
1) プロセスにアタッチしてデバッグを行うため、launch.jsonを作成する。
VSCodeのデバッグ用ファイル:launch.jsonファイルを作成し、アタッチ用の設定を行います。
作成方法詳細は、VSCode:プロセスにアタッチしてデバッグする方法の「手順1:launch.json の用意」をご参照ください。
例)
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "objectscript",
"request": "attach",
"name": "アタッチデバッグ練習",
"system": true,
"processId": "${command:PickProcess}"
}
]
}
2) デバッグ対象メソッドにHangコマンド追加します。
Hangを利用して処理を数秒待機させ、その間に実行中プロセスを特定します。ブレークポイントを置きたい行の前あたりにhangコマンドを追加してください(20~30秒待機するように記述します)。
hang 20
VSCodeでコードを編集するためには、ワークスペース内にクラス定義用ファイル(*.cls)を配置する必要があります。
サーバ側にあるコードに追記したい場合は、ワークスペースにエクスポートしてから追記します。
エクスポートは、サーバ側の対象コードを右クリックし「Export」を選択します。(下図参照)
Export実行後、ワークスペースに移動します。エクスポートファイルは、/src/パッケージ名/クラス名.clsの形式でワークスペースにエクスポートされます。
対象クラスを開き、コードを追記します。
コードの追記が終わったら、Ctrl+Sで保存します(保存+コンパイルが実施されます)。
3) 外部クライアントからHTTP要求を実行します。
例は、PostmanからPOST要求でクエリパラメータとBodyに文字列を指定しています。
Sendボタンを実行すると、2) で指定したHangコマンドが実行されるため、指定秒数待機します。
この間に、アタッチ対象プロセスを探します。
4) アタッチ対象プロセスにアタッチする。
3) の実行後すぐ、VSCodeのデバッグウィンドウを開き、launch.jsonで指定したデバッグ名(例では、「アタッチデバッグ練習」)をクリックし、プロセスリストを表示させます。
プロセスリストには「RESTディスパッチクラス名.*」(*には数字が入ります)の形式で処理中ルーチン名が表示されます。
対象プロセスを見つけたら行をクリックしプロセスにアタッチします。
注意:指定したhangの秒数の間にプロセスを見つけてアタッチしてください。
5) デバッグを実行する。
Hangコマンドの実行が終わると、ブレークマークのところでデバッグが一時停止します。
後は、デバッグ用アイコンを利用してデバッグを進めるだけです。
6) デバッグを終了する。
最後に、デバッグ停止のボタンを押して終了します。
メモ:
アタッチ用プロセス一覧に対象プロセスが表示されない場合は、一旦VSCodeを再起動して再度お試しください。
アタッチのデバッグを実行するために追加したhangコマンドはデバッグ終了時必ず削除/コメント化してください。
記事
Megumi Kakechi · 2020年9月16日
これはInterSystems FAQ サイトの記事です。
HTMLからRESTを使って画像ファイルをアップロードする方法をご紹介します。
1.はじめに、以下のようなhtmlとクラスを作成してください。
*UploadTest.html
<html lang="ja">
<head>
<title>Upload</title>
</head>
<body>
<input id="up" type="file" />
<button id="btn">Upload</button>
<div></div>
<script type="text/javascript">
const sendfile = function(e) {
let up = document.getElementById("up");
let file = up.files[0];
let fd = new FormData();
fd.append("imgfile", file);
let xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
var result = document.querySelector('div');
xmlhttp.onload = function () {
result.innerHTML = xmlhttp.responseText;
};
};
xmlhttp.open("POST", "http://127.0.0.1:52773/csp/user/isjtest/uploadimg", true);
xmlhttp.send(fd);
}
let btn = document.getElementById("btn");
btn.addEventListener("click", sendfile);
</script>
</body>
</html>
*User.MyREST.cls (IRIS/CachéサーバのUSERネームスペースに作成してください)
※こちらのサンプルでは、C:\tempフォルダにファイルをUploadしています。 適宜フォルダを作成して頂くか、任意のフォルダパスに変更して再コンパイルしてください。
Class User.MyREST Extends %CSP.REST
{
Parameter CONVERTINPUTSTREAM = 1;
Parameter HandleCorsRequest = 1;
XData UrlMap
{
<Routes>
<Route Url="/uploadimg" Method="POST" Call="readMimeData" />
</Routes>
}
ClassMethod readMimeData() As %Status
{
set upload=$g(%request.MimeData("imgfile", 1))
set fname=%request.MimeData("imgfile",1).FileName
set file=##class(%File).%New("c:\temp\"_fname)
do file.Open("NWUK\BIN\")
do file.CopyFrom(upload)
set st = file.%Save()
if st {
write fname_" アップロード完了!!"
} else {
write fname_" アップロード失敗"
}
do file.Close()
quit $$$OK
}
}
2. ウェブ・アプリケーション /csp/user/isjtest の定義を作成します。 管理ポータル:[システム管理]>[セキュリティ]>[アプリケーション]> [ウェブ・アプリケーション]>[新しいウェブ・アプリケーションを作成] RESTのディスパッチ・クラスに、1.で作成したUser.MyRESTクラスを指定します。(下記画像参照)
3. 必要に応じて UploadTest.html を編集し(※1)、Webサーバのドキュメントルート(※2)に配置します。 ※1. xmlhttp.openには、環境にあったIPアドレス・ポートを指定してお試しください。
xmlhttp.open("POST", "http://<サーバIP>:<IRIS/Cachéポート>/csp/user/isjtest/uploadimg", true);
※2. 例:C:\inetpub\wwwroot
4. クライアントブラウザより以下を実行し、任意のファイルをUploadします。 http://localhost/UploadTest.html
5. 指定したフォルダにファイルがUploadされたことをご確認ください。 この投稿を参考にさせて頂き、初めてRESTでIRISへの接続をするプログラムを作成してみました。何となく思った通りに動作しそうな雰囲気です。アプリケーション開発環境での組み込みができていませんので、まだ何が起こるか不安がよぎっていますが、現時点では満足しています。本日は力尽きました...
私の場合、すでに構築済みの手法(フレームワーク、と呼べるほど大層なものではなりません)があり、その手法に合わせるために、敢えてJSONを利用していません。そのため、多くのRESTのサンプルコードがJSON前提ということもあり参考になるものが見当たらず、苦しみました。(自業自得、ですが..)
ところで、上記のサンプルコード(UploadTest.html のJavaScript部分)についてです。
var result = document.querySelector('div');
のコードがありますが、「div」に対応するオブジェクト(タグ)が無いように思われます。その為?サーバーからのレスポンスが画面上に表示されないようです。ご確認頂ければ幸いです。 RESTでIRISへの接続をお試しいただきありがとうございます。また、サンプルコードへのご指摘ありがとうございます。確かに、タグが抜けておりました。大変失礼いたしました。サンプル(UploadTest.html)に足りないタグを追加しました。お手数をおかけしますが、ご確認をよろしくお願いいたします。 <button id="btn">Upload</button> <div></div> ←このタグを追加 ご確認とコードの改修、有難う御座いました。
重箱の隅をつつくような内容でしたがご対応頂き感謝いたします。
記事
Hiroshi Sato · 2020年7月2日
初めに
VisM.OCXはVisual Basicでクライアント・サーバー型のアプリケーション開発を支援するためにInterSystemsが提供してきたツールです。
誕生から既に20年以上が経過した非常に古いテクノロジーです。
OCX規格(ActiveXコンポーネント)は、マイクロソフト社が推進してきた規格ですが、やがてマイクロソフト社が後継となる.Net Frameworkをリリースし、その新しいフレームワークへの移行を強力に推進すると同時に、OCX規格は非推奨機能となっています。
一方で下位互換性のため、.Net Framework配下でOCXを動作可能とする仕組みが用意されており、結果としてOCXは、.Net Framework環境下で動作可能です。
従って、VisM.OCXも.Net対応のプログラミング言語C#やVB.NETからそのまま利用することができます。
しかしながら.Net Framework上で動作するとは言え、.Net Frameworkが用意する安全性の高い資源管理や強固なセキュリティ機能の恩恵を受けることができず、いわゆるマネージドコードとして動作できないという制約を抱えています。
上記の状況を整理した結果、InterSystems社は、IRISをリリースするに際し、ViSM.OCXは、標準製品では動作せずに、特別なキーを適用することで動作可能になるような措置(使用を制限する)を適用しました。
その結果、VisM.OCXを使用するためには、InterSystems社にお問い合わせいただき、その特別なキーを取得するための申請を行う必要があります。
また広く皆様にIRISを使って評価していただきたいと考えて提供している無償のCommunityエディションでも動作しません。
ここでIRISでVisM.OCXアプリケーションを動かすのはハードルが高いと感じた方に朗報です。
VisM.OCXを使用せずに、ViSM.OCX対応アプリケーションをIRIS上で動作可能にするための移行ツール(Caché Directエミュレーター)を用意しました。
このツールは、Open Exchangeから取得可能です。
Open Exchangeサイト
ここでは、このツールを使った移行作業を実際に簡単なVBアプリケーションを例として紹介していきたいと思います。
ここで説明しているADBKアプリケーションは、下記のGithubから入手可能です。
ADBKサンプル
ADBKアプリケーション
このサンプルアプリケーションは、20年以上前にVB6サンプルとして作成されました。
アドレス帳をCaché データベースに保存し、クライアントのVB6アプリケーションからVisM.OCXを使って、データの登録や検索を行う非常にシンプルなサンプルです。
VB6プロジェクトを.Netプロジェクトに変換する
このVB6のアプリケーションをまず.Netアプリケーションに変換しなければなりません。
この変換を行うためには、最新のVisual Studioではだめで、Visual Studio 2008を別途インストールする必要があります。
変換方法は以下のサイトをご参考ください。
VB6アプリケーションをVB.NETに変換する方法
VisM.OCXの削除
次にその変換された.net プロジェクトファイルを新しいVisual Studio(現時点では2019が最新)で読み込みます。
まずメインフォームの右下に配置されているVisM.OCX(青い立方体のアイコン)が見えると思います。
この部品を削除します。(選択した後に、右クリックで削除メニュー項目が表示される)
次に右側に表示されているソリューションエクスプローラーの参照の所を開いて、AxVISMLibを選択して右クリックし、削除を選びます。
必ず部品の削除を先に行ってください。
ここで更新したファイルの保存を行い、プロジェクトを閉じます。
必要に応じて、この状態一式を別ディレクトリーにバックアップします。
C#エミュレータークラスを使用可能にする
Caché DirectエミュレータをOpen Exchangeからダウンロードし、そのZipファイルを適当なディレクトリに展開します。
cacheDirectWapper.csを新規のC# クラスライブラリープロジェクトに取り込みます。
参照設定でIRISの.Netライブラリーを指定します。
c:\intersystems\IRIS\dev\dotnet\bin\v4.5InterSystems.Data.IRISClient.dll
プロジェクトメニューからxxxのプロパティをクリックします。
xxxはプロジェクト名です。左側のペインからアプリケーションを選びます。
アセンブリ名は適当な名前にします。(CacheDirectEmulatorなど)対象のフレームワークが.Net Framework 4.5でなければ、.Net Framework 4.5に変更します。
左側のペインからビルドを選びます。構成をアクティブな(Release)またはReleaseにします。
ビルドします。(dllが作成されます)
Caché Directエミュレーターの参照設定を追加する
VB.Netのプロジェクトを再度開き、プロジェクトメニューから参照の追加を選択します。
作成されたdllを参照設定します。(dllとアプリケーションプロジェクトの.Net Frameworkのバージョンを合わせる必要があります)
アプリケーション修正
それでは、VisM.OCX用に作られたアプリケーションをCaché Directエミュレータのインタフェースを使って動作するように変更しましょう。
まず、ADBKMain.vbを開きます。
まず、新しく追加したCaché Directエミュレータライブラリーを参照できるようにインポートする必要があります。
これは先頭にあるOption文の後ろに以下のように追加します。
Option Strict OffOption Explicit On'以下のインポートが必要Imports cdapp
続いてVisM.OCXの置き換えです。
このVBアプリは、直接VisM.OCXを使用するのではなく、それをラップするクラスを作成していました。従って、ここでは、VisM.OCXを直接置き換えるのではなく、そのラッパークラスを置き換える方式を採用します。
元々の定義は以下のようになっていました。
cVMClassというのが、VisMをラップしたクラスです。
Dim CacheDirect As New cVMClass
これをCaché Directエミュレータクラスを参照するように変更します。
長いので途中で折り返しています。
Public WithEvents CacheDirect As cacheDirectWapper = New cacheDirectWapper("Server = localhost; Port=1972; Namespace=USER; Password = SYS; User ID = _system;")
VisM.OCXの場合は、サーバーへの接続は、いくつかオプションがあるのですが、このエミュレーターはオブジェクト生成時にサーバー接続まで行う仕様になっています。
WithEvents句は、イベント処理が不要の場合は、指定しなくてもいいです。
これでCacheDirectを参照しているところは、ほとんど修正することなくそのまま使用できます。
但しフォームのロード処理にいくつか変更が必要でした。
Private Sub ADBKMain_Load(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Load CacheDirect.VisM = VisM1.GetOcx If Not Install("Address Book Demo Application", VisM1, "GLO", "^ADBK", "USER", "+'$D(^$ROUTINE(""ADBK""))", "adbk.gsa", "MAC", "ADBK", "USER", "+'$D(^$ROUTINE(""ADBK""))", "adbk.rsa") Then End End Sub
ここで元々のラッパークラスでは、VisM.OCXをプロパティとして設定する仕様だったのですが、これは今回必要なくなりました。
次にデータやルーチンが初期インストールされていない場合にそれらをロードする仕組みを用意していたのですが、これは今はちゃんとライブラリーが用意されていて、それを使うほうが簡単なので、書き換えることにします。
Private Sub ADBKMain_Load(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Load 'CacheDirect.VisM = VisM1.GetOcx 'If Not Install("Address Book Demo Application", VisM1, "GLO", "^ADBK", "USER", "+'$D(^$ROUTINE(""ADBK""))", "adbk.gsa", "MAC", "ADBK", "USER", "+'$D(^$ROUTINE(""ADBK""))", "adbk.rsa") Then End
CacheDirect.Execute("=$DATA(^$ROUTINE(""ADBK""))") If CacheDirect.VALUE = 0 Then CacheDirect.P0 = "c:\temp" CacheDirect.P1 = "ck-d" CacheDirect.Execute("set P2=$system.OBJ.ImportDir(P0,,P1,.P2)") If CacheDirect.P2.Substring(0, 1) <> 1 Then MsgBox("Loadでエラーが発生しました" & CacheDirect.P2)
End If
End If
End Sub
最後にオブジェクトの消滅に関してもより安全な方法を採用したために、書き換えが必要です。
Private Sub ADBKMain_FormClosed(ByVal eventSender As System.Object, ByVal eventArgs As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed 'UPGRADE_NOTE: オブジェクト CacheDirect をガベージ コレクトするまでこのオブジェクトを破棄することはできません。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6E35BFF6-CD74-4B09-9689-3E1A43DF8969"' をクリックしてください。 CacheDirect = Nothing End Sub
Caché Directエミュレータクラスのendメソッドを呼び出すように変更します。
Private Sub ADBKMain_FormClosed(ByVal eventSender As System.Object, ByVal eventArgs As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed 'UPGRADE_NOTE: オブジェクト CacheDirect をガベージ コレクトするまでこのオブジェクトを破棄することはできません。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6E35BFF6-CD74-4B09-9689-3E1A43DF8969"' をクリックしてください。 'CacheDirect = Nothing CacheDirect.end() End Sub
続いてFindByName.vbの変更点を見てみましょう。
ここでもCacheDirectのインスタンス化を実施していましたが、今回はADBKMainクラスのCaché Directエミュレータクラスを共有できるので、ここで宣言する必要がなくなります。
以下の記述をコメントアウトします。
'Dim CacheDirect As New cVMClass
次に検索ボタンが押された時の処理を変更していきます。
Private Sub CmdFind_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles CmdFind.Click Dim i As Object 'Dim NOL As Short' 名前のリストを取得 ListLookupName.Items.Clear() '以下の機能は未実装 CacheDirect.Clear() CacheDirect.PDELIM = Chr(1) CacheDirect.P0 = "^ADBK(""XNAME"")" CacheDirect.P1 = TxtSNAME.Text CacheDirect.Execute("Do GetList^VISMUTIL(P0,P1,P1,"" "")") If CDbl(CacheDirect.Error_Renamed) <> 0 Then ' MsgBox (" Error " & CacheDirect.ErrorName) Exit Sub End If ' 取得した名前リストをListBoxに展開 For i = 1 To CacheDirect.NoOfPLISTItem 'UPGRADE_WARNING: オブジェクト i の既定プロパティを解決できませんでした。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6A50421D-15FE-4896-8A1B-2EC21E9037B2"' をクリックしてください。 ListLookupName.Items.Add(CacheDirect.PLISTItem(i)) Next iEnd Sub
ここでCacheDirect.Clear()はラッパークラスが独自に用意していたメソッドで、新しいエミュレータクラスには存在しません。
内容を確認した所、P0-P9,PLIST変数を初期化する処理のようで、処理を俯瞰する限り、ここで初期化する必要がないと判断し、コメントアウトすることにします。
ここでは、ADBKMainのエミュレータクラスを参照する必要があるため、すべてのCacheDirect参照の前にADBKMain.を付加する必要があります。
そして、VB6からVB.NETにコンバートする際に、Errorというキーワードは強制的に変換されるようで、Error_Renamedになっていたので、これを元のErrorに戻す必要がありました。
NoOfPLISTItemとPLISTItem(i)はラッパークラス独自の実装だったのですが、機能的には全く同じものがエミュレータークラスにあるので、そのメソッドに名前の変更を行いました。
Private Sub CmdFind_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles CmdFind.Click Dim i As Object 'Dim NOL As Short ' 名前のリストを取得 ListLookupName.Items.Clear() '以下の機能は未実装 'CacheDirect.Clear() ADBKMain.CacheDirect.PDELIM = Chr(1) ADBKMain.CacheDirect.P0 = "^ADBK(""XNAME"")" ADBKMain.CacheDirect.P1 = TxtSNAME.Text ADBKMain.CacheDirect.Execute("Do GetList^VISMUTIL(P0,P1,P1,"" "")") 'If CDbl(CacheDirect.Error_Renamed) <> 0 Then If CDbl(ADBKMain.CacheDirect.Error) <> 0 Then MsgBox (" Error " & ADBKMain.CacheDirect.ErrorName) Exit Sub End If ' 取得した名前リストをListBoxに展開 'For i = 1 To CacheDirect.NoOfPLISTItem For i = 1 To ADBKMain.CacheDirect.getPLISTLength() 'UPGRADE_WARNING: オブジェクト i の既定プロパティを解決できませんでした。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6A50421D-15FE-4896-8A1B-2EC21E9037B2"' をクリックしてください。 'ListLookupName.Items.Add(CacheDirect.PLISTItem(i)) ListLookupName.Items.Add(ADBKMain.CacheDirect.getPLIST(i)) Next iEnd Sub
次にイベント処理の変更です。
VisM.OCXでExecuteメソッドが実行されたことを補足するためのイベント処理が以下の様に定義されていました。
'Private Sub VisM1_Executed(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles VisM1.Executed Dim status As Object If VisM1.P9 <> "" Then 'UPGRADE_WARNING: オブジェクト status の既定プロパティを解決できませんでした。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6A50421D-15FE-4896-8A1B-2EC21E9037B2"' をクリックしてください。 status = MsgBox("データエラー " & VisM1.P9, MsgBoxStyle.OKOnly, "データエラー") End If End Sub
Handle以下をエミュレーターのイベントに書き換える必要があります。ここではVisM.OCXを直接参照していたので、その部分をエミュレータークラスを参照する様に変更します。
Private Sub CacheDirect_Executed(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles CacheDirect.ExecuteEvent Dim status As Object If CacheDirect.P9 <> "" Then ' 'UPGRADE_WARNING: オブジェクト status の既定プロパティを解決できませんでした。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6A50421D-15FE-4896-8A1B-2EC21E9037B2"' をクリックしてください。 status = MsgBox("データエラー " & CacheDirect.P9, MsgBoxStyle.OkOnly, "データエラー") End If End Sub
Errorイベントに関しても同様の変更を行います。
'Private Sub VisM1_OnError(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles VisM1.OnError Dim status As Object 'UPGRADE_WARNING: オブジェクト status の既定プロパティを解決できませんでした。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6A50421D-15FE-4896-8A1B-2EC21E9037B2"' をクリックしてください。 status = MsgBox("Cache' Direct Error " & VisM1.ErrorName, MsgBoxStyle.OKOnly, "Cache' Direct Error") 'エラーが発生した時点でのローカル変数をダンプする CacheDirect.PrintLocalVariable() End Sub
Private Sub CacheDirect_OnError(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles CacheDirect.ErrorEvent Dim status As Object ' 'UPGRADE_WARNING: オブジェクト status の既定プロパティを解決できませんでした。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6A50421D-15FE-4896-8A1B-2EC21E9037B2"' をクリックしてください。 status = MsgBox("Cache' Direct Error " & CacheDirect.ErrorName, MsgBoxStyle.OkOnly, "Cache' Direct Error") ''エラーが発生した時点でのローカル変数をダンプする 'CacheDirect.PrintLocalVariable() MsgBox("エラーが発生しました" & CacheDirect.ErrorName, MsgBoxStyle.OkOnly) End Sub
次に検索した結果で検索するためのOKボタンが押された時の処理です
Private Sub CmdOK_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles CmdOK.Click Dim result() As String CacheDirect.Clear() CacheDirect.P0 = VB6.GetItemString(ListLookupName, ListLookupName.SelectedIndex) '選択されている名前に対応するデータを検索する。 CacheDirect.Execute(("Do GetData^ADBK(P0)")) 'If CDbl(CacheDirect.Error_Renamed) <> 0 Then Exit Sub End If '検索フォームを非表示 Me.Hide() '取得データをフォームフィールドに展開する 'CType(ADBKMain.Controls("TxtNAME"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 1) 'CType(ADBKMain.Controls("TxtZIP"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 2) 'CType(ADBKMain.Controls("TxtADDRESS"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 3) 'CType(ADBKMain.Controls("TxtTELH"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 4) 'CType(ADBKMain.Controls("TxtTELO"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 5) 'CType(ADBKMain.Controls("TxtAGE"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 6) 'CType(ADBKMain.Controls("TxtDOB"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 7) End Sub
ここでもClearメソッドが呼ばれていますが、不要と判断し、コメントアウトしました。 先ほどと同様ErrorがError_Renameに強制的に変換されていました。 VB6.GetItemStringもVB6固有のメソッドになるので、書き換えが必要でした。 次にMPieceメソッドもラッパークラス固有の関数でしたが、内容を確認すると.NetのSplit関数を使うともっとシンプルに変換できることがわかりました。 先ほどと同様に先頭にADBKMain.を付加する必要があります。
Private Sub CmdOK_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles CmdOK.Click Dim result() As String 'CacheDirect.Clear() 'CacheDirect.P0 = VB6.GetItemString(ListLookupName, ListLookupName.SelectedIndex) If (ListLookupName.SelectedIndex >= 0) Then ADBKMain.CacheDirect.P0 = ListLookupName.Items(ListLookupName.SelectedIndex).ToString() '選択されている名前に対応するデータを検索する。 ADBKMain.CacheDirect.Execute(("Do GetData^ADBK(P0)")) 'If CDbl(CacheDirect.Error_Renamed) <> 0 Then Exit Sub End If '検索フォームを非表示 Me.Hide() '取得データをフォームフィールドに展開する 'CType(ADBKMain.Controls("TxtNAME"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 1) 'CType(ADBKMain.Controls("TxtZIP"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 2) 'CType(ADBKMain.Controls("TxtADDRESS"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 3) 'CType(ADBKMain.Controls("TxtTELH"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 4) 'CType(ADBKMain.Controls("TxtTELO"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 5) 'CType(ADBKMain.Controls("TxtAGE"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 6) 'CType(ADBKMain.Controls("TxtDOB"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 7) result = ADBKMain.CacheDirect.P1.ToString().Split(Chr(2)) CType(ADBKMain.Controls("TxtNAME"), Object).Text = result(0) CType(ADBKMain.Controls("TxtZIP"), Object).Text = result(1) CType(ADBKMain.Controls("TxtADDRESS"), Object).Text = result(2) CType(ADBKMain.Controls("TxtTELH"), Object).Text = result(3) CType(ADBKMain.Controls("TxtTELO"), Object).Text = result(4) CType(ADBKMain.Controls("TxtAGE"), Object).Text = result(5) CType(ADBKMain.Controls("TxtDOB"), Object).Text = result(6) End If End Sub
次にロード処理ですが、このフォーム上では何もする必要がないので、VisMコントロールのロード処理をコメントアウトします。
Private Sub FindByName_Load(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Load 'CacheDirect.VisM = CType(Controls("VisM1"), Object) End Sub
フォームのクローズ処理では、エミュレータクラスの終了処理は不要なので、その処理をコメントアウトします。
Private Sub FindByName_FormClosed(ByVal eventSender As System.Object, ByVal eventArgs As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed 'UPGRADE_NOTE: オブジェクト CacheDirect をガベージ コレクトするまでこのオブジェクトを破棄することはできません。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6E35BFF6-CD74-4B09-9689-3E1A43DF8969"' をクリックしてください。 'CacheDirect = Nothing End Sub
これで修正は終了で、アプリケーションも動作するはずです。
autoinstall.basとVmclass.clsは移行の結果不必要になったので、プロジェクトから削除します。
アプリケーション実行
エミュレータクラスのロード
OpenExchangeのサイトからCacheDirect.Emulator.clsをダウンロードし、それをIRISにロード(USERネームスペース)します。
アプリケーションルーチン、グローバル、クラスのロード
ADBKサンプルのGitHubからUser/ADBK.cls,ADBK.mac,VISMUTIL.mac,adbkglb.xmlをダウンロードし、c:\tempにコピーします。
アプリケーションの実行を行うと最初に必要なグローバル、クラス、ルーチンのロードを行うはずです。
最後に
全く無修正というわけにはいきませんが、既存の資産を生かしつつ、VisM.OCXを使用したアプリケーションの移行が簡単にできるということをご理解いただき、VisMアプリケーションの移行にチャレンジしていただきたいと思います。
もう一つ、今回はサーバー側の処理は全く修正していない点も強調しておきたいと思います。
ついでに今回のサンプルでも所々実施したように古い処理をリファクタリングするのにもいい機会だと思います。 Delphiなど.Netをサポートしていない開発ツールでVisM.OCXを使っていた人のためにODBC版も作ってみました。https://github.com/wolfman0719/CacheDirectODBCEmulator