1. データ管理について

リレーショナルデータベース(RDB)などに代表されるデータの構造定義や永続化を実現する仕組みとして、 iPLAssではEntityメタデータを利用します。 RDBにおけるテーブルはEntityとして定義し、テーブルのカラムはPropertyとして定義します。

Entityには以下の機能・特徴があります。

  • 動的な定義
    Entityは管理画面上から動的に作成することができます。 RDBのように物理的にテーブルを追加・修正する必要はありません。 またEntityに定義可能なPropertyにはタイプ(StringやInteger、Binaryなど)ごとに 特化したProperty型があらかじめ用意されています。 格納したいデータタイプに合わせてProperty型を選択していきます。

  • データ操作画面の自動生成(Generic Entity Manager)
    Entityを定義するだけで、汎用的な検索画面、登録・編集画面が実行可能になります。 汎用画面は、表示項目や表示レイアウトなど、独自にカスタマイズすることもできます。 またカレンダービュー、ツリービューなどいくつかの表示形式を標準で提供しています。
    詳細はGEM(Generic Entity Manager)を参照してください。

  • データ検証
    Propertyごとに必須チェックやLengthチェックなどのValidatorを設定することができます。

  • EventListener
    EntityのCRUD操作(登録、参照、更新、削除など)時の前後に独自処理を組み込むことができます。

  • データのバージョン管理
    Entityの1レコードに対して、複数のバージョンを保持することができます。 バージョン管理を利用することで、修正履歴を保持できたり、先日付データを事前に登録することが可能になります。

  • Entityの権限制御
    EntityのCRUD操作(登録、参照、更新、削除など)に対する権限をロールを利用して制御することができます。
    詳細は認可を参照してください。

  • クエリ言語(EQL)
    データ操作はiPLAss独自のEQLというSQLライクなクエリ言語を利用します。 Entityデータの永続化にはバックエンドでRDBを利用しますが、 開発者はバックエンドのRDB差異を意識する必要はほとんどありません。
    詳細はEQLリファレンスを参照してください。

  • WebApi操作
    EntityのCRUD操作(登録、参照、更新、削除など)を実行するWebApiを標準で提供しています。
    詳細はEntityCRUD APIを参照してください。

  • 全文検索
    Entityデータに対して、全文検索エンジンを利用したIndexの生成、検索機能を標準でサポートしています。
    詳細は全文検索を参照してください。

  • データ監査ログ
    EntityのCRUD操作(登録、参照、更新、削除など)に対するログを記録することができます。 記録されたログはデータ操作画面上で表示したり、管理コンソール上で参照することができます。

  • データ暗号化
    Entityのデータ内容をデータベースに保存する際に暗号化することができます。 データベースの暗号化オプションを利用可能な場合は、そちらを利用することを推奨しますが、 データベースの暗号化オプションの利用が難しい場合の次善のソリューションとして利用可能です。

2. Entity

2.1. Entityの作成

Entityアイコンを右クリックして「Entityを作成する」を選択してください。

階層化(フォルダのようにグループ化)する場合はドット(.)区切りで指定してください。 他のメタデータは基本的にスラッシュ(/)で階層化しますが、Entityは特別です。
Entityを作成したタイミングで、メニューアイテムが作成されます。 またテナントに標準で作成されている DEFAULT メニュー定義がある場合は、 最後にアイテムが追加されます。

2.2. 共通Property

Entityにはあらかじめ標準PropertyとシステムPropertyが定義されています。

あらかじめ定義されているPropertyは変更や削除することはできません。
共通のPropertyとして利用されている名前は予約語です。Entity内で同じ名前のPropertyを定義することはできません。

標準Property

レコードのキーに該当するIDやレコードの内容を表すPropertyが定義されています。

名前 表示名 データ型 内容

oid

オブジェクトID

String

レコードを一意に特定するID。変更不可。

name

名前

String

レコードを表すラベル。必須項目。値はユニークである必要はありません。

description

説明

String

レコードの説明。任意項目。

オブジェクトID (oid)

Entityデータは1レコード単位に oid という一意のKEY値を保持します。

標準の動作として、oid はEntityデータの登録時に自動的に採番されます。 更新処理で変更することはできません。 後述するEntityどうしの参照定義(Reference Property)においても、 この oid で参照関係を保持します(正確には oid + version です)。

entity property oid
一意となる範囲

標準で自動採番される oid 値は、Entity単位で一意となる値です。 テナント単位で一意な値ではないので、 oid からEntityを特定することはできません。

名前 (name)

name はレコードに対するラベルを想定しています。
name は必須項目ですが値の変更は可能です。また値として一意である必要もありません。

説明 (description)

description はレコードに対する説明を想定しています。
description はString型の任意項目なので、利用可否も含め自由に利用してください。

システムProperty

データのバージョン制御のためのPropertyや、データ操作時の情報を保持するPropertyが定義されています。 システムPropertyは自由に値を設定することはできません。データ操作時に自動で制御されます。

名前 表示名 データ型 内容

version

バージョン

Integer

データのバージョン管理 で利用。管理しない場合は0。

state

ステータス

Select

バージョン管理用。

startDate

有効開始日

DateTime

バージョン管理用。

endDate

有効終了日

DateTime

バージョン管理用。

createDate

作成日

DateTime

データ作成日時。

updateDate

更新日

DateTime

データ更新日時。

createBy

作成者

String

データ作成者。作成者に該当する User Entityの oid が格納される。

updateBy

更新者

String

データ更新者。更新者に該当する User Entityの oid が格納される。

lockedBy

ロックユーザ

String

データロック機能用。ロック者に該当する User Entityの oid が格納される。

システムPropertyの注意点

システムPropertyは制御用としてiPLAss内部で制御されるため、 各アプリケーションで独自の用途として値を格納したりしないでください。 バージョンアップなどで増減したり、格納される値が変更される可能性があります。

startDateendDate についても、「ユーザ」「おしらせ情報」Entityなどで一部利用していますが、 Entityの バージョン管理設定 により利用される可能性があるため、基本的には利用できません。

2.3. 設定

Entityに対して、保存したいデータ項目に合わせたPropertyを追加していきます。
Entityに対するより高度な設定については 高度な設定 で説明します。

Propertyの作成

Propertyを作る際は、格納する値の型に特化したデータ型の選択や、必須項目や長さなどの制約条件の設定を行います。

Propertyを追加する場合は Add ボタン、編集する場合は対象のPropertyレコードをダブルクリック、 削除する場合は対象のPropertyを選択(ShiftやCtrlで複数可)して Remove ボタンをクリックします。 またPropertyの順番を並び替える場合はDrag&Dropで並び替えてください。

Propertyの設定

Propertyは次の項目を設定可能です。 また指定したPropertyのデータ型によって、設定する項目が変更されます (一部データ型により変更できない項目があります)。

設定項目 設定内容

Name

物理名を指定します。英数字のみ指定可能です。

Display Name

表示名を指定します。
未指定の場合、Entity定義の保存時にNameが設定されます。

Type

データ型を指定します。

Multiple

多重度を指定します。

Required

必須項目かを指定します。
必須項目にした場合、検証ロジック(Validator)に NotNull が追加されます。

CanEdit

値を変更できるかを指定します。
Entityデータの更新時(update)の変更チェックや、汎用画面上での編集可否に利用されます。

Index Type

インデックスを指定します。

Encrypt Mode

暗号化有無、および形式を指定します。

Validator

検証ロジックを指定します。

データ型の指定

提供されているPropertyのデータ型です。基本的な型とiPLAssに特化した特殊な型があります。

基本型
説明 Java型

String

文字列型です。
文字数に関する制約があります。

String

Boolean

真・偽を表す型です。

Boolean

Integer

整数を表す数値型です。

Long

Float

小数を表す数値型です。

Double

Decimal

正確な小数計算を扱う数値型です。
少数桁数と丸めモードが指定可能です。

BigDecimal

DateTime

日付と時間を表す型です。

java.sql.Timestamp

Date

日付を表す型です。

java.sql.Date

Time

時間を表す型です。

java.sql.Time

特殊型(拡張型)
説明 Java型

Select

選択値として「値とラベル」のセットを定義することができる型です。

org.iplass.mtp.entity.SelectValue

AutoNumber

自動採番値を扱う型です。

String

Expression

式を設定することができる型です。

String

Binary

バイナリデータを扱う型です。(BLOB型)

org.iplass.mtp.entity.BinaryReference

LongText

String型では入りきらない文字列を扱う型です。(BLOB型)

String

Reference

各エンティティを関連付ける事ができる型です。

org.iplass.mtp.entity.Entity

Java型はCommandやUtilityClass、TemplateなどでEntityデータを扱う際のデータ型です。

それぞれのデータ型の詳細は、データ型 を参照してください。

多重度について

Entityには1つのPropertyに対して複数の値を保持することが可能となっています(配列イメージ)。

oid name col1(multiple=5) ref1(multiple=*)

1

data1

[a,b,c,d,e]

[{oid=1,ver=0},{oid=2,ver=0},{oid=3,ver=0}]

2

data2

[a,b,c]

[]

3

data3

[]

[{oid=1,ver=0},{oid=2,ver=0}]

  • Reference 型については、無制限を表す「*」を指定できます。

  • Reference 型以外のPropertyは数値のみ指定できます。

Reference 型以外で多数の多重度を持たせたい場合は、パフォーマンスを考慮して、 別Entityを定義して Reference 型で参照するようにするか、 独自の Storage Space を利用することを検討してください。

2.4. 高度な設定

Entityに対して提供されているその他の機能について説明します。

設定項目 設定内容

共通Propertyの変更

oidname Propertyをカスタマイズします。

Versioning

Entityデータのバージョン管理方式を指定します。

Storage Space

Entityデータの格納先を指定します。

save audit log

EntityデータのCRUD操作に対するログを記録するかを指定します。

crawl for full text search

全文検索機能用のINDEXを作成するかを指定します。

cache query result

Entityの検索結果をキャッシュするかを指定します。

Mapping Class

CommandなどでEntity操作を行う際に、Javaで実装されたEntityクラスを利用する場合に指定します。

EventListener

EntityのEventListenerを指定します。

Data Localization

Entityデータの国際化対応を指定します。

共通Propertyの変更

oidname については他のPropertyを指定することで代用することができます。

oid Propertyの変更

外部システムからのデータ取り込みや、IDとしてユーザにわかりやすい値にしたい場合など、 oid として利用するPropertyを変更することが可能です。

Admin Consoleで oid を指定する場合は、「OID」列で指定してください。

entity property oid custom
指定可能なProperty属性

以下の属性のPropertyのみ oid として指定することが可能です。

  • 型が 基本型 または AutoNumber である

  • 必須項目である

  • 変更不可である

  • 多重度が1である

複合指定

複数のPropertyを指定することが可能です。 この場合、 oid に格納される値は、選択したPropertyを上から順番にハイフン「-」で 結合した値になります。

登録済データに対する注意点

既に登録済みのEntityデータが存在する状態で oid Propertyを変更した場合、 登録済みのデータは自動的には変更されません。 このような場合は、Entityデータのエクスポート/インポートなどによりデータを手動で変更する必要があります。

name Propertyの変更

oid 同様、 name も対象とするPropertyを変更することが可能です。

Admin Consoleで変更する場合は、「Name」列で指定してください。

指定可能なProperty属性

以下の属性のPropertyのみ name として指定することが可能です。

複合指定

oid と異なり、複数のPropertyを指定することはできません。

「name」を指定した場合の注意点

name を指定した場合には、以下の点について考慮する必要があります。

  • 登録時の動作
    name Propertyを独自に設定した場合、Entityデータの更新時に name に同じ値が設定されます( name Propertyがなくなるわけではありません)。 例えば、 name Propertyと、独自に指定したPropertyそれぞれに値を設定して更新した場合、name に設定された値は上書きされます。

  • 汎用画面(GEM)のカスタマイズが必要
    汎用画面では、標準で namedescription Propertyが検索・詳細画面上に表示されます。 name Propertyを独自に設定した場合は、name を削除し独自Propertyを追加するなど、汎用画面のレイアウト調整が必要です。

登録済データに対する注意点

既に登録済みのEntityデータが存在する状態で name Propertyを変更した場合、 登録済みのデータは自動的には変更されません。 例えば、必須ではない項目を必須に変更して name に指定した場合も、 登録済のデータは値が設定されていない可能性がある状態になってしまいます。このようなデータは最初の更新時にエラーになります。
この場合は、Entityデータのエクスポート/インポートなどにより登録済みデータを手動でメンテナンスする必要があります。

nameを利用したくない場合

作成したいEntityに name に該当するPropertyがない場合は、 AutoNumber 型のPropertyを作成して、それを name として指定してください。

データのバージョン管理

Entityの1レコードに対して、複数のバージョンを保持することができます。 バージョン管理を利用することで、修正履歴を保持できたり、先日付データを事前に登録することが可能になります。

管理方式には以下の種類があります。

タイプ 説明

NONE

バージョンデータを保持しません。データに対する最新の状態のみ保持されます。

NUMBER BASE

共通Propertyの version を利用して、複数のバージョンデータを保持します。

TIME BASE

共通Propertyの startDateendDate (有効期間)を利用して、複数のバージョンデータを保持します。

NUMBER BASE

共通Propertyの version を複数保持することが可能になります。 検索処理で有効なデータは version が最大のものになります。

entity versioning numberbase
version は排他制御のためのものではありません。排他制御は updateDate でチェックしています。
TIME BASE

共通Propertyの startDateendDate (有効期間)を複数保持することが可能になります。 データを更新する際に有効期間を指定します。 検索処理で有効なデータは システム日時で決定されます。

entity versioning timebase

データ更新時に有効期間が未指定の場合は、自動的に「システム日時」~「2099/12/31 00:00:00」が設定されます。 また TIME BASE の場合も version はカウントアップされます。

TIME BASEの更新時に期間を変更する際の注意点

上の例のように有効期間が重ならないようにデータを更新した場合は問題ないのですが、 例えば有効期間が「2018/1/1」から「2018/12/31」のデータに対して、「2018/6/1」から別の値に変更したい場合は、 別バージョンのデータ「2018/6/1」~「2018/12/31」を作成すると有効期間がかぶります。

entity versioning timebase update1

