データモデルクラス
ORDA を使用して、データモデル上に高レベルクラス関数を作成することができます。 これによってビジネス指向のコードを書き、APIのように "公開" することができます。 データストア、データクラス、エンティティ、およびエンティティセレクションはそれぞれ、関数を持つことのできるクラスオブジェクトとして提供されています。
たとえば、選択中の社員より給与の高い社員一覧を返す getNextWithHigherSalary()
関数を EmployeeEntity
クラスに作成したとします。 この関数は簡単に呼び出すことができます:
$nextHigh:=ds.Employee.get(1).getNextWithHigherSalary()
これらの関数はローカルデータストアだけでなく、クライアント/サーバーやリモートアーキテクチャーでも使用することができます:
//$cityManager はリモートデータストアへの参照です
Form.comp.city:=$cityManager.City.getCityName(Form.comp.zipcode)
この機能により、4D アプルケーションのビジネスロジックをまるごと独立したレイヤーに保存し、高レベルのセキュリティで簡単に管理・利用することができます:
-
わかりやすく使いやすい関数のみを公開し、その裏にある構造の複雑性を "隠す" ことができます。
-
構造が発展した場合には影響を受ける関数を適応させるだけで、クライアントアプリケーションは引き続き透過的にそれらを呼び出すことができます。
-
デフォルトでは、データモデルクラス関数はすべて、リモートアプリケーションに対し 非公開 に設定されており、RESTリクエストで呼び出すことはできません。 公開する関数は
exposed
キーワードによって明示的に宣言する必要があります。
各データモデルオブジェクトに関わるクラスは、4D によって あらかじめ自動的に作成 されます。
アーキテクチャー
ORDA では、4D
クラスストア を介して公開される 汎用クラス と、cs
クラスストア で公開される ユーザークラス が提供されています:
ORDA データモデルクラスはすべて cs
クラスストアのプロパティとして公開されます。 次の ORDA クラスが提供されています:
クラス | 例 | 次によってインスタンス化されます |
---|---|---|
cs.DataStore | cs.DataStore | ds コマンド |
cs.DataClassName | cs.Employee | dataStore.DataClassName , dataStore["DataClassName"] |
cs.DataClassNameEntity | cs.EmployeeEntity | dataClass.get() , dataClass.new() , entitySelection.first() , entitySelection.last() , entity.previous() , entity.next() , entity.first() , entity.last() , entity.clone() |
cs.DataClassNameSelection | cs.EmployeeSelection | dataClass.query() , entitySelection.query() , dataClass.all() , dataClass.fromCollection() , dataClass.newSelection() , entitySelection.drop() , entity.getSelection() , entitySelection.and() , entitySelection.minus() , entitySelection.or() , entitySelection.orderBy() , entitySelection.orderByFormula() , entitySelection.slice() , Create entity selection |
ORDA ユーザークラスは通常のクラスファイル (.4dm) としてプロジェクトの Classes サブフォルダーに保存されます (後述参照)。
ORDA データモデルユーザークラスのオブジェクトインスタンスは、それらの親クラスのプロパティや関数を使うことができます:
- Datastore クラスオブジェクトは、ORDA Datastore 汎用クラス の関数を呼び出すことができます。
- DataClass クラスオブジェクトは、ORDA DataClass 汎用クラス の関数を呼び出すことができます。
- EntitySelection クラスオブジェクトは ORDA EntitySelection 汎用クラス の関数を呼び出すことができます。
- Entity クラスオブジェクトは ORDA Entity 汎用クラス の関数を呼び出すことができます。
クラスの説明
履歴
リリース | 内容 |
---|---|
18 R5 | データモデルクラス関数は、デフォルトでは REST に公開されません。 新しい exposed および local キーワード。 |
DataStore クラス
4D のデータベースは、自身の DataStore クラスを cs
クラスストアに公開します。
- 親クラス: 4D.DataStoreImplementation
- クラス名: cs.DataStore
DataStore クラス内には、ds
オブジェクトを介して使用する関数を作成することができます。
例題
// cs.DataStore class
Class extends DataStoreImplementation
Function getDesc
$0:="社員と会社を公開するデータベース"
この関数は次のように使えます:
$desc:=ds.getDesc() //"社員と会社を..."
DataClass クラス
ORDA で公開されるテーブル毎に、DataClass クラスが cs
クラスストアに公開されます。
- 親クラス: 4D.DataClass
- クラス名: cs.DataClassName (DataClassName はテーブル名です)
- 例: cs.Employee
例題
// cs.Company クラス
Class extends DataClass
// 収益が平均以上の会社を返します
// Company DataClass にリレートしているエンティティセレクションを返します
Function GetBestOnes()
$sel:=This.query("revenues >= :1";This.all().average("revenues"));
$0:=$sel
全会社データから平均以上の会社データをエンティティセレクションに抽出するには次を実行します:
var $best : cs.CompanySelection
$best:=ds.Company.GetBestOnes()
リモートデータストアの例
次の City カタログをリモートデータストアとして公開しています:
City クラス
は API を提供しています:
// cs.City クラス
Class extends DataClass
Function getCityName()
var $1; $zipcode : Integer
var $zip : 4D.Entity
var $0 : Text
$zipcode:=$1
$zip:=ds.ZipCode.get($zipcode)
$0:=""
If ($zip#Null)
$0:=$zip.city.name
End if
クライアントはまず、リモートデータストアのセッションを開始します:
$cityManager:=Open datastore(New object("hostname";"127.0.0.1:8111");"CityManager")
クライアントアプリケーションは API を使い、たとえばフォームに入力された郵便番号 (zipcode) に合致する都市を取得することができます:
Form.comp.city:=$cityManager.City.getCityName(Form.comp.zipcode)
EntitySelection クラス
ORDA で公開されるテーブル毎に、EntitySelection クラスが cs
クラスストアに公開されます。
- 親クラス: 4D.EntitySelection
- クラス名: DataClassNameSelection (DataClassName はテーブル名です)
- 例: cs.EmployeeSelection
例題
// cs.EmployeeSelection クラス
Class extends EntitySelection
// 給与が平均超えの社員を当該エンティティセレクションから抽出します
Function withSalaryGreaterThanAverage
C_OBJECT($0)
$0:=This.query("salary > :1";This.average("salary")).orderBy("salary")
任意の社員エンティティセレクションより、給与が平均以上の社員を取得するには:
$moreThanAvg:=ds.Company.all().employees.withSalaryGreaterThanAverage()
Entity クラス
ORDA で公開されるテーブル毎に、Entity クラスが cs
クラスストアに公開されます。
- 親クラス: 4D.Entity
- クラス名: DataClassNameEntity (DataClassName はテーブル名です)
- 例: cs.CityEntity
例題
// cs.CityEntity クラス
Class extends Entity
Function getPopulation()
$0:=This.zips.sum("population")
Function isBigCity
C_BOOLEAN($0)
// 関数 getPopulation() をクラス内で使用することができます
$0:=This.getPopulation()>50000
次のように関数を呼び出すことができます:
var $cityManager; $city : Object
$cityManager:=Open datastore(New object("hostname";"127.0.0.1:8111");"CityManager")
$city:=$cityManager.City.getCity("Caguas")
If ($city.isBigCity())
ALERT($city.name + " は大きな町です。")
End if
定義規則
データモデルクラスを作成・編集する際には次のルールに留意しなくてはなりません:
-
4D のテーブル名は、cs クラスストア 内において自動的に DataClass クラス名として使用されるため、cs 名前空間において衝突があってはなりません。 特に:
- 4D テーブルと ユーザークラス名 に同じ名前を使用してはいけません。 衝突が起きた場合には、ユーザークラスのコンストラクターは使用不可となります (コンパイラーにより警告が返されます)。
- 4D テーブルに予約語を使用してはいけません (例: "DataClass")。
-
クラス定義の際、
Class extends
ステートメントに使用する親クラスの名前は完全に合致するものでなくてはいけません (文字の大小が区別されます)。 たとえば、EntitySelection クラスを継承するにはClass extends EntitySelection
と書きます。 -
データモデルクラスオブジェクトのインスタンス化に
new()
キーワードは使えません (エラーが返されます)。 上述の ORDA クラステーブルに一覧化されている、通常の インスタンス化の方法 を使う必要があります。 -
4D
クラスストア のネイティブな ORDA クラス関数を、データモデルユーザークラス関数でオーバーライドすることはできません。
公開vs非公開関数
セキュリティ上の理由により、データモデルクラス関数はデフォルトですべて、リモートリクエストに対し 非公開 (つまりプライベート) に設定されています。
リモートリクエストには次のものが含まれます:
Open datastore
によって接続されたリモートの 4Dアプリケーションが送信するリクエスト- RESTリクエスト
通常の 4Dクライアント/サーバーリクエストは影響されません。 このアーキテクチャーにおいては、データモデルクラス関数は常に利用可能です。
公開されていない関数はリモートアプリケーションで利用することができず、RESTリクエストによるオブジェクトインスタンスに対して呼び出すこともできません。 リモートアプリケーションが非公開関数をアクセスしようとすると、"-10729 (未知のメンバー機能です)" エラーが返されます。
リモートリクエストによる呼び出しを許可するには、exposed
キーワードを使ってデータモデルクラス関数を明示的に宣言する必要があります。 シンタックスは次の通りです:
// 公開関数の宣言
exposed Function <functionName>
exposed
キーワードは、データモデルクラス関数に対してのみ利用可能です。 通常のユーザークラス 関数に対して使った場合、キーワードは無視され、コンパイラーはエラーを返します。
例題
公開された関数によって、DataClass クラスのプライベート関数を呼び出します:
Class extends DataClass
// 公開関数
exposed Function registerNewStudent($student : Object) -> $status : Object
var $entity : cs.StudentsEntity
$entity:=ds.Students.new()
$entity.fromObject($student)
$entity.school:=This.query("name=:1"; $student.schoolName).first()
$entity.serialNumber:=This.computeSerialNumber()
$status:=$entity.save()
// 非公開 (プライベート) 関数
Function computeIDNumber()-> $id : Integer
// 新規ID番号を算出します
$id:=...
呼び出し元のコードは次の通りです:
var $remoteDS; $student; $status : Object
var $id : Integer
$remoteDS:=Open datastore(New object("hostname"; "127.0.0.1:8044"); "students")
$student:=New object("firstname"; "Mary"; "lastname"; "Smith"; "schoolName"; "Math school")
$status:=$remoteDS.Schools.registerNewStudent($student) // OK
$id:=$remoteDS.Schools.computeIDNumber() // エラー (未知のメンバー機能です)
ローカル関数
クライアント/サーバーアーキテクチャーではデフォルトで、ORDA データモデル関数は サーバー上で 実行されます。 関数リクエストとその結果だけが通信されるため、通常はベストパフォーマンスが提供されます。
しかしながら、状況によってはその関数はクライアント側で完結するものかもしれません (たとえば、すでにローカルキャッシュにあるデータを処理する場合など)。 そのような場合には、local
キーワードを使ってサーバーへのリクエストをおこなわないようにし、アプリケーションのパフォーマンスを向上させることができます。 シンタックスは次の通りです:
// クライアント/サーバーにおいてローカル実行する関数の宣言
local Function <functionName>
このキーワードを使うと、関数は常にクライアントサイドで実行されます。
local
キーワードは、データモデルクラス関数に対してのみ利用可能です。 通常のユーザークラス 関数に対して使った場合、キーワードは無視され、コンパイラーはエラーを返します。
最終的にサーバーへのアクセスが必要になっても (ORDAキャッシュが有効期限切れになった場合など) 関数は動作します。 もっとも、それではローカル実行によるパフォーマンスの向上は見込めないため、ローカル関数がサーバー上のデータにアクセスしないことを確認しておくことが推奨されます。 サーバーに対して複数のリクエストをおこなうローカル関数は、サーバー上で実行されて結果だけを返す関数よりも非効率的です。 たとえば、Schools Entityクラスの次の関数を考えます:
// 2000年以降の生まれの生徒を検索します
// local キーワードを適切に使用していない例です
local Function getYoungest
var $0 : Object
$0:=This.students.query("birthDate >= :1"; !2000-01-01!).orderBy("birthDate desc").slice(0; 5)
local
キーワードを 使わない 場合、1つのリクエストで結果が得られます。local
キーワードを 使う 場合、4つのリクエストが必要になります: Schools エンティティの students エンティティセレクションの取得、query()
の実行、orderBy()
の実行、slice()
の実行。 この例では、local
キーワードを使用するのは適切ではありません。
例題
年齢の計算
birthDate (生年月日) 属性を持つエンティティがある場合に、リストボックス内で呼び出すための age()
関数を定義します。 この関数をクライアントサイドで実行することで、リストボックスの各行がサーバーへのリクエストを生成するのを防ぎます。
StudentsEntity クラス:
Class extends Entity
local Function age() -> $age: Variant
If (This.birthDate#!00-00-00!)
$age:=Year of(Current date)-Year of(This.birthDate)
Else
$age:=Null
End if
属性のチェック
クライアントにロードされ、ユーザーによって更新されたエンティティの属性について、サーバーへ保存リクエストを出すまえに、それらの一貫性を検査します。
StudentsEntity クラスのローカル関数 checkData()
は生徒の年齢をチェックします:
Class extends Entity
local Function checkData() -> $status : Object
$status:=New object("success"; True)
Case of
: (This.age()=Null)
$status.success:=False
$status.statusText:="生年月日が入力されていません。"
:((This.age() <15) | (This.age()>30) )
$status.success:=False
$status.statusText:="生徒の年齢は 15 〜 30 の範囲で入力してください。この生徒の年齢は "+String(This.age()+"です。")
End case
呼び出し元のコード:
var $status : Object
// Form.student は全属性とともにロードされており、フォーム上で更新されました
$status:=Form.student.checkData()
If ($status.success)
$status:=Form.student.save() // サーバーを呼び出します
End if
データモデルクラスの管理
クラスファイル
ORDA データモデルユーザークラスは、クラスと同じ名称の .4dm ファイルを 通常のクラスファイルと同じ場所 (つまり、Project フォルダー内の /Sources/Classes
フォルダー) に追加することで定義されます。 たとえば、Utilities
データクラスのエンティティクラスは、UtilitiesEntity.4dm
ファイルによって定義されます。
クラスの作成
各データモデルオブジェクトに関わるクラスは、4D によってあらかじめ自動的にメモリ内に作成されます。
空の ORDA クラスは、デフォルトではエクスプローラーに表示されません。 表示するにはエクスプローラーのオプションメニューより データクラスを全て表示 を選択します:
ORDA ユーザークラスは通常のクラスとは異なるアイコンで表されます。 空のクラスは薄く表示されます:
ORDA クラスファイルを作成するには、エクスプローラーで任意のクラスをダブルクリックします。 4D はクラスファイルを作成し、extends
ステートメントを自動で追加します。 たとえば、Entity クラスを継承するクラスの場合は:
Class extends Entity
定義されたクラスはエクスプローラー内で濃く表示されます。
クラスの編集
定義された ORDA クラスファイルを 4D メソッドエディターで開くには、ORDA クラス名を選択してエクスプローラーのオブションメニュー、またはコンテキストメニューの 編集... を使用するか、ORDA クラス名をダブルクリックします:
ローカルデータストア (ds
) に基づいた ORDA クラスの場合には、4D ストラクチャーウィンドウからも直接クラスコードにアクセスできます:
メソッドエディター
4D メソッドエディターにおいて、ORDA クラス型として定義された変数は、自動補完機能の対象となります。 Entity クラス変数の例です: