双方向の一対多の所有関係オブジェクトの実装方法

この資料では双方向の一対多の所有関係 (Bidirectional Owned One-to-Many Relationship) を実装する方法を示します。 まだ読んでいないのでしたら、先に双方向の一対一の所有関係のページを読むと理解しやすいでしょう。

双方向の一対多所有関係の実装

一対一のときと同様に、車 (Car) とドア (Door) の例を取り上げます。親となる Car オブジェクトに、複数の Door が子供として関連付けされることを考えます。 具体的には Door を要素としてもつコレクションを親が保持します。

また、双方向とすべく子オブジェクトである Door 側には、親オブジェクトへの参照が含まれます。

親側の実装方法

では、親オブジェクトとなる Car クラスからみてみましょう。

import java.util.ArrayList;
import java.util.List;
import javax.jdo.annotations.Element;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import com.google.appengine.api.datastore.Key;

@PersistenceCapable
public class Car {
  @PrimaryKey
  @Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)
  private Key key;
  
  @Persistent
  private String name;
  
  @Persistent(mappedBy="car", defaultFetchGroup="true")
  @Element(dependent="true")
  private List<Door> doors;
  
  public Car(String name){
    this.name = name;
    this.doors = new ArrayList<Door>();
  }
  
  public Key getKey(){
    return key;
  }
  
  public String getName(){
    return name;
  }
  
  public List<Door> getDoors(){
    return doors;
  }
  
  public void addDoor(String doorColor){
    doors.add(new Door(doorColor, this));
  }
}

ポイントは黄色でマークした箇所です。ひとつひとつ見ていきます。

まず、複数の Door オブジェクトを保持するために、Door 型のコレクション (List) のフィールドにしています。 これで子オブジェクトを多数保持できるようになります。 通常一対多という場合は、こうしたコレクションを保持することを指し、異なる型のオブジェクトを異なるフィールドにひとつずつ保持することではありません。

一対一の場合と似てるが微妙に違うので要注意!

「子コレクションの要素は親に依存している」、すなわち "親を削除したときに子供の要素も削除する" ということを示すために、 @Element(dependent="true") を指定します。

一対一の関係の場合では、依存関係を示すのは @Persistent の dependent 属性でしたが、一対多の場合はこのように @Element を利用するので注意してください。

尚、コレクション利用時は Google App Engine でインデックス作成に時間がかかる場合があります。 特に "The index for this query is not ready to serve." というエラー は 少し利用可能になるまで時間がかかりますので注意してください。

また、「双方向」とするための @Persistent の mappedBy 属性は親オブジェクトに指定します。これも一対一の時と違いますね。 一対一の関係の場合は子オブジェクト側に mappedBy 属性を指定しましたが、一対多の場合は親側に指定します。

子側の実装

次に子オブジェクト側の実装方法をみてみます。

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import com.google.appengine.api.datastore.Key;

@PersistenceCapable
public class Door {
  @PrimaryKey
  @Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)
  private Key key;
  
  @Persistent
  private String color;
  
  @Persistent
  private Car car;
  
  public Door(String color, Car car){
    this.color = color;
    this.car = car;
  }
  
  public Key getKey(){
    return key;
  }
  
  public String getColor(){
    return color;
  }
  
  public Car getCar(){
    return car;
  }
}

こちらは一対一の場合と比べて、mappedBy 属性の指定がなくなるなど、よりシンプルになっているだけです。

この資料では、双方向一対多所有関係のオブジェクト定義方法について例を示しつつ説明しました。