システム日時に対して有効なデータ期間が複数存在する場合は、 version が大きい方が有効なデータとなります。 もしデータとして重複を避けたい場合は、登録済のデータの有効終了日を新しい有効開始日の前(「2018/1/1」~「2018/5/31」)に修正してください。

entity versioning timebase update2
バージョンデータの更新方法

新しいバージョンのデータを作成する場合は、明示的に更新する必要があります。 普通に更新した場合は新しいバージョンデータは作成されず、対象バージョンのデータが更新されます。

汎用画面からデータを登録する場合は、詳細画面に 新しいバージョンとして更新 というボタンが表示されるので、 そこから更新を行います。

Commandなど独自で更新処理を実装する場合は UpdateOption で設定します。

import org.iplass.mtp.entity.EntityValidationException;
import org.iplass.mtp.entity.UpdateOption;
import org.iplass.mtp.entity.TargetVersion;

EntityManager em = ManagerLocator.manager(EntityManager.class);

try {
    //別バージョンで更新する場合、UpdateOptionに対して、
    //TargetVersion.NEWを指定すると新しいバージョンとして保存される
    //通常のupdate処理は「TargetVersion.CURRENT_VALID」(有効バージョン)
    UpdateOption option = new UpdateOption();
    option.setTargetVersion(TargetVersion.NEW);

    em.update(entity, option);
} catch (EntityValidationException e) {
}
ReferencePropertyが参照するバージョン

Entity間の連携を定義するためのReference型のPropertyが参照する参照先のバージョンは、 ReferencePropertyで指定した「バージョン管理」設定によって決定されます。

Unique Property の制約

バージョン管理を行う場合、 Unique Index が指定されたPropertyは変更不可になります。 Entity定義の保存時に、 Unique Index が指定されているPropertyは canEdit = false として保存されます。

Storage Spaceの変更

全てのEntityのデータは、標準の設定ではバックエンドのRDB上で1つの物理テーブルに格納されます。

例えば、

  • 一部のデータを暗号化したいため当該Entityを格納するテーブルのテーブルスペースを分けたい

  • あるEntityは大量件数が想定されるため、他のデータとは物理的に別テーブルで管理し、他のEntityへのパフォーマンス面での悪影響を抑えたい

といった要件を実現すため、Entityデータを格納する領域をEntity単位で指定することが可能です。 この領域のことをiPLAssでは Storage Space と呼称します。

ただしこの機能を利用するには、通常意識しないバックエンドDB上に物理テーブルを作成し、service-configにStorageSpace定義が必要となります。

詳細は Storage Space を参照してください。

データ操作ログの記録

EntityデータのCRUD操作に対するログを記録することができます。

記録されたログは、汎用画面の操作ログSectionで表示することが可能です。 詳細は 操作ログセクションを参照してください。

また AuditLogManager を利用することでプログラム上で取得することが可能です。

/** 注意:以下はGroovy形式で書いています。 */

import org.iplass.mtp.ManagerLocator;
import org.iplass.mtp.entity.auditlog.AuditLog;
import org.iplass.mtp.entity.auditlog.AuditLogManager;
//import org.iplass.mtp.entity.auditlog.AuditLog.Action;

AuditLogManager alm = ManagerLocator.manager(AuditLogManager.class);

String entityName = "samples.Sample";
String oid = "00001";

//ログは件数が多くなることが想定されるため、limit(-1などは不可)、offsetの指定が必要です
int limit = 100;
int offset = 0;

//ログの取得
List<AuditLog> auditLogs = alm.getAuditLog(entityName, oid, limit, offset);

println("audit log size =" + auditLogs.size());

auditLogs.each{auditLog ->
    println("log id =" + auditLog.getLogId());
    println("action =" + auditLog.getAction());           //操作Action(AuditLog.Action)
    println("user =" + auditLog.getUserId())              //操作ユーザ(oid)
    println("property =" + auditLog.getPropertyName())    //プロパティ
    println("oldValue =" + auditLog.getOldValue())        //更新前の値
    println("newValue =" + auditLog.getNewValue())        //更新後の値

    //他の値はAuditLogのJavaDocを参照してください。
};

/* java形式
auditLogs.forEach(auditLog -> {
    System.out.println("log id =" + auditLog.getLogId());
    System.out.println("action =" + auditLog.getAction());
    ・・・・・
});
*/

操作ユーザに格納されている値は ユーザID のため、名称が必要な場合は検索する必要があります。 またReferencePropertyに対する値も oid のため、参照先の名前などが必要な場合は検索する必要があります。

Query結果のキャッシュ

検索時のQueryに対する検索結果をキャッシュすることが可能です。 実行されたQueryと同じ条件のQueryがキャッシュに存在すれば、実際の検索を実行せずにキャッシュの結果を返します。 キャッシュデータは、対象のEntityデータが1件でも更新されたタイミングでクリアされます。

Mapping Classの利用

Entityの検索処理として利用する EntityManager のload処理やQuery(EQL)を使ったsearchEntity処理の結果として、 標準では Entity インターフェースを実装した GenericEntity クラスが返ってきます。

org.iplass.mtp.entity.Entity
org.iplass.mtp.entity.GenericEntity

この GenericEntity クラスには共通Property項目に対するAccessorメソッドは提供されていますが、 各Entityで個別に追加したPropertyに対しては、 getValue("プロパティ名")setValue("プロパティ名", 値) としてデータを操作する必要があります。

そこで Entity インターフェースを実装したJavaクラスを作成し、各Entityのプロパティに対応したAccessorメソッドを定義できるようにするのが このMapping Classです。Javaクラスの名前を指定した場合、loadやsearchEntity処理の結果として、指定したJavaクラスのインスタンスが返ります。

AdminConsoleの Create Java Class を実行することで、 保存されているEntity定義に対応するJavaクラス( GenericEntity の継承クラス)のコードを生成、ダウンロードできます。

MappingするJavaクラスは、クラスパス上に配置する必要があります。Paas的な環境では利用できません。

EventListener

Entityに対する操作時に独自の処理を実行したい場合、操作イベントに対してListenerを設定することができます。 ListenerはJavaClassまたはGroovyScriptとして実装することができます。

Entityのイベント

イベントは EntityManager を経由したEntity操作時に発生します。 汎用画面でのEntity操作時にも、内部的に EntityManager を利用しているためイベントが発生します。

以下に EntityManager のメソッドとイベントの関連を示します。

EntityManager#method イベント 戻り値 説明

load

onLoad

void

ロード処理。

searchEntity

onLoad

void

検索処理(Entity形式)。1件ごとに呼び出される

validate

beforeValidate

void

検証処理。

insert

beforeValidate
beforeInsert
afterInsert

void
boolean
void

登録処理。

update

beforeValidate
beforeUpdate
afterUpdate

void
boolean
void

更新処理。

delete

beforeDelete
afterDelete
afterPurge

boolean
void
void

削除処理。afterPurge は、DeleteOptionの purge=true の場合のみ実行されます。

purge

afterPurge

void

ごみ箱から、削除処理。

restore

afterRestore

void

ごみ箱から、復活処理。

searchupdateAlldeleteAll 時はイベントは発生しません。

イベントの戻り値として boolean を返すものは、 false を返すと後続の処理を実行しません。

updatedelete 時など更新前のEntityが存在する状態で、各通知メソッドが呼び出される際の引き渡されるentityインスタンスは、 EntityManagerから各更新メソッド呼び出し時のEntityインスタンスがそのまま引き渡されたものです。 そのため、oid以外のプロパティ項目を保持していない場合もあることご注意ください。
Javaクラスによる実装

JavaクラスとしてEventListenerを実装する場合は、 EntityEventListener インターフェースを実装したクラスか、 空実装されている EntityEventAdapter クラスの継承クラスを作成します。

org.iplass.mtp.entity.EntityEventListener
org.iplass.mtp.entity.EntityEventAdapter
設定
設定項目 設定内容

class name

実装したクラスを指定します。

mapped by reference info is unnecessary for listener

update 処理の場合に、beforeUpdateイベントに渡される更新前Entityをloadする際に被参照Propertyを対象にしないかを指定します(LoadOptionを制御)。 詳細は、update時の更新前Entityについて を参照してください。

(例)EntityEventListenerを利用したEntityPropertyの更新
public class UserEntityEventListener extends EntityEventAdapter {

    /**
     * FIRST_NAME、LAST_NAMEからNameを設定する
     */
    @Override
    public void beforeValidate(Entity entity, EntityEventContext context) {
        String firstName = entity.getValue(User.FIRST_NAME);
        String lastName = entity.getValue(User.LAST_NAME);
        String name = firstName + lastName;
        entity.setName(name);   //ユーザ名をセットする
    }

    /**
     * 更新対象項目として名前が含まれていない場合、名前を追加する
     * (validate内で手動で変更しているので)
     */
    @Override
    public boolean beforeUpdate(Entity entity, EntityEventContext context) {
        //コンテキストから更新Optionを取得
        UpdateOption uo = (UpdateOption) context.getAttribute(EntityEventContext.UPDATE_OPTION);

        //更新対象項目として名前が含まれていない場合
        if (!uo.getUpdateProperties().contains(Entity.NAME)) {
            //コンテキストから変更前のEntityを取得
            Entity before = (Entity)context.getAttribute(EntityEventContext.BEFORE_UPDATE_ENTITY);

            //名前の変更チェック
            if (!before.getName().equals(entity.getName())) {
                uo.getUpdateProperties().add(Entity.NAME);  //異なる場合は更新項目として追加する
            }
        }
        return true;
    }
}

引数で渡されるEntityEventContextについては、EntityEventContext を参照してください。

Scriptによる実装

Script(GrovyScript)でEventListenerを実装する場合は、scriptとして2種類の定義方法があります。

  • Javaと同様に、EntityEventListenerをimplementsしたクラスを記述。

  • 対象のEventを指定し、実行されるScriptを記述。

設定
設定項目 設定内容

script

実行するScriptを指定します。Javaと同様の形式で記述する場合は、 Javaクラスによる実装 を参照してください。 クラス形式を指定した場合は、選択されたEventは無視されます。

events

実行するイベントをしています。 複数指定した場合は、各イベントで同じスクリプトが実行されます。

mapped by reference info is unnecessary for listener

update 処理の場合に、beforeUpdateイベントに渡される更新前Entityをloadする際に被参照Propertyを対象にしないかを指定します(LoadOptionを制御)。 詳細は、update時の更新前Entityについて を参照してください。

利用可能な変数

利用可能なバインド変数は以下のものです。

バインド変数 設定される値

entity

対象のEntityデータ。

context

EntityEventContext

event

発生したイベント種別。 org.iplass.mtp.entity.definition.listeners.EventType

user

ユーザ情報。

date

システム日時。

event には発生したイベントのタイプが設定されています。 Script形式の場合、1つのListenerで複数のEventに対する処理を実装できるので、Eventごとに処理を分けたい場合の判断として利用します。 このような場合、EventごとにそれぞれListener定義することもできます。

(例)typeによる処理の制御方法
import org.iplass.mtp.entity.definition.listeners.EventType;

if (event == EventType.BEFORE_VALIDATE) {

    

} else if (event == EventType.BEFORE_UPDATE) {

    

    return true;
}

boolean の戻り値が必要なイベントにに対して、戻り値を返していない場合は true として制御します。

EntityEventContext

更新系のイベントに対しては EntityEventContext という引数で、対象Entityに関する更新情報が渡されます。

org.iplass.mtp.entity.EntityEventContext

EntityEventContextには以下の情報が格納されています。

KEY 対象Event

VALIDATE_PROPERTIES

beforeValidate

バリデーション対象のProperty名のList(List<String>)。

  • validate時は引数で渡された validatePropertyList

  • insert時は null

  • update時はUpdateOptionの updateProperties

UPDATE_OPTION

update時

updateの引数で渡されたUpdateOption。

BEFORE_UPDATE_ENTITY

beforeUpdate

更新前のEntity(load)

DELETE_OPTION

delete時

deleteの引数で渡されたDeleteOption

KEYはEntityEventContextの変数名として定義されています。

イベント間での値の受け渡し方法

EntityEventContextはMap形式で値を保持することができるので、 イベント間で独自の値も受け渡すことができます。
EntityEventContext#getAttribute(KEY)EntityEventContext#setAttribute(KEY, value) で値を取得、設定してください。

EntityEventContextはEntityManagerのメソッド呼び出しの単位でインスタンスが生成されます。 Entityに対して複数イベントを登録している場合は、複数イベント間で共有されます。 ただし update 時に発生する beforeValidate イベントは、 beforeValidate 内のみで共有されます(後続の beforeUpdate 以降は別インスタンス)。

(例)update時のListener呼び出しイメージ
EntityManager#update()

    ////beforeValidateイベント実行
    EntityEventContext eeContext = new EntityEventContext();
    for (EntityEventListener listener: listeners) {
        listener.beforeValidate(entity, eeContext);
    }

    //validate処理実行

    //handleBeforeUpdateイベント実行
    //検証時のEntityEventContextは引き継がない
    EntityEventContext eeContext2 = new EntityEventContext();
    for (EntityEventListener listener: listeners) {
        if (!listener.handleBeforeUpdate(entity, eeContext2)) {
            return;
        }
    }

    //実際のupdate処理実行

    //handleAfterUpdateイベント実行
    for (EntityEventListener listener: listeners) {
        listener.handleAfterUpdate(entity, eeContext2);
    }
update時の更新前Entityについて

update処理時にはEntityEventContextに更新前のEntity情報がloadされてセットされます。 このload時に被参照Propertyを取得しないかを設定します。

true の場合、更新前Entityのload処理で被参照Propertyを除外します。 (load時のLoadOptionを withMappedByReference=false にして処理します)

被参照Propertyは更新対象のPropertyではないため、Listener内部の処理で参照する必要がないのであれば、除外することで処理を高速化できます。

データの多言語化

Entityデータ自体に対する多言語設定が行えます。多言語設定は Data Localization で指定します。
詳細については、 データの多言語化を参照してください。

3. Entity Property

データ型、Validatorについての詳細を記載します。
インデックス指定、データの暗号化については、 高度な設定で説明します。

3.1. データ型

データ型を一覧で参照したい場合は、 データ型の指定 を参照してください。

基本型の注意点

String型の文字数制約

