メタデータAPIでDBスキーマ作成

by Shinichi Tomita on 9月 5, 2007 at 06:19 午後

Summer'07より(Developer Editionのみのプレビューリリースですが)メタデータAPIが公開されました。Salesforceをデータベースになぞらえるなら、今まではDML(データ操作言語)だけであったのが、今度からはDDL(データ定義言語)相当の機能も追加されるようになったと思っていただければよいかもしれません。これによって、Salesforceのデータベーススキーマ(カスタムオブジェクト、カスタム項目)を操作する独自プログラムを作成することが可能になります。

メタデータAPIにアクセスするには、まずAPIの仕様を定義したWSDLファイルをダウンロードします。Developer Editionの管理ユーザアカウントでSalesforceにログインし、「設定>統合>Apex API」から、「Metadata WSDL のダウンロード」のリンクからダウンロードができます。なお、同時にPartner WSDL についてもダウンロードしておいてください。

WSDLを利用するため、今回はJava言語を利用してクライアントを書いてみます。事前にApache Axis 1.4をダウンロードしておいて、関連ライブラリをローカルファイルに保存しておきましょう。

まずAxisのWSDL2Javaを利用して、先ほどダウンロードしたWSDLファイルから、APIにアクセスするためのスタブソースファイルを生成します。<CLASSPATH>にはAxisのライブラリへのパスを含めて指定します。

java -classpath <CLASSPATH> org.apache.axis.wsdl.WSDL2Java -a metadata.wsdl

同様にして、Partner WSDLについてもスタブソースファイルを生成します。

java -classpath <CLASSPATH> org.apache.axis.wsdl.WSDL2Java -a partner.wsdl

生成されたクラスを利用することで、JavaのクライアントプログラムからメタデータAPIにアクセスすることができます。もちろん、メタデータAPIにアクセスするにはまずSalesforceから認証を受けなければいけません。ログイン認証は従来のAPIにアクセスする場合と同様に行います。

SforceServiceLocator sforceServiceLocator = new SforceServiceLocator();
SoapBindingStub sforce = (SoapBindingStub) sforceServiceLocator.getSoap();

LoginResult loginResult = sforce.login(username, password);

ログインに成功すると、認証済みセッションIDが取得できますので、この値をメタデータAPIのスタブにヘッダ情報としてセットします。これでメタデータAPIのメソッドを利用できるようになります。

MetadataServiceLocator metaServiceLocator = new MetadataServiceLocator();
meta = (MetadataBindingStub) metaServiceLocator.getMetadata();

SessionHeader sessionHeader = new SessionHeader();
sessionHeader.setSessionId(loginResult.getSessionId());
meta.setHeader(metaServiceLocator.getServiceName().getNamespaceURI(), "SessionHeader", sessionHeader);

Salesforceではカスタムオブジェクトがデータベースにおけるテーブル(表)に相当し、カスタム項目はカラム(列)に相当します。まず最初にカスタムオブジェクトを作成してみましょう。カスタムオブジェクトを作成するには、WSDLから生成されたクラスに含まれる CustomObject クラスをインスタンス化することで行います。カスタムオブジェクトに必要なプロパティをインスタンスにセットした後、レコード名を表示するためのカスタム項目を1つ作成し、NameFieldとして設定します。

CustomObject customObj = new CustomObject();
customObj.setFullName("MyCustomObject__c"); // 作成するオブジェクトのAPI参照名(__cを末尾に付加)を指定
customObj.setLabel("私のカスタムオブジェクト"); // 表示ラベル
customObj.setPluralLabel("私のカスタムオブジェクト"); // 複数形の表示ラベル
customObj.setDescription("このオブジェクトはメタデータAPIから作成されました"); // オブジェクトの説明
customObj.setEnableReports(true); // レポートの許可
customObj.setEnableActivities(true); // 活動の許可
customObj.setEnableHistory(true); // 項目履歴管理
customObj.setDeploymentStatus(DeploymentStatus.Deployed); //リリース状況
customObj.setSharingModel(SharingModel.ReadWrite); // デフォルトの共有設定

