ObjectSquare [ 2002 年 7 月号 ]

[技術情報]




今日から始めるオブジェクト指向データベース
〜教えてロバート Step.3〜

株式会社オージス総研
ES事業部PDチーム
国正@Objyチーム
with
Objyチームメンバ

・目次
10.前回までのあらすじ
11.Every object has a beautiful name ♪
 11.1.Object取得の為に(名前付け)
 11.2.スコープネーム
 11.3.ooMap
12.関連の表現方法
13.最後に

10.前回までのあらすじ

オブジェクトの広場愛読者の皆様、ちょっと間があいてしまいましたが、「今日から始めるオブジェクト指向データベース 〜教えてロバート〜 (略して『おしロバ』)」3ヶ月ぶりの連載です。
さて前回の連載では、Objectivity for Java(以下OFJ)では、こう実装すべし!ということで、JavaのクラスをObjectivity/DBに格納するために、必ず抑えておかなければならないポイントについて紹介しました( バックナンバーはこちら)。ちょっとディープな内容も混じっていましたが、ご理解いただけましたか?「うーん、やっぱりよくわからない」という貴方は、ここであきらめず、もう一度本連載を最初から繰り返し読むことをオススメします。 前回までの内容は大体理解できたという貴殿貴女のために、最後の連載となる今回では、OFJをより使いこなすための、機能を二つ(名前付け機能、リレーション)ほど紹介させていただきます。さて今回の案内ですが(関西弁が気になるという意見もありますが)、やっぱりあのお二人にお願いしましょう。



11.Every object has a beautiful name♪

◆◇ 11.1 Object取得の為に(名前付け) ◇◆
Stone(以下す)「(前回の続きで)オブジェクトをデータベースに格納するためには、OFJのAPIを利用して、トランザクションを明示的に開始して、そのトランザクションの中で、APIを利用すると・・・。なるほど、オブジェクトを格納する場合はOFJのAPIで出来ることはわかったけど、格納したオブジェクトを取り出す方法はどうやるの?やっぱりAPIを使って取り出すんだよね。」

Robert(以下ろ)「そうや。いくつか方法はあるんやけど、一番単純な方法は、すべての永続オブジェクトがもつ固有の識別子(OID)を指定して、データベースからオブジェクトを取ってくる方法やね。せやけど基本的にOIDは、Objectivity/DBが内部的に使用するもんやから、あまりこの辺を開発者やユーザには意識させたくあらへんね。ということでデータベースからのオブジェクトの取得について紹介させてもらいましょ。
今回はスコープネームの利用と、ooMapの利用について説明したらええかな。」

◆◇ 11.2 スコープネーム ◇◆
「まずはスコープネームやけど、その名の通り、ある名前空間内(スコープ)で唯一の名前をオブジェクトに付ける方法やねん。ここで名前空間っていうのは「8.1 Objectivity/DBの階層構造」で紹介したすべての階層を指定することができるんやけど、データベースやコンテナといった”入れ物”に相当するものだけやなく、永続オブジェクトも名前空間として指定することが出来るのがミソやな。」

「うーん。データベースやコンテナ内で唯一の名前をつけるっていうのは理解できるんだけど、名前空間として永続オブジェクトも指定できる!?」

「そうや。スコープネームっていうんは、言ってしまえばオブジェクトのあだ名みたいなもんやな。Stoneはんかて仲間内であだ名を付けたりしますやろ?その時に、Stoneはんだけが仲間内で使うあだ名と違うあだ名をつけて、自分の中だけで使うということもありますやろ?それと同じ事やねん。Objectivity/DBでは、このあだ名をスコープネーム、そしてそのスコープネームが有効となる範囲をネームスコープていうねん。コードで見たほうがわかりよいやろうね。」
      ・・・
      ooContObj cont;
      if (db.hasContainer("TEST_CONT")) {
        cont = db.lookupContainer("TEST_CONT");
      } else {
        cont = new ooContObj();
        db.addContainer(cont, "TEST_CONT", 1, 10, 100);
        cont = db.lookupContainer("TEST_CONT");
      }

      ObjectA a = new ObjectA();
      cont.cluster(a);

      ObjectB b = new ObjectB();
      cont.cluster(b);

      // スコープネームによる名前付け
      // ネームスコープ:コンテナ
      // スコープネーム:"SpecialNameFromContainer"
      cont.nameObj(a,"SpecialNameFromContainer");

      // スコープネームによる名前付け
      // ネームスコープ:永続オブジェクト"b"
      // スコープネーム:"SpecialNameFromObject"
      b.nameObj(a,"SpecialNameFromObject");

      ・・・