String型はバックエンドとして利用するRDBの定義によって、格納できる最大文字数が制限されます。 標準の設定の場合、Oracleは varchar2(4000) 、MySQLは TEXT です。 また文字コードは UTF-8 なので、全角2000文字でもありません。 もしこの制限に引っ掛かる文字列型を扱う可能性がある場合は LongText を利用してください。 LongTextにした場合BLOBとして保存されるため、検索時に制限があります (LongText型の検索)。

Integer型のJavaクラス

Entityの検索や更新処理で、Integer型を操作する際は、Longクラスとして値を取得・設定します(Integerクラスではありません)。

Decimal型の丸めモード

Decimal型の場合、データの保存時に丸めた値で保存されます。 「丸めモード」には以下の種類があります(java.math.RoundMode)。

モード(java.math.RoundMode) 説明

UP

0から離れるようにする(正数切り上げ/負数切り上げ)

DOWN

0に近づける(正数切り下げ/負数切り下げ)

CEILING

正の無限大に近づける(正数切り上げ/負数切り下げ)

FLOOR

負の無限大に近づける(正数切り下げ/負数切り上げ)

HALF_UP

四捨五入

HALF_DOWN

五捨六入

HALF_EVEN

銀行型丸め

Select

Select型は選択ボックスの選択値ように、登録できる値を「値とラベル」のセットとして予め定義できるPropertyです。 値セットの定義方法は、複数のEntityで共有可能なSelectValueメタデータとして定義する方法(Global Value)と、 それぞれのPropertyごとに個別に定義する方法(Local Value)の2種類があります。

設定

次の項目を設定可能です。

設定項目 設定内容

Global Value

SelectValueメタデータを指定します。

Local Value

このPropertyに特化した選択可能項目の {値、ラベル} リストを指定します。 値はString型のため、数値、Boolean値として扱う場合は注意してください。

Global ValueLocal Value を共に設定した場合は、 Local Value が優先されます (Global Value がクリアされた状態で保存されます)。
Global Value を利用する場合は、 Local Value を全て削除して保存してください。
操作方法

Entityの検索や更新処理で、Select型Propertyを操作する際は、以下のクラスを利用します。

マッピングクラス
org.iplass.mtp.entity.SelectValue
(例)更新処理
EntityManager em = ManagerLocator.manager(EntityManager.class);

Entity entity = new GenericEntity("samples.Sample");
entity.setName("xxxxx");

//SelectValueの生成
SelectValue selectValue = new SelectValue("01");    //登録するためだけであれば、valueのみでOK

//SelectValueをPropertyにセット
entity.setValue("select", selectValue);

//登録
String oid = em.insert(entity);
(例)参照処理
EntityManager em = ManagerLocator.manager(EntityManager.class);

//検索(load)
String oid = "00001";
Long version  = 0;
Entity entity = em.load(oid, version, "samples.Sample");

//SelectValueの取得
//取得したEntityのPropertyにはSelectValueが設定されている
SelectValue selectValue = entity.getValue("select");

System.out.println(selectValue.getValue() + "=" + selectValue.getDisplayName());
Select型のソート順

Select型に対するソートは、定義での並び順が適用されます。

(例)ソート順の設定
value displayName

98

未開始

01

開始中

02

終了

00

エラー

99

キャンセル

上表の順番で定義した場合、value 値でソートせず、定義した順番「未開始 ~ キャンセル」でデータがソートされます。

AutoNumber

AutoNumber型はEntityデータの登録時に自動的に値を採番するPropertyです。 日付やユーザ情報などを利用した書式設定をすることが可能です。 データ登録時以外値を変更できません(読み取り専用)。

設定

次の項目を設定可能です。

設定項目 設定内容

開始値

採番を開始する番号を指定します。

既にデータが登録済みの状態でこの値を変更しても、カウンタはリセットされません。 カウンタの値をリセットしたい場合は、リセット機能を利用してください。

固定桁数

桁数を固定したい場合に指定します。 設定された桁数分、ゼロパディングした値が返されます。0を設定した場合はゼロパディングされません。

採番値が固定桁数を超えた場合は、そのまま採番値が利用されます。

採番ルール

採番する際の飛び番に関するルールを指定します。

ALLOW_SKIPPING 飛び番を許容する

Entityの登録エラー時など、採番値の連続性を保証しません。 Entityの登録処理とは別トランザクションで採番するため、登録時の並列実効性は高まります。

STRICT_SEQUENCE 飛び番を許容しない

Entityの登録エラー時など、採番値の連続性を保証させます。 Entityの登録処理と同一トランザクションで採番するため、登録処理はシーケンシャルな処理になります。

書式

登録する値をGroovyTemplate形式で書式指定することができます。

書式設定

書式で利用可能なバインド変数は以下のものです。

バインド変数 設定される値

nextVal()

次の採番された番号

yyyy

MM

dd

HH

時、24h表記

mm

ss

date

java.sql.Timestampのインスタンス

user

登録者User情報。 ${user.xxxx} としてUserエンティティのプロパティを指定できます。