// レコード名の項目
CustomField nameField = new CustomField();
nameField.setFullName("MyCustomObject__c.Name"); // オブジェクトのAPI参照名 . 項目のAPI参照名
nameField.setType(FieldType.AutoNumber); // 項目のデータ型(レコード名項目の場合はTextあるいはAutoNumberのみ)
nameField.setDisplayFormat("{00000}"); // データ型がAutoNumber(自動採番)の場合は必ず指定
nameField.setLabel("レコード番号"); // 表示ラベル
nameField.setDescription("この項目はメタデータAPIから作成されました"); // 項目の説明

// カスタムオブジェクトのレコード名項目に設定
customObj.setNameField(nameField);

カスタムオブジェクトの作成要求を発行するには、MetadataBiding#create() メソッドを利用します。引数には作成するメタデータオブジェクト(カスタムオブジェクト、項目)を配列で指定します。

AsyncResult[] results = meta.create(new Metadata[] { customObj });

サーバ側で完全にデータ定義が終了するまでには時間がかかる場合があるため、APIのレスポンスはリクエストの終了を待たずに一旦非同期で返されます。このレスポンスにはリクエストの進行状況を問い合わせるためのIDが含まれています。データ定義が完了したことを保証するためには、与えられたIDを元にしてクライアントプログラムからサーバに対して定期的な問い合わせ(ポーリング)を行う必要があります。

カスタムオブジェクトの定義が完了したら、そのカスタムオブジェクトに対してカスタム項目を追加定義することができます。カスタム項目のデータ型としては、テキストや数値、日付、選択リストなども指定できますし、他オブジェクトへのリレーション(参照関係、主従関係)なども指定することができます。

// テキスト項目の作成
CustomField textField = new CustomField();
textField.setFullName("MyCustomObject__c.TextField__c"); // オブジェクトのAPI参照名 . 項目のAPI参照名
textField.setType(FieldType.Text); // データ型(テキスト)
textField.setLabel("テキスト項目"); // 表示ラベル
textField.setDescription("この項目はメタデータAPIから作成されました"); // 項目の説明

// 選択リスト項目の作成
CustomField picklistField = new CustomField();
picklistField.setFullName("MyCustomObject__c.PicklistField__c"); // オブジェクトのAPI参照名 . 項目のAPI参照名
picklistField.setType(FieldType.Picklist); // データ型(選択リスト)
picklistField.setLabel("選択リスト項目"); // 表示ラベル
picklistField.setDescription("この項目はメタデータAPIから作成されました"); // 項目の説明

Picklist picklist = new Picklist();
PicklistValue[] picklistValues = new PicklistValue[]{ new PicklistValue(), new PicklistValue() };
picklistValues[0].setLabel("男");
picklistValues[0].setValue("M");
picklistValues[1].setLabel("女");
picklistValues[1].setValue("F");
picklist.setPicklistValues(picklistValues);
picklistField.setPicklist(picklist); // 選択リストとして設定

// 参照項目(リレーション)の作成
CustomField lookupField = new CustomField();
lookupField.setFullName("MyCustomObject__c.AccountLookupField__c"); // オブジェクトのAPI参照名 . 項目のAPI参照名
lookupField.setType(FieldType.Lookup); // データ型(参照関係)
lookupField.setLabel("参照項目(取引先)"); // 表示ラベル
lookupField.setDescription("この項目はメタデータAPIから作成されました"); // 項目の説明

lookupField.setReferenceTo("Account"); // 参照するオブジェクトのAPI参照名
lookupField.setRelationshipName("MyCustomObjects"); // 子リレーションの関連名(取引先=>カスタムオブジェクト)