「上のコード内のnameObj()っていうメソッドが、オブジェクトにスコープネームを設定するためのメソッドやねん。たとえばA.nameObj(B,"NAME")っていうようにメソッドを呼び出すと、『Aが使うあだ名として、Bに"NAME"っていうあだ名をつけまっせ』ってことになるわけやね。せやから最初の呼び出しでは、contで参照されるコンテナから"SpecialNameFromContainer"っていう名前がaに付けられて、次の呼び出しでは、同じオブジェクトの参照aにbが"SpecialNameFromObject"っていう名前を付けてることになんねんな。」


Fig.11 スコープネーム

「そのスコープネームを使ったオブジェクトの取り出し方は次のようになんねん。」
      ・・・
      // スコープネームによるオブジェクトの取得
      // ネームスコープ:コンテナ"cont"
      // スコープネーム:"SpecialNameFromContainer"
      ObjectA a = (ObjectA)cont.lookupObj("SpecialNameFromContainer");
      ・・・

      ・・・
      // スコープネームによるオブジェクトの取得
      // ネームスコープ:永続オブジェクト"b"
      // スコープネーム:"SpecialNameFromObject"
      ObjectA a = (ObjectA)b.lookupObj("SpecialNameFromObject");
      ・・・
「なるほどねぇ。最初の例はcontがオブジェクトaに"SpecialNameFromContainer"って付けたわけだから、コンテナの参照contからその名前を使ってオブジェクトaを取り出していると。で、次の例は同じようにObjectBの参照bから"SpecialNameFromObject"を使って、オブジェクトaを取り出していると。」

「せや。contに対して"SpecialNameFromObject"を使って呼び出しても、オブジェクトaは取り出せへんわけやね。」

◆◇ 11.3 ooMap ◇◆
「オブジェクトに名前を付けて管理する、もうひとつの方法が、ooMapを利用する方法やねん。ooMapっていう名前からも想像できるかも知れへんけど、キーと値を組としてオブジェクトを管理する方法やねん。実際には、キーとしてStringクラス、値としてObjectクラスが要求されんねん。ooMap自体永続化可能なクラスになってて、実際にオブジェクトをooMapに登録する前に、他の永続オブジェクトと同じように、データベースにクラスタリングしておく必要がある点は注意が必要やね。」
      ・・・
      ooContObj cont;
      if (db.hasContainer("TEST_CONT")) {
        cont = db.lookupContainer("TEST_CONT");
      } else {
        cont = new ooContObj();
        db.addContainer(cont, "TEST_CONT", 1, 10, 100);
        cont = db.lookupContainer("TEST_CONT");
      }

      // ooMapオブジェクトの生成とクラスタリング
      ooMap map = new ooMap();
      cont.cluster(map);

      ObjectA a = new ObjectA();
      cont.cluster(a);

      ooMapにオブジェクトaを登録(名前は"ObjectA")
      map.add(a,"ObjectA");

      ・・・
「ちなみに、上の例やと、オブジェクトaをクラスタリングしてからooMapに登録しているけど、オブジェクトaのクラスタリングを忘れてもエラーにはならへんねん。なんでかというと、ooMapがクラスタリングされているコンテナに、暗黙的にクラスタリングされるようになってんねんな。この辺をうまく設計して、ooMapを格納するコンテナと、ooMapに登録されるオブジェクトを格納するコンテナを分けるように設計しておくと、例えば以下のような場合に、並行性の向上に役立ったりすんねん。」


Fig.13-1 ooMapによる並行性の向上 1


Fig.13-2 ooMapによる並行性の向上 2


Fig.13-3 ooMapによる並行性の向上 3
「Process1もProcess2も、同じooMapに登録されているオブジェクトを更新するような状況やけど、ooMapは更新されるわけではなく、検索のために参照されるだけやから、必要となるロックはリードロックでOKで、さらに複数のトランザクションからのリードロックの要求は、競合しない(*1)から、Process1がリードロックを取得してても、Process2がooMapを利用した検索を行うことができんねん。
そしてオブジェクトの取得の方法はこんな感じ」
      ・・・
      ObjectA a = (ObjectA)map.lookup("ObjectA");
      ・・・