未指定の場合、採番された値が設定されます( ${nextVal() と同等)。

(例)書式設定
${yyyy}-${MM}-${dd}-${nextVal()}

結果は 2018-01-01-0000001001 のようになります。

カウンタのリセット

Entityデータを他の環境からエクスポート/インポートで移行した場合は、内部で保持しているカウンタ値をリセットする必要があります。

AutoNumber型自体はUnique制約とはなっていないため、移行後の環境で重複した値が採番されてもエラーにはなりません。 AutoNumber型Propertyに対して、 OIDUnique Index を指定している場合は、重複が発生したタイミングでエラーになります。

現在値の取得

AdminConsole上は Current ボタンをクリックすることで、現在のカウンタの値を表示します。
ソース上から取得したい場合は

EntityDefinitionManager#getAutoNumberCurrentValue(String definitionName, String propertyName);

を利用します。

値のリセット

AdminConsole上は Reset ボタンをクリックすることで、「開始値」に設定された値でカウンタをリセットします。 リセットする場合は、「開始値」を変更して Reset を実行してください。
ソース上からリセットしたい場合は

EntityDefinitionManager#resetAutoNumberCounter(String definitionName, String propertyName, long startsWith);

を利用します。

Expression

Expression型は四則演算やCase文、スカラーサブクエリ(結果が1つになるクエリ)など、 Query機能の Value Expression として指定可能な式を設定することができるPropertyです。

設定

次の項目を設定可能です。

設定項目 設定内容

Result Type

式の結果として返す値のProperty型を指定します。未指定の場合はString型です。

指定可能な式は、Queryの Value Expression として有効な式になります。

(例)四則演算以外の例
case
  when integer1 = 50 then '△△△'
  when integer1 = 80 then '○○○'
  else '×××'
end
integer1 /  (select sum(integer1) from samples.Sample)
case
  when 50 = (select integer1 + integer2 from sample.Sample on .this=this)  then '△△△' (1)
  when 80 = (select integer1 + integer2 from sample.Sample on .this=this)  then '○○○'
  else '×××'
end
1 thisはoidとほぼ同じ意味です。逆に .oid=oid という表現は不可となっています。
四則演算で参照するプロパティの多重度が1以外の場合、正確な計算が実行されません。 多重度が1のプロパティに対してのみ利用してください。

Binary

Binary型は画像や文書ファイルなどのバイナリデータを扱うためのPropertyです。

設定

Binary型に特化した設定項目はありません。

操作方法

Entityの検索や更新処理で、Binary型Propertyを操作する際は、以下のクラスを利用します。

マッピングクラス
org.iplass.mtp.entity.BinaryReference
(例)更新処理

バイナリファイルをEntityに登録する場合は、BinaryReferenceを生成したうえでEntityにセットします。

EntityManager em = ManagerLocator.manager(EntityManager.class);

Entity entity = new GenericEntity("samples.Sample");
entity.setName("xxxxx");

//BinaryReferenceの生成
BinaryReference bin = null;
try (FileInputStream is = new FileInputStream(file)){

    //EntityManagerを利用してBinaryReferenceを生成
    bin = em.createBinaryReference(file.getName(), "image/gif", is);
} catch (FileNotFoundException e) {
    throw new ApplicationException("ファイルが取得できません。", e);
}

//生成したBinaryReferenceをEntityにセット
entity.setValue("binary", bin);

//登録
String oid = em.insert(entity);
(例)参照処理

Entityを検索したタイミングではBinaryReferenceとしてPropertyに値がセットされています。 BinaryReferenceにはバイナリ自体は格納されていません。 BinaryReferenceに紐づくバイナリを取得したい場合はEntityManagerを利用して取得します。

EntityManager em = ManagerLocator.manager(EntityManager.class);

//検索
String oid = "00001";
long version  = 0;
Entity entity = em.load(oid, version, "samples.Sample");

//BinaryReferenceの取得
//取得したEntityのPropertyにはBinaryReferenceが格納されている
BinaryReference bin = entity.getValue("binary");

//System.out.println(bin.getLobId()
//        + ", name=" + bin.getName() + ", type=" + bin.getType());

//バイナリの取得(Fileとして取得する場合)
//(一時的に)保存したいdirは別途指定すること
File file = new File(dir, bin.getName());

//EntityManagerを利用してバイナリのInputStreamを取得
try (FileOutputStream fos = new FileOutputStream(file);
    InputStream is = em.getInputStream(bin);
) {
    //この例では「org.apache.commons.io.IOUtils」を利用
    IOUtils.copy(is, fos);
} catch (IOException e) {
    throw new ApplicationException("ファイルが取得できません。", e);
}
(例)Web画面での参照処理

Webの画面上に表示させるような場合はimgタグを利用します。 imgタグで指定するActionを別途作成し、Resultのタイプとして Stream を指定します。 RequestContextに対してResultの StreamAttributeName に指定したAttributeにBinaryReferenceをセットすることで、 画像を表示することが可能になります。 (ResultのStreamタイプを利用することで、自身でStreamを取得する処理が不要になります)

この一連の実装は以下を参考にしてください。

  • Template例
    jsp/gem/generic/editor/binary/BinaryPropertyEditor_View.jsp

  • Action例
    gem/binary/download

    このActionクラスはJava Commandクラスに指定されたアノテーションから作成されたものです。 JavaCommandクラスは以下です。

    org.iplass.gem.command.binary.DownloadCommand

ユーティリティ機能

Binaryに対する最大ファイルサイズやMagicByteチェックの実行可否、ウィルススキャンの実行可否を service-config.xmlのWebFrontendServiceで定義できます。
設定内容の詳細は、WebFrontendServiceを参照してください。

またBinaryの永続化方法として、RDB(BLOBデータ)として保存するか、サーバ上にファイルとして保存するかを service-config.xmlのLobStoreServiceで定義できます。
設定内容の詳細は、LobStoreServiceを参照してください。

LongText

LongText型はString型ではサイズが足りない場合(String型の文字数制限)などの テキストデータを扱うためのPropertyです。 内部的にBinary型同様Lobまたはファイルとして管理しているため、検索時に制限があります。

設定

LongText型に特化した設定項目はありません。

LongTextについては、EQLを利用した検索条件の指定は完全にはできません。 LongTextに対する完全な検索機能を提供する場合は、 全文検索 機能を利用します。 このため汎用検索画面においても検索条件としてLongText型のPropertyは表示されません。

ただし、service-config.xmlにてPropertyServiceの設定を行うことで、先頭から一定文字数分の検索が可能になります。
設定内容の詳細は、PropertyServiceを参照してください。

Reference

Reference型はEntity間の参照関係を定義するためのPropertyです。 RDBではSQLのJoinを利用してTable間の結合を定義しますが、 iPLAssのEntityではプロパティとして参照関係を定義する事で同等の機能を実現します。

Referenceの構造

オブジェクトID (oid) で少し触れましたが、Reference型は参照しているEntityの oidversion を保持します。

entity property oid

この参照関係を利用することで、参照元のEntityを検索する際に参照先のEntity情報を取得できます。

EQLではFrom句でDBのJoinのように複数Entityを結合することができません。 RDBのJoinと同様の機能をEQLで実現する場合には、対象Entityに対するReferenceプロパティを作成します。 作成したReferenceプロパティをSelect句に指定したり、Where条件で絞り込むことができます。
(Expression型など、Select句に対してスカラーサブクエリとして他Entityを参照することは可能です)
「参照」(「順参照」)と「被参照」

Reference型には、指定した参照Entityに対する「順参照」と「被参照」という概念があります。

「順参照」は、当Entity(Referenceを定義しているEntity)が、参照Entityの、どのデータを利用するかを保持するための、当Entityの更新対象属性として扱う目的で定義します。 ですので当Entityデータを更新する際には、Entityに対してReference値を指定して更新する必要があります。

「被参照」は、当Entityが、参照Entityの、どのデータによって参照されているかを把握する目的で定義します、いわば逆引きです。 ですので当Entityを更新する際に、「被参照」として定義したPropertyに対して値を設定しても、参照関係が作成されるわけではありません。 「被参照」Propertyを定義することで、参照される側(この場合「被参照」Propertyを定義した当Entity)から参照Entityデータを取得することが可能になります。

「順参照」か「被参照」かは「被参照Property」を指定するかで決まります。

「被参照Property」が未指定の場合

「参照Entity」に対して「順参照」(「参照Entity」を参照する)

「被参照Property」を指定した場合

「参照Entity」に対して「被参照」(「参照Entity」から「被参照Property」名で参照される)

設定

次の項目を設定可能です。

設定項目 設定内容

参照Entity

参照関係にあるEntityを指定します。

参照関係

参照するEntityとの参照関係を指定します。
詳細は参照関係の指定を参照してください。

被参照Property、
操作履歴を被参照側のEntityに記録

このEntityが参照関係にあるEntityから参照される側(被参照)の場合に指定します。
詳細は被参照Propertyの指定を参照してください。

参照先のバージョン

参照先のデータとして取得するバージョンのポリシーを指定します。
詳細は参照先バージョンの指定を参照してください。

ソート条件

このPropertyの多重度が1以外の場合に、どの順番で取得するかを指定します。 参照先EntityのPropertyに対して、昇順/降順を指定します。

参照関係の指定

参照するEntityとの関係を指定します。 指定されたタイプによって、EntityデータCRUD時の動作が変わります。

ASSOCIATION 通常の参照

参照先Entityと特別な関係を持ちません。 このEntityデータが削除された場合、参照先のEntityデータは削除されません。

COMPOSITION 親子関係

参照先Entityを子として参照します。 このEntityデータが削除された場合、参照先のEntityデータも削除されます。

被参照Propertyの指定

iPLAssでは、参照する側がReference型のプロパティで参照先のEntityを指定することで、参照関係を構築します。 この状態だと、参照される側がどのEntityから参照されているかを把握することができません。 参照される側が参照しているEntityを把握したい場合に「被参照Property」を利用します。 「被参照Property」をSelect句などで利用することで、参照している側の情報を取得できます。

「被参照Property」に指定可能なPropertyは、「参照Entity」で指定したEntityのProperty定義に存在する 当Entityが参照先になっているReferencePropertyです。
操作ログを被参照側のEntityに記録

参照情報の実態は参照する側のEntityにあります。 このため参照先EntityのCRUD操作に関する操作ログは、参照する側のEntityに保存されます。 参照される側で参照先の操作ログを把握したい場合は、「操作ログを被参照側のEntityに記録」を true に設定します。

この設定が有効になるのは、「被参照Property」を指定した場合です。(未指定の場合、有効になりません)

この機能の主な利用用途としては 参照関係を定義する際の考慮があります。

参照先バージョンの指定

参照先のEntityがバージョン管理されている場合に、参照データとして取得するバージョンのポリシーを指定します。

参照先のEntityがバージョン管理していない場合は、バージョンは0で上書きされるため、 ここで指定した値は意味がありません。
CURRENT_BASE 最新バージョンを取得

参照先のEntityの最新バージョン(同一oid)のデータを取得します。

RECORD_BASE 保存時のバージョンを取得

データが保存された時の参照先のEntityのバージョン(同一oid)のデータを取得します。 保存後に参照先のEntityデータが更新されても、参照する情報は保存時の状態のまま変わりません。

AS_OF_EXPRESSION_BASE 特定のバージョンを取得

基準値に指定した条件を満たす参照先のEntityのバージョン(同一oid)のデータを取得します。

基準値の指定

「特定のバージョンを取得」を指定した場合、バージョンを特定するための基準値を指定します。 基準値は参照先Entityのバージョン管理方式により設定する値が異なります。

NUMBER BASE の場合

参照元Entityの数値Property名(versionなども指定可能)、または直接数値のリテラルを指定します。

TIME BASE の場合

参照元Entityの日時Property名(createDateなども指定可能)、または直接日時のリテラルを指定します。

操作方法

Entityの検索や更新処理で、Reference型Propertyを操作する際は、以下のクラスを利用します。

マッピングクラス
org.iplass.mtp.entity.Entity
(例)更新処理
EntityManager em = ManagerLocator.manager(EntityManager.class);

Entity entity = new GenericEntity("samples.Sample");
entity.setName("xxxxx");

//Reference用Entityの生成(登録済みであれば検索して取得してもOK)
//Entityの実装クラスGenericEntityを利用
Entity refEntity = new GenericEntity("samples.RefEntity");
refEntity.setOid("xxxxx");  //oidとversionをセットしておけばOK
refEntity.setVersion(0);
//生成したReference用EntityをEntityにセット
entity.setValue("reference", refEntity);

//MultipleのReference用Entityの生成(配列でセット)
List<Entity> multiReferences = new ArrayList<Entity>;
Entity refsEntity1 = new GenericEntity("samples.RefEntity");
refsEntity1.setOid("xxxxx");
refsEntity1.setVersion(0);
multiReferences.add(refsEntity1);
Entity refsEntity2 = new GenericEntity("samples.RefEntity");
refsEntity2.setOid("xxxxx");
refsEntity2.setVersion(0);
multiReferences.add(refsEntity2);

//参照が1件の場合でもEntityの配列でセット
entity.setValue("multiReference", multiReferences.toArray(new Entity[0]));

//登録
String oid = em.insert(entity);
(例)参照処理

多重度が1以外の場合の検索時は、loadとsearchEntityで挙動が異なります。
load時はEntityの配列で返ってきますが、searchEntity時はSearchResultの結果として、 紐付いているEntityの件数分別々のEntityレコードとして返ってきます。
(SearchResultで返ってくるListが、Referenceとして紐づいているEntityの件数分になる)

EntityManager em = ManagerLocator.manager(EntityManager.class);

//検索(load)
String oid = "00001";
long version  = 0;
Entity entity = em.load(oid, version, "samples.Sample");

//Reference Entityの取得
Entity refEntity = entity.getValue("reference");
Entity[] multiRefEntity = entity.getValue("multiReference");

//検索(searchEntity)
Query query = new Query()
    .select("multiReference.oid", "multiReference.name")
    .from("samples.Sample")
    .where(new Equals("oid", oid));

SearchResult<Entity> result = em.searchEntity(query);

System.out.println("result size = " + result.getList().size());

result.getList().forEach(entity2 -> {
    Entity multiRefEntity2 = entity2.getValue("multiReference");
});

このsearchEntityの仕様により、汎用検索画面ではデフォルトとして多重度が1以外のReference Propertyは出力対象としていません。 汎用検索画面の設定で多重度が1以外のReference項目を検索結果として表示するように定義した場合は、同一のoidで複数のレコードが表示されます。

参照関係を定義する際の考慮

Entityに対して「順参照」用のReferenceプロパティを定義すると、当Entityデータの更新時に、 参照しているEntityデータ件数分の属性(oid、versionのセット)を更新しなおします。 参照先Entityの件数が少ない場合は特にレスポンス的な問題は発生しませんが、参照先Entityが大量の場合はレスポンスにも影響があります。

例えば、親子関係があるEntityで子側のEntityが大量の件数となるような場合は、子から親に「順参照」の定義をすることも1つの方法です。

entity reference mappedby

この場合、親Entityの属性は親Entityの属性のみ更新するような画面を作成して対応する必要があります。 子Entityからみると親Entityに対する参照関係は1なので、更新時のレスポンスも問題ありません。

親Entityで排他制御する場合

上のように、子Entityから親Entityに対して「順参照」を定義した場合、子Entityを更新しても親Entityは更新されません。 複数の子Entityを同時に更新する可能性がある場合に、親Entity側で排他制御を行いたい時はこのままだと制御できません。

これを回避するには子Entityの更新時に親Entityを強制的に更新させます。 更新処理は子EntityのEventListenerか独自に作成したCommandで行います。 EventListenerでの実装がActionやCommandなどをカスタマイズする必要がないので容易ですが、 1処理の中で複数の子Entityを一括で親Entityに紐づけるような場合は、EventListenerではなくCommandで実装してください。
(EventListenerで実装した場合、子Entityの件数分、親Entityを更新する処理が実行されることになります)

(例)EntityEventListenerで参照Entityを強制的に更新する(Script形式)

EventListenerの beforeInsert beforeUpdate beforeDelete で以下のScriptを実行します。

import org.iplass.mtp.ManagerLocator;
import org.iplass.mtp.entity.EntityManager;
import org.iplass.mtp.entity.Entity;
import org.iplass.mtp.entity.UpdateOption;

EntityManager em = ManagerLocator.manager(EntityManager.class);

//parent取得(親Entityをparentプロパティとして参照している場合)
Entity parent = entity.getValue("parent");  //対象EntityはentityとしてListenerにバインドされている

//System.out.println("parent oid=" + parent.getOid() + ",version=" + parent.getVersion());

//更新Optionを生成
UpdateOption option = new UpdateOption();
//更新Propertyを設定(例:更新日)。なにかを指定しないとNullPointerが発生する
option.setUpdateProperties(Entity.UPDATE_DATE);

//強制更新設定(変更がなくても更新)
option.setForceUpdate(true);

//parentの更新
em.update(parent, option);
被参照時の操作ログについて

子から親に「順参照」を定義する場合、ユーザがデータを操作するための詳細画面としては 子Entityの詳細画面をメインで利用するという使い方が多いと思います。 このとき詳細画面上に操作ログを表示した場合、親Entityの操作ログは子Entityの操作ログ上には表示されません。

そこで、親Entityの操作ログを子Entityの操作ログにも出力する機能が「操作履歴を被参照側のEntityに記録」チェックです。 親EntityのPropertyとして、子Entityに対する「被参照」を定義して、「操作履歴を被参照側のEntityに記録」をチェックすることで、親Entityの操作ログが子Entityの操作ログに記録されるようになり、子Entityの詳細画面から親Entityの操作ログが参照可能になります。

3.2. Validator

提供Validator

標準で提供しているPropertyのValidatorです。

Type 説明 Java型

NotNull

入力の有無を検証します。Propertyで設定する Required と連動します。

*

Length

文字列の長さを検証します。

String

Range

数値の範囲を検証します。

Number

Regex

正規表現を利用して値を検証します。

String、Number

BinarySize

バイナリのサイズを検証します。

BinaryReference

BinaryType

バイナリのタイプを検証します。

BinaryReference

Scripting

GroovyScript書式で検証ロジックを実装します。

*

対応するJava型以外のProperty型に対してValidatorを指定した場合、 検証時に EntityRuntimeException が発生します。

Not Null 以外は、値が null の場合はチェックされません(true)。

NotNull

入力の有無を検証します。 AdminConsole上の編集画面では、Required の設定と連動して追加、削除されます。

以下の場合にエラーとなります。

  • 値が null

  • String型の場合、値が空

  • 配列の場合、サイズが0

対応するJava型

すべての型で利用可能です。

設定

NotNullに特化した設定項目はありません。

Length

文字列の長さを検証します。

以下の場合にエラーとなります。

  • checkBytesfalse の場合、値の長さが min 未満

  • checkBytesfalse の場合、値の長さが max より上

  • checkBytestrue の場合、値のバイト数が min 未満

  • checkBytestrue の場合、値のバイト数が max より上

対応するJava型

String型で利用可能です。

設定
設定項目 設定内容

min

最小値を指定します。

max

最大値を指定します。

checkBytes

バイト数でチェックする場合、trueにします。

Range

数値の範囲を検証します。

以下の場合にエラーとなります。

  • grater than min valuetrue の場合、値が min 以下

  • grater than min valuefalse の場合、値が min 未満

  • less than max valuetrue の場合、値が max 以上

  • less than max valuefalse の場合、値が max より上

対応するJava型

Number型で利用可能です。 Number型に対応するPropertyタイプは Integer Float Decimal です。

設定
設定項目 設定内容

min

最小値を指定します。

grater than min value

最小値を含まない場合、trueにします。

max

最大値を指定します。

less than max value

最大値を含まない場合、trueにします。

Regex

正規表現を利用して値を検証します。

以下の場合にエラーとなります。

  • 値が正規表現にマッチしない場合

(例)半角英数字
[0-9a-zA-Z]+
(例)ひらがな
[\\u3040-\\u309F]+
対応するJava型

String型、Number型で利用可能です。 Number型の場合、 toString() した値に対して検証します。 Number型に対応するPropertyタイプは Integer Float Decimal です。

設定
設定項目 設定内容

pattern

正規表現式を指定します。

BinarySize

バイナリのサイズを検証します。

以下の場合にエラーとなります。

  • バイナリのサイズが min 未満

  • バイナリのサイズが max より上

対応するJava型

BinaryReference型で利用可能です。

設定
設定項目 設定内容

min

最小値(byte)を指定します。

max

最大値(byte)を指定します。

BinaryType

バイナリのタイプを検証します。

以下の場合にエラーとなります。

  • バイナリのタイプが正規表現にマッチしない場合

対応するJava型

BinaryReference型で利用可能です。

設定
設定項目 設定内容

acceptMimeTypesPattern

mimeタイプを指定します(正規表現)。

Scripting

GroovyScript書式で検証ロジックを実装します。

以下の場合にエラーとなります。

  • scriptの戻り値が false

対応するJava型

すべての型で利用可能です。

設定
設定項目 設定内容

script

GroovyScriptを指定します。 戻り値としてBoolean(true:OK、false:NG)を返すように実装する必要があります。

asArray

多重度が1ではない場合に、value変数に配列をバインドするか、 配列分ループしてvalueにバインドするかを指定します。
true の場合、配列をバインドします。

利用可能な変数

利用可能なバインド変数は以下のものです。

バインド変数 設定される値

entity

対象のEntityデータ。

propertyName

プロパティ名

value

プロパティ値。各Propertyに対応するJava型が格納されています。

(例)ReferencePropertyに対する存在チェック
import org.iplass.mtp.entity.EntityManager;
import org.iplass.mtp.ManagerLocator;
import org.iplass.mtp.entity.Entity;

//値が未設定の場合はOK
if (value == null) {
    return true;
}

EntityManager em = ManagerLocator.manager(EntityManager.class);

//対象のReferenceProperty
Entity refValue = (Entity)value;    //valueにProperty値が入っている(バインド)

//存在チェック
Entity refData = em.load(refValue.getOid(), refValue.getVersion(), refValue.getDefinitionName());

if (refData == null) {
    return false;   //存在しないのでNG
} else {
    return true;    //存在するのでOK
}

メッセージの設定

Validateエラー時のメッセージを指定します。

メッセージの指定は、直接文字列を指定する方法とメタデータのMessageを利用する方法の2種類あります。

設定
設定項目 設定内容

Message(Direct)

メッセージを直接指定します。

Message Category
Message Id

メタデータのMessageを選択します。メタデータのMessageを利用することで、メッセージを共有・管理することができます。
メッセージの詳細はメッセージの多言語設定を参照してください。

Code

メッセージコードを指定します。この項目は任意です。 バッチ処理やWebサービスなどの独自処理内でコードをレスポンスとして返したい場合などに利用します。

Message(Direct)Message Id が両方指定されている場合は、 Message(Direct) が優先されます。
利用可能な変数

メッセージ文言として、プロパティ名や値をバインド書式(${xxx})で参照することができます (DirectでもメタデータのMessageでも参照できます)。

利用可能なバインド変数は以下のものです。

バインド変数 設定される値

name

プロパティ名

entityName

エンティティ名

min

Length、Range、BinarySizeの min に設定した値

max

Length、Range、BinarySizeの max に設定した値

min、maxについては、他のタイプの場合はバインドされていないため ${min}${max} とそのまま出力されます。

Validationの操作方法

汎用画面を利用してEntityの更新処理を行う場合は、汎用画面側でValidatorエラーのハンドリングは自動的に行われます。 ここではバッチやWebサービスを利用した独自のEntity更新処理を実装する場合のValidatorエラーハンドリングの方法を説明します。

Entity更新時の操作方法

EntityManagerを利用した insertupdate 時には 自動的にValidatorチェックが実行されます。 エラーが発生した場合は EntityValidationException がthrowされます。 この EntityValidationException 内に、エラーとなった ValidateError が格納されています。

import org.iplass.mtp.entity.EntityValidationException;
import org.iplass.mtp.entity.ValidateError;

EntityManager em = ManagerLocator.manager(EntityManager.class);

try {
    //登録の場合
    em.insert(entity);

    //更新の場合
    UpdateOption option = new UpdateOption();
    option.setUpdateProperties();
    em.update(entity, option);
} catch (EntityValidationException e) { //EntityValidationExceptionがthrow
    //ValidateErrorの取得
    ValidateError[] errors = e.getValidateResult();

    Arrays.stream(errors).forEach(error -> {
        //対象Property名
        System.out.println("propertyName =" + error.getPropertyName());

        //Listなのは同一Propertyに対する複数Validator用
        //エラーコードとエラーメッセージは同じサイズ
        //エラーコードが未指定の場合は""
        List<String> errorCodes = error.getErrorCodes();
        List<String> errorMessages = error.getErrorMessages();

        for (int i = 0; i < errorMessages.size(); i++) {
            System.out.println(
                error.getPropertyName()
                + " error = [" + errorCodes.get(i) + "]"
                + errorMessages.get(i));
        }
    });
}

Entityのデータ移行時など、データ更新時にValidatorチェックを行いたくない場合は、 InsertOptionUpdateOption を利用します。

手動でのValidatorチェック

EntityManager#validate を利用することで手動でValidatorチェックを実行できます。 この場合、チェック結果は「ValidateResult」として返ってきます。

import org.iplass.mtp.entity.ValidateResult;

EntityManager em = ManagerLocator.manager(EntityManager.class);

ValidateResult result = em.validate(entity);    //Validatorチェックの実行

if (result.hasError()) {
    //ValidateErrorの取得
    ValidateError[] errors = result.getError();
    
}

3.3. 高度な設定

インデックス設定

検索処理の高速化のためのRDBのIndexのような機能として、Propertyに対するIndexを設定できます。 Indexを設定すると検索時は高速化されますが、データ更新時のパフォーマンスに影響が発生するため、設定時は考慮が必要です。

Index設定には以下の種類があります。

タイプ 説明

NON_INDEXED

Indexを利用しません。

UNIQUE

一意のIndexを作成します。重複データを登録しようとした場合、エラーになります。

UNIQUE_WITHOUT_NULL

一意のIndexを作成します。ただし null の場合は重複を許可します。

NON_UNIQUE

一意ではないIndexを作成します。

Indexの制限事項

Indexに関する注意点、制約事項を説明します。

  • Property単位でのみ設定が可能です。
    複合Index(複数Propertyの結合)はサポートしていません。

  • UNIQUEUNIQUE_WITHOUT_NULL はEntity単位での値の重複をチェックします。
    Entity更新時に重複がある場合はエラーとなります。

  • 多重度が1以外のPropertyはIndexを指定できません。

  • Expression Binary LongText Refernce はIndexを指定できません。

  • Entityのバージョン管理を行う場合、 UNIQUEUNIQUE_WITHOUT_NULL として指定したPropertyは変更不可になります。 Entity定義の保存時に自動的に CanEdit = false として登録されます。

  • 既に登録済みのEntityデータが存在する場合にIndex Typeを変更すると、Entity定義の保存時にIndexの再作成処理が実行されます。 大量データが登録されている場合などは考慮が必要です。
    UNIQUEUNIQUE_WITHOUT_NULL の場合、一意でないデータが存在すると再作成時にエラーが発生し、定義自体が保存できません。

Unique Indexに対するMySQL上での制約について

UNIQUEUNIQUE_WITHOUT_NULL は、バックエンドでRDBのUnique Indexを利用しています。 MySQLではUnique Indexのbyte制限として767byteまでしか格納することができません。 このため、 UNIQUEUNIQUE_WITHOUT_NULL を指定した場合は、 DBの文字コードによって格納できる最大の値長に制約が発生します (データ登録時にエラーになります)。

データ暗号化

当該カラムのデータを暗号化してDBに格納したい場合に設定します。 DB/OSなどが持つ透過的暗号化機能を利用することを推奨しますが、ライセンス費用の問題やインフラ環境で暗号化サポートしてない場合など、それらの利用が難しい場合、当該機能を利用してください。 なお、当該機能を利用する場合、アプリケーションの機能的な制限が入ります。

暗号化のモードとして以下の2つ用意をしています。

モード インデックス化 暗号化強度 検索条件指定 備考

ECBモード

可能

弱い

完全一致のみサポート

集計はカウントのみ可能

CBCモード

不可

強い

不可

集計はカウントのみ可能

暗号化した場合の制限事項

暗号化に関する注意点、制約事項を説明します。

  • 検索は ECBモード の場合かつ完全一致検索のみ可能。関数の適用、ソートは不可。

  • 未暗号化、暗号化を切り替えた場合、プロパティの実データは引き継がれない。

  • ECBモードCBCモード を切り替えた場合、プロパティの実データは引き継がれない。

  • 暗号化しているプロパティの型を変更した場合、以下のパターン以外は実データは引き継がれない。
    Select型、 AutoNumber型、 Boolean型、 Integer型、 Float型、 Decimal型、 Date型、 DateTime型、 Time型 → String型
    Boolean型 → Select型
    暗号化していない場合と比較し、数値型間の相互変換、日付型間の相互変換、String型からLongText型への変換がされない。

  • Decimal型のscaleを変更した場合、自動的にデータの洗い替えは行われない。

service-config.xmlの設定変更

プロパティ単位の暗号化を利用する場合、PropertyEncryptionServiceにて、暗号化アルゴリズムなどの設定を行ってください。

暗号化アルゴリズムは複数定義することができます。 新規に保存される値は一番後に定義された暗号化アルゴリズムを利用して暗号化されます。

4. SelectValue

SelectValueは、Entityの Select 型Propertyの選択値を定義するためのものです。

SelectValueメタデータとして定義することで、複数のEntity間や複数のProperty間で選択可能な値のセットを使いまわすことができます。

4.1. SelectValueの作成

SelectValueアイコンを右クリックして「SelectValueを作成する」を選択してください。

4.2. 設定

設定項目 設定内容

value

値を指定します。String型のため、数値、Boolean値として扱う場合は注意してください。

display name

表示名(ラベル)を指定します。

5. StorageSpace

5.1. StorageSpaceについて

iPLAssでは、AdminConsoleなどを利用して、動的にEntityの定義を変更することが可能です。
これら動的に定義されたEntityのデータは、StoreServiceに定義されたStorageSpaceに格納されます。

標準のStorage Space設定

iPLAssの初期設定では3つのStorageSpaceが定義されています。

<!-- Entity Store Settings -->
<service>
  <interface>org.iplass.mtp.impl.datastore.StoreService</interface>

  <property name="dataStore" class="org.iplass.mtp.impl.datastore.grdb.GRdbDataStore">
    <property name="storageSpace">
      <property name="storageSpaceName" value="default" />
      ・・・・・
    </property>
    <property name="storageSpace">
      <property name="storageSpaceName" value="mtp" />
      <property name="tableNamePostfix" value="MTP" />
      ・・・・・
    </property>
    <property name="storageSpace">
      <property name="storageSpaceName" value="user" />
      <property name="tableNamePostfix" value="USER" />
      ・・・・・
    </property>
  </property>
</service>

どのStorageSpaceにデータを保存するかはEntity単位で指定します。 iPLAssの標準Entityであるmtp配下のEntityは以下のStorageSpaceに保存されるようになっています。

Entity Storage Space

mtp.auth.User

user

それ以外のEntity(mtp.*.)

mtp

標準で定義されているStorageSpace usermtp については、基盤としての利用を前提にしているため、 アプリで作成するEntityでは利用しないでください。

アプリで作成するEntityについては、通常StorageSpaceが未指定のため default というStorageSpaceに保存されます。

entity storagespace storage

StorageSpaceの機能として、アプリで独自のStorageSpaceを追加することも可能になっています(service-config単位、Tenant単位ではありません)。

独自のStorageSpaceを定義することで

  • 一部大量データが格納されるEntityと、その他のEntityをそれぞれ別のStorageSpace(物理テーブル)に格納することができる

  • 各StorageSpace単位で物理テーブルの特性を変更することが可能なので、Entityの特性毎にうまく使い分ければ、 RDB上のテーブルスペースが効率的に利用され、RDBのファイルサイズを小さくすることができる

  • StorageSpace毎にパーティション定義内容を変更することができる

  • EntityのPropertyに対して Store Col Name を指定することで、 プロパティデータの物理格納カラムを独自で定義したテーブルカラムに直接格納することができる。
    これを利用して複合Indexの定義などのRDBネイティブの機能を利用することができる

などのカスタマイズが可能になります。

次は、このStorageSpace定義を実際に構成するテーブル構造について説明します。

テーブル構造

StorageSpaceはそれぞれで以下のテーブルセットで構成されます。

テーブル名 用途

obj_store

Entityのデータを格納する汎用テーブル。動的なEntity定義に対応するため予め複数のカラムが定義されている。
rbは削除データの格納用。

obj_store_rb

obj_ref

Entityデータ間の参照情報を格納するテーブル。
rbは削除データの格納用。

obj_ref_rb

obj_unique_str

UniqueIndexが設定されたプロパティ値を格納するテーブル群。
プロパティ型に合わせて、それぞれ定義されている。

obj_unique_ts

obj_unique_num

obj_unique_dbl

obj_unique_date

obj_index_str

Indexが設定されたプロパティ値を格納するテーブル群。
プロパティ型に合わせて、それぞれ定義されている。

obj_index_ts

obj_index_num

obj_index_dbl

obj_index_date

このテーブルセットがStorageSpaceごとに必要になります。

この中でも重要となるのが obj_store テーブルです。

「obj_store」は

  • Entityとして定義されるすべてのデータが格納される

  • 汎用的に利用するため、あらかじめ文字列型、数値型、日付型、浮動小数点型それぞれ、複数のカラムが定義されている

  • Index、UniqueIndex付のカラムもあらかじめ定義されている

  • Entityのプロパティはいずれかのカラムに自動的にマッピングされる
    (テナント、Entity単位で、それぞれどのカラムにどのプロパティ値が格納されるかは異なる)

  • 事前にテーブルに定義されているカラム数以上のプロパティがEntityに定義された場合、PageNoを用いて1データを複数行に格納する。

という役割を持っています。

実際にデータが格納されるイメージを示します。

データの格納イメージ
制御用共通カラム プロパティ値格納用汎用カラム Index値格納カラム

(TenantID)

(Entity定義ID)

(oid)

(name)

文字列Property

数値Property

IndexProperty

tenant_id

obj_def_id

obj_id

pg_no

obj_name

STR_1

STR_2

NUM_1

NUM_2

ISTR_1

ISTR_2

・・・

1

Product

00001

0

productA

string101

string202

110

istring101

istring102

1

Product

00002

0

productB

string201

string202

210

istring201

istring202

1

Product

00003

0

productC

string301

string302

310

istring301

istring302

1

QA

00001

0

qa001

aaa

bbb

1

QA

00001

1

ccc

ddd

1

QA

00002

0

qa002

eee

fff

1

QA

00002

1

ggg

1

Point

00001

0

point1

OK

100

1

Point

00002

0

point2

NG

200

Entityに定義されるPropertyの型ごとの数に対して、事前にテーブルに定義された各型ごとのカラムが足りなくなった場合は、 pg_noを利用して、複数レコードで保持します。
Query(EQL)を利用して検索されるときは、基盤内部で自動的に自己結合することで疑似的に1レコードとして返しています。

このようにiPLAss基盤内の仕様に沿った汎用的なテーブルを利用して、動的に定義されたEntityのプロパティデータを格納し、 EntityManagerを通してデータを制御しています。

次は、 obj_store テーブルに対する仕様について説明します。

obj_storeの仕様

EntityManagerでEntityデータを制御するために、obj_store テーブルの構造は一定の仕様(制約)があります。

テーブルは「システム共通項目(変更不可)」、「UniqueIndex値格納カラム」、「Index値格納カラム」、「Property値格納汎用カラム」に分かれます。 このうち「システム共通項目(変更不可)」以外は、service-configの storageSpace 定義と関連します。

標準で定義されている default StorageSpaceをもとに説明します。

ここではMySQLの標準設定を例に説明します。RDBによって標準で定義されている値は異なります。
<!-- Entity Store Settings -->
<service>
  <interface>org.iplass.mtp.spi.datastore.StoreService</interface>

  <property name="dataStore" class="org.iplass.mtp.impl.datastore.grdb.GRdbDataStore">
    ・・・・
    <property name="storageSpace">
      <property name="storageSpaceName" value="default" />
      <property name="varcharColumns" value="64" />
      <property name="decimalColumns" value="32" />
      <property name="timestampColumns" value="32" />
      <property name="doubleColumns" value="16" />
      <property name="indexedVarcharColumns" value="5" />
      <property name="indexedDecimalColumns" value="4" />
      <property name="indexedTimestampColumns" value="4" />
      <property name="indexedDoubleColumns" value="4" />
      <property name="uniqueIndexedVarcharColumns" value="2" />
      <property name="uniqueIndexedDecimalColumns" value="2" />
      <property name="uniqueIndexedTimestampColumns" value="2" />
      <property name="uniqueIndexedDoubleColumns" value="2" />
    </property>
    ・・・・
  </property>
</service>

各Propertyについて説明します。

設定項目 設定内容

storageSpaceName

Entity定義で選択する際に表示されるStorage Space名を指定します。
標準で、 defaultmtpuser が定義されているので、それ以外の名前を指定してください。

tableNamePostfix

StorageSpace用のテーブルに付加する接尾語を指定します。英数字のみ利用してください。
標準で、 defaultmtpuser が定義されているので、それ以外の名前を指定してください。

「テーブル構造」で説明したテーブルセットの各テーブル名に、ここで指定したPostfixを追加したテーブルが必要になります。

obj_storeの場合、

"obj_store" + __ + Postfix

となります。アンダースコア―を2つでつなげます。
例えば、 mtp StorageSpaceの場合は、obj_store__mtp となります。 obj_indexobj_unique_index なども同様です。

varcharColumns

文字列型のプロパティを格納するための列数を指定します。
ここで指定した数分、「STR_1」、「STR_2」、「STR_n」の列が必要になります。

decimalColumns

Decimal型のプロパティを格納するための列数を指定します。
ここで指定した数分、「NUM_1」、「NUM_2」、「NUM_n」の列が必要になります。

timestampColumns

Timestamp型のプロパティを格納するための列数を指定します。
ここで指定した数分、「TS_1」、「TS_2」、「TS_n」の列が必要になります。

doubleColumns

浮動小数点型のプロパティを格納するための列数を指定します。
ここで指定した数分、「DBL_1」、「DBL_2」、「DBL_n」の列が必要になります。

indexedVarcharColumns

Index指定された文字列型のプロパティを格納するための列数を指定します。
ここで指定した数分、「ISTR_1」、「ISTR_2」、「ISTR_n」の列が必要になります。

indexedDecimalColumns

Index指定されたDecimal型のプロパティを格納するための列数を指定します。
ここで指定した数分、「INUM_1」、「INUM_2」、「INUM_n」の列が必要になります。

indexedTimestampColumns

Index指定されたTimestamp型のプロパティを格納するための列数を指定します。
ここで指定した数分、「ITS_1」、「ITS_2」、「ITS_n」の列が必要になります。

indexedDoubleColumns

Index指定された浮動小数点型のプロパティを格納するための列数を指定します。
ここで指定した数分、「IDBL_1」、「IDBL_2」、「IDBL_n」の列が必要になります。

uniqueIndexedVarcharColumns

Unique Index指定された文字列型のプロパティを格納するための列数を指定します。
ここで指定した数分、「USTR_1」、「USTR_2」、「USTR_n」の列が必要になります。

uniqueIndexedDecimalColumns

Unique Index指定されたDecimal型のプロパティを格納するための列数を指定します。
ここで指定した数分、「UNUM_1」、「UNUM_2」、「UNUM_n」の列が必要になります。

uniqueIndexedTimestampColumns

Unique Index指定されたTimestamp型のプロパティを格納するための列数を指定します。
ここで指定した数分、「UTS_1」、「UTS_2」、「UTS_n」の列が必要になります。

uniqueIndexedDoubleColumns

Decimal型のプロパティを格納するための列数を指定します。
ここで指定した数分、「UDBL_1」、「UDBL_2」、「UDBL_n」の列が必要になります。

customPartition

この設定はobj_storeのルールとは直接関係はありません。

ただしMySQLの場合、自動でPartitionが拡張できないので、テナント作成用ToolであるTenantManagerで テナントを作成・削除する際にPartitionの作成・削除を行っています。 その際、この設定が true のStorageSpaceについてはPartitionに対する処理を行いません。

Partitionを利用する場合に、標準のPartitionと異なるPartitionを利用するかを指定します。 デフォルトが false のため、標準のPartitionの場合は指定する必要はありません。

標準のPartitionとは

obj_store${tableNamePostfix}_テナントID

の命名規則に則って、テナント単位でPartitionを作成しているものです。

Entityのプロパティ型と列の型の対応について

EntityのPropertyで指定された型によって、以下のカラムに値が格納されます。

カラムの型 対象となるEntityプロパティの型

Varchar

AutoNumber、Boolean、Select、String、LongText、Binary

Timestamp

Date、Datetime、Time

Decimal

Decimal、Integer

Double

Float

このようにservice-configに定義されたStorageSpace定義に沿った obj_store を作成することで、 EntityManagerがEntityデータを自動的に制御します。

5.2. カスタマイズ

「obj_storeの仕様」に則って obj_store テーブルを作成することで、独自のStorageSpaceを利用することができます。

これによって、例えば、

  • 大量データが格納されるEntityのために order StorageSpaceを準備し、 OrderEntityは order StorageSpaceに格納する

  • プロパティ数が少ないEntity用に small StorageSpaceとして、文字列型、数値型、日付型のカラムを3個ずつだけ定義する

  • 数値型のPropertyが多いEntity用に num StorageSpaceとして、数値型のカラムを300個定義して、日付型は1個だけにする

など、Entityの特性毎にうまく使い分ければ、RDB上のテーブルスペースが効率的に利用され、RDBのファイルサイズを小さくすることができます。 また、汎用カラム数がオーバーした場合に利用される pg_no による疑似レコード化も抑えることができます。

entity storagespace custom

独自のStorageSpaceを作成するには、service-configのStorageSpace定義に対応するStorageSpace用のテーブルセットを作成する必要があります。

このDDLを生成するためのツールとして、「Custom Storage Space」ツールを提供しています。 このツールで生成されたDDLをデータベースに反映すれば、独自のStorageSpaceが利用できるようになります。

ツールの利用方法は Custom Storage Spaceを参照してください。

5.3. カラムマッピング

Entityのプロパティ定義では、 Store Col Name という属性を指定することができます。
この機能は、StorageSpaceに紐ずく obj_store テーブルに独自のカラムを定義することで、そのカラムにプロパティ値を保存する機能です。

この値が未指定の場合は、プロパティの型とStorageSpace定義をもとにして基盤内部で「Property値格納汎用カラム」に自動的に値を格納します。 言いかえれば、 Store Col Name が未指定の場合は、どのカラムにデータが格納されているかはアプリ側では判断できません。

カラムマッピング機能により

  • 汎用カラムの文字列型は「TEXT」(MySQLの場合)として定義しているが、 独自のカラムでは「VARCHAR(3)」として定義することで、テーブルスペースを効率的に利用できる。

  • 独自カラムを指定することで、 obj_store のどのカラムに値が格納されるかが判断できる。
    例えば複合Indexを作成したり、Partitionを作成する際のKEYとして指定することができるなど、RDBネイティブの機能が利用できる。
    (年度カラムを定義して、テナント+年度などでPartitionを分けるなど)

といったことが可能になります。

極端な例としては、
あるEntityに特化したStorageSpaceを作成して、全てのプロパティに対して独自カラムを作成して、 プロパティで Store Col Name を定義すれば、「システム共通項目(変更不可)」以外は不要になります。

obj_store に対して独自のカラムを作成する場合は、 「カスタマイズ」で説明した手順を実施して作成されたDDLに対して追加してください。

以下の点に注意してください。

  • カラムの追加が必要なのは obj_storeobj_store_rb (削除データ格納用)テーブルの2つです。
    このテーブルのカラムは同期している必要があります。(データを物理削除するのみの場合はrbは利用されません)

  • 独自で追加するカラムの名前には、「STR_1」「NUM_1」「TS_1」「DBL_1」など、基盤が自動割当てに利用する名前は利用しないでください。

5.4. Entity定義の変更時の注意点

Entity定義を更新する場合には以下の点に注意してください。

StorageSpace変更時

既に登録済のデータは変更後のStorageSpaceに移動しません。
もしデータを移行したい場合は、「Storage Space Migration」ツールを利用するか、 変更前にPackageやEntityExplorerなどでエクスポートしたあとにStorageSpaceを変更し、インポートを行ってください。

ツールの利用方法は Storage Space Migrationを参照してください。

プロパティの追加・削除時

基本的にデータの更新・ロックは発生しません。
ただし、プロパティ追加に伴ってページの追加が必要になった場合は、データに対してレコードロックが掛ります。

プロパティの更新時
  • データ型を変更した場合
    データの変換が可能な場合、データの値は引き継がれます。 ただし、データに対してレコードロックが掛ります。

    データの変換が不可能な場合、データの値は引き継がれません。 このため基本的にはデータに対してロックは掛りません。
    ただし、新しいデータ型に合わせて新しくカラムが割り当てられるため、 この結果としてページの追加が必要になった場合は、レコードロックが掛ります。

  • Index定義を変更した場合
    データに対してレコードロックが掛ります。

プロパティ型の変更によるデータの引き継ぎ
  • 相互変換可能

    プロパティ型 補足

    DECIMAL ⇔ INTEGER ⇔ FLOAT

    桁数に応じての四捨五入等は発生

    DATE ⇔ DATETIME

    時間部分は00:00:00で補完

    TIME ⇔ DATETIME

    日付部分は1970-01-01で補完

    AUTONUMBER ⇔ STRING

  • 一方向のみ変換可能

    プロパティ型 補足

    * ⇒ STRING

    LONGTEXT,BINARY除くすべての型はSTRINGに変換可能

    STRING ⇒ LONGTEXT

    STRING型にて格納されている文字列のうち、DBのVARCHARカラムのバイト数-21バイト以上の文字は切り捨てされる。
    Oracleの場合3979byte以上の文字は切り捨てされる

    BOOLEAN ⇒ SELECT

    SELECT型の値としては、false→0、true→1になる

6.1. 概要

全文検索とは、Entity 及び Entity内の列への横断検索できる機能です。
例えば、既存のEQLを使った場合は以下のようになります。

複数の列に対して検索する       →   対象の列の数だけWHERE句を書く必要がある
複数のEntityに対して検索する   →   結合または複数回のEQLを実行する必要がある

全文検索を使えば、開発者が上記のロジックを組まなくても横断的な検索結果を取得することが可能です。

ただし、全文検索はリアルタイムでデータが反映されるわけではありません。ご注意ください。

iPLAssでは、Lucene(ルシーン)とSolr(ソーラー)という全文検索エンジンを利用しており、設定でどちらかを利用することが可能です。
各エンジンの簡単な差は下記を参照してください。

Lucene Solr

概要

100% PureJavaで作成された、インデックス作成タイプの全文検索ライブラリ。
Luceneが提供するAPIを用いることで、簡単に使いやすい全文検索プログラムを作ることが可能。

Luceneをベースに、管理画面やキャッシュ機構を取り入れたアプリケーション。

データ保存先

ファイル
service-config.xmlに保存先フォルダを指定するだけで利用が可能。

Solrサーバ内
別途Solrサーバを構築する必要がある。(本資料は構築済みであることが前提)

Analyzer(※)の扱いやすさ

デフォルトで使えるAnalyzerを適用する場合は、とても簡単。(複数種類有)
ただし、カスタマイズを行う場合は、Javaファイルを作成する必要がある。

Analyzerはないため、Tokenizer/Filterを個別に設定する必要がある。
ただし、カスタマイズも含め設定はXMLの更新だけ済むため、その後はSolrサーバの再起動のみで反映が可能。

Analyzer とは

全文検索では、以下の2種類のクラス(Tokenizer/Filter)を組み合わせて、文字の分割を行います。
Analyzerを利用することで、すでに決まったクラス・決まった順序で文字の組み合わせができるようになります。

  • Tokenizer
    検索対象値(Entityに登録されているデータ等)や検索文字列(検索キーワードとして入力された文字列)を、検索のための文書の最小要素(Token)へ分割するクラスです。
    検索結果にヒットする条件は、検索対象値と検索文字列それぞれのTokenが一致することとなるため、Tokenizerの指定により検索の使い勝手が変わります
    例えば、「関西国際空港」という文字列があったとします。指定するTokenizerにより生成されるTokenは、以下のパターン等に分かれます。

    • 「関西国際空港」
      (「関西国際空港」を1つの検索文字列として扱われます。この場合、検索文字列に「関西」と入力してもヒットしません)

    • 「関西」「国際」「空港」
      (意味のある文字列に分割します。これにより、「関西」、「国際」、「空港」のいずれかの入力でヒットします)

    • 「関西」「西国」「国際」「際空」「空港」「港」
      (左記のように2文字ずつに分割します。これにより、上記よりもヒットする文字列の数は多くなります)
      ※ただし「東京都庁」のような場合は、「京都」という検索文字列でもヒットする、などの意図しないヒットが発生しやすいです

  • Filter
    表記ゆれの補完や検索文字として適さない文字列の削除などを行うクラスです。
    例えば、Lucene側でいえば以下のようなFilterが存在します。

    • JapaneseKatakanaStemFilter
      4文字以上のカタカナの長音を除去します。「サーバー」は「サーバ」とも補完され、検索文字列にどちらを入力してもヒットします。

    • CJKWidthFilter
      半角カナは全角カナへ、全角英数は半角英数へ補完され、補完後の文字列でヒットするようになります。

  • 利用バージョン
    利用しているバージョンは以下の通りです。

    iPLAss lucene Solr

    3.0.0 ~

    7.4.0

    6.2.1

    バージョンは上記表を元にURLからダウンロードしてください。
    ※過去バージョンもこちらのURLから辿ることができます。

    該当バージョンのフォルダをクリック後、zipファイルをダウンロードしてください。
    各jarは解凍後のルートから下記の通りの場所に配置してあります。

    jar名 ディレクトリ

    lucene-analyzers-common-x.x.x.jar

    lucene-x.x.x/analysis/common

    lucene-analyzers-kuromoji-x.x.x.jar

    lucene-x.x.x/analysis/kuromoji

    lucene-analyzers-smartcn-x.x.x.jar

    lucene-x.x.x/analysis/smartcn

    lucene-core-x.x.x.jar

    lucene-x.x.x/core

6.2. 設定

Lucene設定

  1. service-config.xmlの設定

    1. デフォルトのAnalyzerを利用する場合
      デフォルトの全文検索(Lucene)を利用する場合、下記設定を確認してください。

      <!-- 全文検索で利用するService -->
      <service>
        <interface>org.iplass.mtp.impl.fulltextsearch.FulltextSearchService</interface>
        <property name="useFulltextSearch" value="true" />
        <property name="maxRows" value="10" />
        <property name="throwExceptionWhenOverLimit" value="false"/>
      
        <!-- lucene利用 -->
        <class>org.iplass.mtp.impl.fulltextsearch.FulltextSearchLuceneService</class>
        <property name="directory" value="D:\USER\Data\lucene" />
        <property name="analyzer" value="org.apache.lucene.analysis.ja.JapaneseAnalyzer" />
      </service>
      プロパティ名 内容説明

      useFulltextSearch

      false:全文検索機能が無効になります。
      true:全文検索機能が有効になります。

      maxRows

      全文検索サーバから取得する検索結果の最大件数を設定します。
      ※件数が1000件以上で、かつ、Oracleを利用する場合は、Oracle設定の「enableInPartitioning」プロパティ値を「true」へ変更してください。

      throwExceptionWhenOverLimit

      false:maxRowsの設定値以上に検索結果件数が取得できた場合でも、maxRowsの設定値分の件数は結果を表示します。

      ※画像は設定値以上に取得出来た場合となります。設定値未満の場合、メッセージ表示はありません。

      FulltextSearch Lucene SearchResult False

      true:maxRowsの設定値以上に検索結果件数が取得できた場合、結果を表示せずに条件を絞り込むようメッセージで催促されます。

      FulltextSearch Lucene SearchResult True

      directory

      indexを保存するディレクトリを指定します。

      analyzer

      利用するAnalyzerを指定します。

      デフォルトで指定可能なAnalyzerは以下のとおりです。
      (AnalyzerはLuceneが配布している別のjarを追加することで、Analyzerを増やすことも可能です)

      デフォルトのAnalyzer内に定義されているFilterはすべて、Tokenizer実行後に呼び出されます。
      1. org.apache.lucene.analysis.core.WhitespaceAnalyzer
        空白分割

        指定することで実行されるTokenizer/Filter:
        • WhitespaceTokenizer
          空白を利用して分割します。

      2. org.apache.lucene.analysis.core.SimpleAnalyzer
        アルファベット分割

        指定することで実行されるTokenizer/Filter:
        • LetterTokenizer
          非文字を利用して分割します。

        • LowerCaseFilter
          英字の大文字を小文字に変換します。

      3. org.apache.lucene.analysis.core.StopAnalyzer
        ストップワード分割

        指定することで実行されるTokenizer/Filter:
        • LetterTokenizer
          非文字を利用して分割します。

        • LowerCaseFilter
          英字の大文字を小文字に変換します。

        • StopFilter
          指定文字を除外します。除外対象となるTokenは*※1*参照してください。

      4. org.apache.lucene.analysis.standard.StandardAnalyzer
        高度分割(Eメール・アドレス・頭字語・中国語・日本語・韓国語・英数字など)

        指定することで実行されるTokenizer/Filter:
        • StandardTokenizer
          Unicode Standard Annex #29(※2)で定められる語の境界ルールに従って分割します。

        • StandardFilter
          StandardTokenizerで抽出したTokenを正規化します。

        • LowerCaseFilter
          英字の大文字を小文字に変換します。

        • StopFilter
          指定文字を除外します。本Analyzerでも除外TokenはStopAnalyzerと同じです。

      5. org.apache.lucene.analysis.cjk.CJKAnalyzer
        Bi-gram分割(文章が分かち書きされない言語である中国語、日本語および韓国語向け)

        指定することで実行されるTokenizer/Filter:
        • StandardTokenizer
          Unicode Standard Annex #29(※2)で定められる語の境界ルールに従って分割します。

        • CJKWidthFilter
          半角カナは全角カナへ、全角英数は半角英数へ補完します。

        • LowerCaseFilter
          英字の大文字を小文字に変換します。

        • CJKBigramFilter
          漢字、ひらがな、カタカナ、ハングルをbi-gramに変換します。

        • StopFilter
          指定文字を除外します。除外対象となるTokenは*※3*を参照してください。(jar内のstopwords.txtより)

      6. org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer
        中国語分割(簡体字中国語または中国語と英語が混在している文字列に有効)

        指定することで実行されるTokenizer/Filter:
        • HMMChineseTokenizer
          確率的知識といわれるものを利用して分割します。

        • PorterStemFilter
          半角英字系の文字列の揺れを補完します。(boxes→boxなど。ただし、was→waという事例も)

        • StopFilter
          指定文字を除外します。除外対象となるTokenは*※3*を参照してください。(jar内のstopwords.txtより)

      7. org.apache.lucene.analysis.ja.JapaneseAnalyzer
        日本語分割(Searchモード)

        指定することで実行されるTokenizer/Filter:
        • JapaneseTokenizer
          形態素解析といわれるものを利用し分割後、複合語で構成された単語をさらに細かく分割します。

        • JapaneseBaseFormFilter
          動詞などを基本形へ置き換えます。(書き→書く、した→するなど)

        • JapanesePartOfSpeechStopFilter
          指定した品詞を除外します。除外対象となる品詞は*※4*参照してください。

        • CJKWidthFilter
          半角カナは全角カナへ、全角英数は半角英数へ補完します。

        • StopFilter
          指定文字を除外します。除外対象となるTokenは*※5*参照してください。

        • JapaneseKatakanaStemFilter
          4文字以上のカタカナの長音を除去します。

        • LowerCaseFilter
          英字の大文字を小文字に変換します。

      8. org.iplass.mtp.impl.fulltextsearch.JapaneseNormalAnalyzer
        日本語分割(Normalモード):形態素解析を利用して分割します。その他はSearchモードと同様です。(※6

      9. org.iplass.mtp.impl.fulltextsearch.JapaneseExtendedAnalyzer
        日本語分割(Extendedモード):Searchモードと同様の分割を行いつつ、辞書にない未知語をuni-gramに分割します。

        ※1

        Luceneのjar内のstopwords.txtより
        a, an, and,are, as, at, be, but, by, for, if, in,into, is, it, no, not, of, on, or, such,that, the, their, then, there, these, they,this, to, was, will, with

        ※2

        参考URL
        http://unicode.org/reports/tr29/

        ※3

        SmartChineseのjar内のstopwords.txtより
        , . ` - _ = ? ' | " ( ) { } [ ] < > * # & ^ $ @ ! ~ : ; + / \ 《 》 — - , 。 、 : ; ! · ? “ ” ) ( 【 】 [ ] ●    

        ※4

        Kuromoji(日本語分割)のjar内のstoptags.txtより
        接続詞, 助詞, 助詞-格助詞, 助詞-格助詞-一般, 助詞-格助詞-引用, 助詞-格助詞-連語, 助詞-接続助詞, 助詞-係助詞, 助詞-副助詞, 助詞-間投助詞, 助詞-並立助詞, 助詞-終助詞, 助詞-副助詞/並立助詞/終助詞, 助詞-連体化, 助詞-副詞化, 助詞-特殊, 助動詞, 記号, 記号-一般, 記号-読点, 記号-句点, 記号-空白, 記号-括弧開, 記号-括弧閉, その他-間投, フィラー, 非言語音

        ※5

        Kuromoji(日本語分割)のjar内のstopwords.txtより
        の, に, は, を, た, が, で, て, と, し, れ, さ, ある, いる, も, する, から, な, こと, として, い, や, れる, など, なっ, ない, この, ため, その, あっ, よう, また, もの, という, あり, まで, られ, なる, へ, か, だ, これ, によって, により, おり, より, による, ず, なり, られる, において, ば, なかっ, なく, しかし, について, せ, だっ, その後, できる, それ, う, ので, なお, のみ, でき, き, つ, における, および, いう, さらに, でも, ら, たり, その他, に関する, たち, ます, ん, なら, に対して, 特に, せる, 及び, これら, とき, では, にて, ほか, ながら, うち, そして, とともに, ただし, かつて, それぞれ, または, お, ほど, ものの, に対する, ほとんど, と共に, といった, です, とも, ところ, ここ

        ※6

        Kuromoji(日本語分割)のモードごとについての参考URL
        http://d.hatena.ne.jp/Kazuhira/20130602/1370163286

    2. Analyzerをカスタマイズして利用する場合
      カスタマイズの全文検索(Lucene)を利用する場合、下記設定を確認してください。

      <!-- 全文検索で利用するService -->
      <service>
        <interface>org.iplass.mtp.impl.fulltextsearch.FulltextSearchService</interface>
        <property name="useFulltextSearch" value="true" />
        <property name="maxRows" value="10" />
        <property name="throwExceptionWhenOverLimit" value="false"/>
      
        <!-- lucene利用 -->
        <class>org.iplass.mtp.impl.fulltextsearch.FulltextSearchLuceneService</class>
        <property name="directory" value="D:\USER\Data\lucene" />
        <!-- 独自パラメータを利用しない場合 -->
        <property name="analyzer" value="xx.xx.xxxxx.XxxAnalyzer" />
        <!-- 独自パラメータを利用する場合 -->
        <property name="analyzer" class="xx.xx.xxxxx.XxxAnalyzer" >
          <property name="bean" class="fulltext.TestBean" >
            <property name="property1" value="D:\lucene\cstmStopwords.txt" />
            <property name="property2" value="false" />
          </property>
        </property>
      </service>
      プロパティ名 内容説明

      analyzer

      利用するカスタマイズAnalyzerを指定します。作成方法は後述を参照してください。
      (独自パラメータを利用しない場合はvalue値に、する場合はclass値にクラス名を記載してください。)

      bean

      独自パラメータの情報を保持するクラスを指定します。

      name, value など

      独自パラメータ名はbean内のフィールド名に対応した名前をpropertyのname値に設定してください。

      1. 独自パラメータなしのカスタマイズAnalyzerクラスの作成

        package xx.xx.xxx;
        
        import org.apache.lucene.analysis.Analyzer;
        import org.apache.lucene.analysis.TokenStream;
        import org.apache.lucene.analysis.Tokenizer;
        import org.apache.lucene.analysis.cjk.CJKWidthFilter;
        import org.apache.lucene.analysis.core.LowerCaseFilter;
        import org.apache.lucene.analysis.core.StopFilter;
        import org.apache.lucene.analysis.ja.JapaneseAnalyzer;
        import org.apache.lucene.analysis.ja.JapaneseBaseFormFilter;
        import org.apache.lucene.analysis.ja.JapaneseKatakanaStemFilter;
        import org.apache.lucene.analysis.ja.JapanesePartOfSpeechStopFilter;
        import org.apache.lucene.analysis.ja.JapaneseTokenizer;
        import org.iplass.mtp.spi.Config;
        import org.iplass.mtp.spi.Service;
        import org.iplass.mtp.spi.ServiceInitListener;
        
        public class XxxxAnalyzer extends Analyzer implements ServiceInitListener {
        
            public XxxxAnalyzer() {
                // 全プロパティに同じTokenizer/Filterを適用する場合
                // super(GLOBAL_REUSE_STRATEGY); (1)
                // プロパティ毎にTokenizer/Filterを適用する場合
                // super(PER_FIELD_REUSE_STRATEGY); (2)
            }
        
            @Override
            public void inited(Service service, Config config) {
            }
        
            @Override
            public void destroyed() {
        
            }
        
            @Override (4)
            protected TokenStreamComponents createComponents(String propertyName) { (3)
                Tokenizer tokenizer = new JapaneseTokenizer(null, true, JapaneseTokenizer.Mode.SEARCH);
                TokenStream stream = new JapaneseBaseFormFilter(tokenizer);
                stream = new JapanesePartOfSpeechStopFilter(
                    stream,
                    JapaneseAnalyzer.getDefaultStopTags()
                );
                stream = new CJKWidthFilter(stream);
                stream = new StopFilter(
                    stream,
                    JapaneseAnalyzer.getDefaultStopSet()
                );
                stream = new JapaneseKatakanaStemFilter(stream);
                stream = new LowerCaseFilter(stream);
                return new Analyzer.TokenStreamComponents(tokenizer, stream);
            }
            @Override (5)
            protected TokenStream normalize(String fieldName, TokenStream in) {
                TokenStream result = new CJKWidthFilter(in);
                result = new LowerCaseFilter(result);
                return result;
            }
        }
        ※1

        GLOBAL_REUSE_STRATEGY
        全プロパティ(Entityの列)に対し、同じTokenizer/Filterを適用します。
        (Index付与時や検索時に1度だけTokenizer/Filterの定義が呼ばれます)

        ※2

        PER_FIELD_REUSE_STRATEGY
        プロパティ毎にTokenizer/Filterが適用されるようにします。
        (Index付与時や検索時に、対象プロパティの数だけTokeinzer/Filterの定義が呼ばれます) デフォルトではfalseとなっているため、変更しない場合は定義不要です。

        ※3

        この引数値がプロパティ名となります。
        プロパティ毎に設定する場合、この値を利用して内部分岐が可能になります。

        取得できるプロパティ名はiPLAssで定義した物理名となり、また、Entity毎に処理を分岐することはできません。
        ※4

        Override必須メソッド。
        このメソッド内をカスタマイズすることで、任意のTokenizer/Filterを組み合わせて全文検索を利用することができます。
        ※サンプルソースAnalyzer: org.apache.lucene.analysis.ja.JapaneseAnalyzer を設定した場合と同等の処理
        その他利用できるクラスは、下記URL等をご覧ください。

        ※5

        Override必須メソッド。
        このメソッド内をカスタマイズすることで、引数として検索文字列のTokenStreamに対して、NormalizationのFilterを適用することが出来ます。
        Stemmingのものを適用しないべきです。下記のURLでJavaDocのサンプルコードを参照してください。

      2. 独自パラメータありのカスタマイズAnalyzerクラスの作成

        package xx.xx.xxx;
        
        import ()
        
        public class XxxxAnalyzer extends Analyzer implements ServiceInitListener {
        
            private XxxxBean config; (1)
        
            public XxxxAnalyzer() {} (2)
        
            public void setBean(XxxxBean config) { (1)
                this.config = config;
            }
        
            @Override
            public void inited(Service service, Config config) {
            }
        
            @Override
            public void destroyed() {
        
            }
        
            @Override (3)
            protected TokenStreamComponents createComponents(String propertyName) {}
            @Override (4)
            protected TokenStream normalize(String fieldName, TokenStream in) {}
        }
        1 設定ファイルに定義したプロパティ名をフィールドに持つBeanクラスを作成したクラス内で持ち、setterを追加してください。
        2 i-1、i-2と同様。
        3 i-4と同様。
        4 i-5と同様。
        package xx.xx.xxx;
        
        public class XxxxBean {
            private String property1;
            private boolean property2;
            public String getProperty1() {
                return property1;
            }
            public void setProperty1(String property1) {
                this.property1 = property1;
            }
            public boolean getProperty2() {
                return property2;
            }
            public void setProperty2(boolean property2) {
                this.property2 = property2;
            }
        }

        1-bの設定ファイルのように記載した場合、property1、property2がそれぞれ設定された状態になります。

        private String property1 = "D:\lucene\cstmStopwords.txt"
        private boolean property2 = false

Solr設定

  1. service-config.xmlの設定
    下記設定を確認してください。

    <!-- 全文検索で利用するService -->
    <service>
      <interface>org.iplass.mtp.impl.fulltextsearch.FulltextSearchService</interface>
      <property name="useFulltextSearch" value="true" />
      <property name="maxRows" value="10" />
      <property name="throwExceptionWhenOverLimit" value="false"/>
    
      <!-- solr利用 -->
      <class>org.iplass.mtp.impl.fulltextsearch.FulltextSearchSolrService</class>
      <property name="solrUrl" value="http://{ADDRESS}:{PORT}/solr/{CoreName}/" />
    </service>
    プロパティ名 内容説明

    useFulltextSearch

    false:全文検索機能が無効になります。
    true:全文検索機能が有効になります。

    maxRows

    全文検索サーバから取得する検索結果の最大件数を設定します。
    ※件数が1000件以上で、かつ、Oracleを利用する場合は、Oracle設定の「enableInPartitioning」プロパティ値を「true」へ変更してください。

    throwExceptionWhenOverLimit

    false:maxRowsの設定値以上に検索結果件数が取得できた場合でも、maxRowsの設定値分の件数は結果を表示します。

    ※画像は設定値以上に取得出来た場合となります。設定値未満の場合、メッセージ表示はありません。

    FulltextSearch Solr SearchResult False

    true:maxRowsの設定値以上に検索結果件数が取得できた場合、結果を表示せずに条件を絞り込むようメッセージで催促されます。

    FulltextSearch Solr SearchResult True

    solrUrl

    利用するSolrサーバのURL

  2. schema.xmlの設定

    有償版のSDKに同梱されている iplass-ee-solr-XXX.jar ファイルを解凍し、schema\solr ディレクトリ下の schema.xml を取得します。

    schema.xmlは、検索対象値の保持方法や分割方法の情報が定義されています。
    対象ファイルはサーバ構築時に作成されたcore(以下の画像であれば、「collection1」)/confの下に配置するファイルとなります。

    FulltextSearch Solr SchemaXML Setting

    下記はschema.xmlの一部抜粋したものになります。

    <?xml version="1.0" encoding="UTF-8" ?>
    <schema name="iPLAss" version="1.4">
        :
        :
        <fieldType name="text_sen" class="solr.TextField" positionIncrementGap="100" > (1)
            <analyzer> (2)
                <charFilter class="solr.MappingCharFilterFactory" mapping="lang/mapping_ja.txt"/> (3)
                <tokenizer class="solr.JapaneseTokenizerFactory" mode="SEARCH" discardPunctuation="true" />
                <filter class="solr.JapaneseBaseFormFilterFactory"/>
                <filter class="solr.JapanesePartOfSpeechStopFilterFactory" tags="lang/stoptags_ja.txt"/>
                <filter class="solr.CJKWidthFilterFactory"/>
                <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_ja.txt" />
                <filter class="solr.JapaneseKatakanaStemFilterFactory" minimumLength="4"/>
                <filter class="solr.LowerCaseFilterFactory"/>
                <filter class="solr.SynonymFilterFactory" synonyms="lang/synonyms_ja.txt" ignoreCase="true" expand="true"/>
            </analyzer>
        </fieldType>
        :
        :
        <fields>
            <field name="V_1" type="text_sen" indexed="true" stored="true" omitNorms="true" multiValued="true" /> (1)
            <field name="V_2" type="text_sen" indexed="true" stored="true" omitNorms="true" multiValued="true" />
        </fields>
    </schema>
    1 この紐付きが一致するプロパティに対して、上記で定義したTokenizer/Filterが適用されます。
    フィールドごとに変更した場合は、別途fieldTypeを作成し紐付けるようにしていただくことで実現可能になります。
    2 この部分をカスタマイズすることで、任意のTokenizer/Filterを組み合わせて全文検索を利用することができます。
    3 サーバ構築時に作成されたcoreのinstanceDirの conf/lang ディレクトリの下にTokenizer/Filterに適用するxxx.txtファイルを入れます。

    ※サンプル定義: LuceneのデフォルトAnalyzerである org.apache.lucene.analysis.ja.JapaneseAnalyzer をSolrで類似実現する場合の定義例
    (charFilterのクラスは org.apache.lucene.analysis.ja.JapaneseAnalyzer にはない)
    (xxx.txt類は各自用意したものを想定)

    その他利用できるclass値については以下URL等をご覧ください。

    ※Lucene側のAPIに関しては、xxxxFactoryというものがSolrで設定できるクラスとなっております。

    StopAnalyzer

    Luceneで指定できるAnalyzer

    StopFilter

    Luceneで指定できるFilter

    StopFilterFactory

    Solrで指定できるFilter

    <fieldType>タグ内の各タグについて

    • <analyzer>タグ
      Tokenizer/Filterを定義するための親タグです。
      また以下のように定義することも可能です。

      <analyzer type="index"> 検索対象値を保持する前に適用されるTokenizer/Filterの定義します。
          ・・・・
      </analyzer>
      <analyzer type="query"> 検索文字列を検索対象値と比較する前に適用されるTokenizer/Filterを定義します。
          ・・・・
      </analyzer>
    • <charFilter>タグ
      Tokenizer実行前に呼ばれるFilterを定義します。

    • <tokenizer>タグ
      実行するTokenizerを定義します。

    • <filter>タグ
      Tokenizer実行後に呼ばれるFilterを定義します。

class値以外の要素(modeやtagsなど)は指定するclassにより異なりますので、ご注意ください。
  • Solrサーバに関する補足
    Tokenizer/FilterはSolrサーバ本体だけでは動作しないものがあります。
    例えば、schema.xmlに以下の定義がある場合、

        :
        :
        <fieldType name="text_chinese" class="solr.TextField">
            <analyzer>
                <tokenizer class="org.apache.lucene.analysis.cn.smart.HMMChineseTokenizerFactory"/>
                <filter class="solr.LowerCaseFilterFactory" />
            </analyzer>
        </fieldType>
        :
        :

    この場合は、サーバ構築時に作成されたcoreのinstanceDir(以下の画像であれば、「collection1」)下に lib ディレクトリを作成し、そのディレクトリにjarを追加で配置してください。

    FulltextSearch Solr Lib

6.3. 表示方法

  1. インターセプターの設定
    service-config.xmlに以下設定を追加してください。

    <service>
      <interface>org.iplass.mtp.impl.entity.EntityService</interface>
      <property name="interceptor" class="org.iplass.mtp.impl.entity.fulltextsearch.FulltextSearchInterceptor" additional="true" />
    </service>
  2. Indexの設定
    AdminConsoleからIndex対象とするEntity、および、Entityの列の設定をします。
    全文検索対象としたいEntityを開き、「crawl for full text search」にチェックしてください。
    すると、デフォルトでIndex対象となりうるプロパティの「Crawl」列にチェックが出ます。
    各プロジェクトで必要なプロパティに対してのみチェックがあることを確認し、保存してください。
    ※typeがBinaryであるプロパティクを全文検索対象とした場合、バイナリのファイル名 + " " + バイナリのファイルタイプ という値で登録されます。

    FulltextSearch Index Setting
  1. Indexの作成
    全文検索で検索が行われるデータは下記のIndex作成をした時点でのデータとなります。(ただし取得(表示)されるデータは、検索時点の最新データとなります)
    そのため、新しく登録したデータをIndexに含めたいなどの場合は、必ずIndexの再作成を実施してください。

    1. AdminConsoleから
      AdminConsole画面の左下メニューにあるToolsのEntityExplorerを選択します。
      全文検索を利用する設定になっている場合、「Entity Crawl」タブが表示されます。
      「Entity Crawl」タブのリストには「crawl for full text search」にチェックをつけたEntityのみが表示されます。

      FulltextSearch Index Create AdminConsole

      クローリングには選択したEntityのみを対象に実施する方法と、全Entityを全てクローリングする2種類があります。

      A.

      任意のEntityのみを対象としてクローリングしたい場合はリストのEntityにチェックをいれ、Start Crawl ボタンをクリックして下さい。

      B.

      クローリング対象Entityを全てクローリングしたい場合は Re Crawl All Entity ボタンをクリックして下さい。この場合、チェックをいれていないEntityも全てが対象となります。

      FulltextSearch Index Create Crawl
      C.

      クローリング処理が完了しましたら、INDEXデータの変更を反映させるために、Refresh ボタンをクリックしてください。

    2. バッチから
      配布しているiPLAss SDKの iplass-tools-batch/src/main/sh/start_entitydata_crawl.sh をクーロン等にセットすることで、バッチ起動が可能です。 ※

      尚上記shには以下の内容が設定可能です。

      • オプション
        -sとすることでサイレントモードで起動可能です。

      • 引数
        テナントIDを指定する事で対象テナントのみクローリング可能です。

        スケジュール起動するバッチとした場合は-sオプションを指定し、引数はなしで実行する事で、定期的に全テナントに対してクロール処理を実行します。
        クロール対象となるEntityは前述のindexの設定に記載した内容で設定したEntityのみとなります。

        (例)$ ./start_entitydata_crawl.sh -s

  2. 検索の実施

    1. 画面からの検索
      全文検索機能を有効にしている場合のみ画面上部に拡大鏡マークが表示されます。
      拡大鏡マークをクリックすると、検索プルダウンが表示されます。
      また、このプルダウンに表示されるエンティティはAdminConsoleでIndex対象としたEntityのみとなります。
      プルダウン右にあるテキストボックスに文字列を入力して検索することで全文検索が実行されます。
      エンティティを選択しない場合はIndex対象とした全Entityが対象なり、下記のような検索結果画面となります。

      FulltextSearch Search

      (参考)ソートについて
      全文検索機能でのソートは以下のようになっています。

      第1ソート:スコア(降順)
      第2ソート:Entity定義名(昇順)

      スコアとは、検索文字列と検索対象値(行単位)の関連度が数値化されたものとなります。
      これらはSolr/Lucene内部で算出された値を利用しています。
      簡単に条件を表すと、以下に一致すればするほどスコア値が高くなる傾向があります。

      • 検索文字列の単語がより多く含まれている

      • 検索対象の値が短い

      • 全検索対象の中で希少単語が含まれている

      「検索対象の値が短い」の例: 実行環境として、全文検索の取得上限(maxRows設定)を「5」に設定し、「java」という文字列が3つ出てくる情報を4つ、2つ出てくる情報(緑枠の部分)を3つ用意している状態です。
      (全文検索対象のプロパティは「検索対象1」~「検索対象3」)
      スコア値が高くなる傾向の1つである「検索文字列の単語がより多く含まれている」が適用されたことにより、「java」という文字列が2つ出てくる情報は1件分しか表示できません。
      「java」という文字列が2つ出てくる情報の種類として、1件はプロパティに設定した値が「java」だけのもののみ、その他2件はjavaの他に文字列を含んでいる値になっています。
      「検索対象の値が短い」という条件が適用されたことにより、「java」だけ登録した値がスコアとして高くなり、全文検索結果として表示されています。

      FulltextSearch SearchTargetValue Short
    2. 検索の実装方法

      1. 全エンティティを対象に検索する場合
        EntityManagerのfulltextSearchEntityを利用して下さい。
        複数のEntityに対して横断的に一括で検索します。
        ただし、取得できるデータはEntity毎にマージされたものとなります。

      2. EQLの検索条件として全文検索条件を利用する場合
        下記のようにEQLの条件中に、全文検索の条件を含めることが可能です。
        Containsの中に検索キーワードをセットして下さい。
        EQLを利用し検索する場合は、当該Entityに対して設定されているセキュリティ権限が反映されます。

        Containsの中に検索キーワードをセットして下さい。

        EntityManager em = ManagerLocator.manager(EntityManager.class);
        
        Query query = new Query();
        query.select(Entity.OID, Entity.NAME).from("search.Test01").where(new Contains("hogehoge"));
        
        SearchResult<Entity> result = em.searchEntity(query);

        もしくはEQL直接以下のように利用可能です。

        EntityManager em = ManagerLocator.manager(EntityManager.class);
        
        SearchResult<Entity> result = em.searchEntity(Query.newQuery("select oid, name from search.Test01 where contains ('test')"));
  3. 検索結果および詳細などのView設定
    全文検索結果に表示されるプロパティや[詳細、編集]リンク押下時の遷移先ViewをAdminConsoleから設定することが可能です。

    まず、対象のEntity毎に全文検索結果に表示したい項目などを設定したViewをSearch Layoutで作成してください。
    その後、AdminConsole画面のMetaDataSettings内にあるView Components > TopView の設定を変更します。
    ※TopViewの機能概要、作成方法についてはTopViewの章を参照してください。

    右にあるメニューから、Toolbar Parts > Fulltext Search(PU) を選択し、Main Area(Droppable P(Parts))にドラックアンドドロップしてください。

    FulltextSearch SetParts

    ドラックアンドドロップして出てきたFulltext Searchの設定ボタンをクリックすると、「crawl for full text search」にチェックをつけたEntityのみが表示されます。
    Entity View列に設定したSearchViewを使用して、全文検索結果となったEntityの情報を表示します。

    別途対象EntityのSearch ViewでカスタマイズしたViewを作成することで、Entity Viewの選択リストに表示されるようになります。
    FulltextSearch Parts Setting

    その他の設定可能な項目は以下になります。

    設定項目 設定内容

    Display search textbox

    本項目にチェックを入れると、ヘッダーに全文検索用のテキストボックスが表示されます。

    Entity View

    全文検索の結果表示で利用するViewを選択します。

    show in search list

    本項目にチェックを入れると、全文検索実行時の対象として、個別に選択が可能になります。

  4. 詳細・編集リンクのカスタマイズ
    全文検索結果を表示するSearchViewのSearchFormView Settingに自作したアクションを設定することで、カスタマイズした画面への遷移が可能となります。

    FulltextSearch Customize
    1. 詳細リンク先アクション

    2. 編集リンク先アクション

    遷移先で取得可能なリクエスト情報は以下の通りになります。

    パラメータ名 内容説明

    defName

    選択した情報のEntity定義名

    oid

    選択した情報のOID

    version

    選択した情報のVersion

データ管理