// カスタム項目の作成
AsyncResult[] results = meta.create(new Metadata[] { textField, picklistField, lookupField });

なお、1リクエストに指定できるメタデータオブジェクトの数はAPI仕様上は最高200個までとされていますが、少なくとも現在のところ5つほどにしておいたほうがよいようです。また、配列として同時に指定できるメタデータオブジェクトは同じ種類のものである必要があります(1リクエストの中にカスタムオブジェクトとカスタム項目を混在することはできない)。

メタデータAPIが利用できるのは現在のところDeveloper Editionのみですが、作成したメタデータオブジェクトをパッケージングしてAppExchangeにアップロードすることで他の組織に自由にコピーすることができます。そのため、実質どのSalesforce組織、Editionにおいても自由にデータベース・スキーマを作成することができます。

Apex API 10.0 Manual
http://www.salesforce.com/us/developer/docs/api/index.htm

トラックバック

このページのトラックバックURL: http://www.typepad.jp/t/trackback/7240/10277061

このページへのトラックバック一覧 メタデータAPIでDBスキーマ作成:

コメント

Posted by hinokidani on 9月 13, 2007 02:46 午後:

お世話になります。
1点質問がございます。

>サーバ側で完全にデータ定義が終了するまでには時間がかかる場合があるため、APIのレスポンスはリクエストの終了を待たずに一
>旦非同期で返されます。このレスポンスにはリクエストの進行状況を問い合わせるためのIDが含まれています。データ定義が完了した
>ことを保証するためには、与えられたIDを元にしてクライアントプログラムからサーバに対して定期的な問い合わせ(ポーリング)を行う
>必要があります。

とありますが、問い合わせには、具体的にどのAPIを使用すればよいのでしょうか?
よろしくお願いいたします。

Posted by hinokidani on 9月 13, 2007 05:58 午後:

あともう1点

カスタム項目として数値項目を作成する際、
API経由では「小数点の位置」に0がセットできない状態です。
試行した内容は以下の通りですが、実行方法に何か問題があるのでしょうか?

1)CustomFieldの定義部分をソース1のようにした場合、
「Must specify 'scale' for a CustomField of type Number」のエラーが帰ってきてしまいます。
(これは、scaleを指定しなかった場合と同じエラーです)

<ソース1>
hogeField.setFullName("MyHogeObject__c.NumberField__c");
hogeField.setType(FieldType.Number);
hogeField.setLabel("数値項目");
hogeField.setDescription("項目の説明です");
hogeField.setPrecision(new Integer(5));
hogeField.setScale(new Integer(0));

2)ソース2のように値を設定した場合、
正常に終了し、
 文字数=5
 小数点の位置=1
がそれぞれセットされます。

<ソース2>
hogeField.setFullName("MyHogeObject__c.NumberField__c");
hogeField.setType(FieldType.Number);
hogeField.setLabel("数値項目");
hogeField.setDescription("項目の説明です");
hogeField.setPrecision(new Integer(5));
hogeField.setScale(new Integer(0));

Posted by hinokidani on 9月 13, 2007 06:04 午後:

上記ソース2に誤りがありました。
正しくは↓です。

<ソース2(正)>
hogeField.setFullName("MyHogeObject__c.NumberField__c");
hogeField.setType(FieldType.Number);
hogeField.setLabel("数値項目");
hogeField.setDescription("項目の説明です");
hogeField.setPrecision(new Integer(6));
hogeField.setScale(new Integer(1));

Posted by Shinichi Tomita on 9月 13, 2007 09:31 午後:

> 問い合わせには、具体的にどのAPIを使用すればよいのでしょうか?

checkStatusメソッドを利用します。

> カスタム項目として数値項目を作成する際、
> API経由では「小数点の位置」に0がセットできない状態です。

これについては、U.S. Developer Forumに問い合わせてみます。

コメントを投稿

コメントは記事の投稿者が承認するまで表示されません。