「他にも、lookup()メソッドやなくて、elements()メソッドを呼び出すことによって、そのooMapに登録されているすべてのオブジェクトを走査するような、java.util.Iteratorの実装クラスであるcom.objy.db.app.Iteratorを返すこともできんねん。」
      ・・・
      Iterator ite = map.elements();
      ・・・


「Iteratorを利用できるのは使える機能だね。そう考えると、スコープネームを使うよりooMapを使いなさいって事になるのかな?スコープネームを使うメリットというのは無いの?」

「もちろんあるで。ほな、お互いのメリットとデメリットをまとめて、名前付けについては終わりにしましょ」

メリット   デメリット
スコープネーム ・名前空間の範囲を広く指定することができる(フェデレートデータベース、データベース、コンテナ、ベーシックオブジェクト)
・検索速度は最も速い
・スコープネームをつけるオブジェクトの数に限界がある(スコープネームを格納しておく領域が固定サイズである為)
ooMap ・ooMapの格納先を任意に設定することが出来る
・登録するオブジェクトが増えた場合の、ooMap拡張時のパラメータを、ユーザがカスタマイズすることが出来る
・オブジェクトの検索にあたり、前もってooMapを取得しておく必要がある
(*1)
Objectivity/DBでは、ロックの種類としてリードロックアップデートロックの2種類があります。 一つのコンテナに、複数のリードロックの取得が要求されても競合は発生しませんが、複数のアップデートロック、アップデートロックとリードロックの取得、が同時に要求されるとロック要求の衝突が発生します。
また、Objectivity/DBでは、最新の状態である保証は無いものの、アップデートロックが取得されているコンテナの情報を取得するMROWリードロックと呼ばれる特殊なロック機能も提供しております。

12.関連の表現方法
「名前付けについての理解ができたら、もうObjectivity/DBを使って簡単なアプリケーションを作れるレベルに達してるねん。トランザクションの制御と、データの格納、取得の方法が分かってるわけやからね。」

「へぇー、でもそうかもね。なんとなくObjectivity/DBを使えそうな気がしてきたよ。」

「最後に、これぞオブジェクト指向データベースという機能を紹介しときましょか。Stoneはんの作ったモデルでも、オブジェクト間に関連が張られてますやろ?そのオブジェクト間の関連を表現するためにリレーションというクラスが用意されてんねん。」

「ん?そういえば最初の方で言ってたよね。関連を表現するための便利なクラスがあるって(
連載2回目「関連や継承」をご参照ください)。」

「せや。関連を表現するためには、普通のオブジェクトの参照や、参照の配列でもええんやけど、リレーションクラスを利用すると、こんなええことがあるで。」 「で、リレーションクラスなんやけど、いくつか種類があって、多重度に関して"一対一","一対多","多対一","多対多"の4種類があって、さらに方向性として"単方向","双方向"に分けることができんねん。適用するモデルに応じて、最適なリレーションを選択してもらえればいいねん。例として駄菓子屋のモデルの注文と注文明細間の関連を、単方向の一対多のリレーションクラスを使って置き換えてみましょか。」
// --- PersistentOrder.java ---
import com.objy.db.app.*;

public class PersistentOrder extends ooObj{

  // リレーションフィールドの宣言
  private ToManyRelationship details;

  public PersistentOrder(){
  }

  // リレーション定義メソッドの定義
  public static OneToMany details_Relationship(){
    return new OneToMany(
      "details",                // 対象リレーションのフィールド名
      "PersistentDetail",       // リレーション先クラス名
      Relationship.INLINE_NONE  // インラインモードの指定
    );
  }

  // リレーション先にPersistentDetailオブジェクトを追加
  public void addDetails(PersistentDetail detail){
    markModified();
    details.add(detail);
  }
}
「リレーションを利用するために必要な変更を見ていくと・・・」

// リレーションフィールドの宣言 private ToManyRelationship details;
対多リレーションの宣言。ちなみに対一リレーションやと、型はToOneRelationshipになるで。


  // リレーション定義メソッドの定義
  public static OneToMany details_Relationship(){
    return new OneToMany(
      "details",                // 対象リレーションのフィールド名
      "PersistentDetail",       // リレーション先クラス名
      Relationship.INLINE_NONE  // インラインモードの指定
    );
  }
リレーションを利用する場合は、このリレーション定義メソッドを、ユーザが定義せなあかんねん。返り値の型はこの例では一対多やからOneToManyやけど、一対一、多対一、多対多はそれぞれOneToOne,ManyToOne,ManyToManyになるで。それでメソッド名は、 リレーションフィールド名 + "_Relationship"という形式に決まっているから注意してや。
メソッド中で生成しているクラスの引数の意味は、コメントにある通りやけど、3つ目の引数はリレーションをインラインで定義するか、ノンインラインで定義するかの指定やねん。基本的にインラインを指定すると、オブジェクトの内部にリレーション用の領域を確保するから、検索のパフォーマンスは良くなるけど、その分オブジェクトのサイズが大きくなるねん。インラインを指定せえへんかったら、リレーション用に用意されているオブジェクトの外の領域に情報が格納されんねんな。」


  // リレーション先にPersistentDetailオブジェクトを追加
  public void addDetails(PersistentDetail detail){
    markModified();
    details.add(detail);
  }
リレーションにオブジェクトを追加するところやな。対一の場合と、対多の場合では、呼び出すメソッドが違うから注意が必要やで。

後、簡単にやけど、削除の伝播機能と、検索機能について紹介するで。 まず削除の伝播機能やけど、これはリレーション元のオブジェクトが削除された時に、リレーション先のオブジェクトも自動的に削除してくれる機能やねん。先に紹介したリレーション定義メソッド内でOneToManyクラスのコンストラクタを呼び出してるけど、その引数として削除の伝播を許可するように指定することで、その機能がリレーションに追加されんねん。たとえば、車クラスとエンジンクラスのようなライフサイクルが同じ複合オブジェクトを表現するときなんかは便利やね。

で、検索機能やけど、これは対多のリレーションが提供している機能で、リレーション先のオブジェクトをcom.objy.db.app.Iteratorクラスで取り出す機能やねん。」
      ・・・
      Iterator ite = details.scan();
      ・・・
「上の例の様に、scan()メソッドを、引数なしで呼び出すと、リレーション先の全てのオブジェクトを走査するようなIteratorを取得することができんねん。で、さらにscan()メソッドの引数として、条件検索をするための文字列を与えてやる事によって、対象のオブジェクトを絞り込むこともできんねんな。」
      ・・・
      // 条件検索の例
      // 例えば、PersistentDetailクラスに、idというString型 の属性が定義されており、
      // それが"111"から始まる値を持つオブジェクトを検索する場合
      Iterator ite = details.scan("id =~ \"111.*\"");
      ・・・

13.最後に

「・・・ちゅーことで、Objectivity/DB、そしてOFJの仕組みや使い方なんかの紹介をさせてもらいましたけど、結構簡単でっしゃろ?」

「そうだね。データベースを扱うって言うと、ちょっと心構えが必要な感じがするけど、Javaが触れればOKっていうのがいいよね。ちょっとした開発で、一人でデータベース周りとか、画面周りを作ってしまうような時なんかの時は、意外とODBの方が手軽なのかもね。もちろん使い方を知っていればだけど。」

「(あーこの人、自分が言いたかったこと全部言ってもうてるわ)そうやね。小さな開発から、大きな開発まで、オブジェクト指向がこれだけの広まりを見せてる今だからこそ、オブジェクト指向データベースをマスターするチャンスでっせ!!」

「そうでっせ!!」
『今日からはじめるオブジェクト指向データベース 〜教えてロバート〜』いかがでしたでしょうか?「和菓子屋の設定はどうなったの?」「Robertって誰?」と疑問をお持ちの皆様。ええ、何事にも仕様変更というものはつきものでして・・・。最後にRobert氏も言ってましたが、オブジェクト指向技術そのものや、JavaやXMLといったオブジェクト指向関連技術がこれだけの広まりを見せている今だからこそ、皆様もオブジェクト指向データベースにトライしてみませんか?
3回という短い連載でしたが、お付き合いありがとうございました。

© 2002 OGIS-RI Co., Ltd.
Prev Index
Prev. Index