推奨事項: リストの利用

@OneToMany または @ManyToMany のコレクションプロパティのマッピングには、リストを使用することを推奨します。

つまり、Set 使用時に発生する、暗黙的な hashCode() / equals() の使用を回避できます。

リストとセット

Hibernate bag 意味論

Hibernate を使用する場合は、Set と List は Hibernate で異なる意味論を持つため、Set を使用する方が適している場合があります。Set は「bag 意味論」を使用し、Hibernate ではしばしば優先されます。

hashCode() / equals()

Set を使用すると暗黙的に hashCode()/equals() の実装が使用されることになります。これは、エンティティビーンは変化可能で、ビーンが永続化されるまでしばしば @Id の値を持たないという性質があるため、問題が生じます。

エンティティビーンで hashCode()/equals() の実装が難しいということは、List@OneToMany および @ManyToMany コレクションを保持するための推奨コレクションタイプであることを意味します。

@Entity
@Table(name="customer")
public class Customer extends BaseModel {
  ...

  // List is recommended for collection types

  @OneToMany(mappedBy="customer", cascade=CascadeType.PERSIST)
  List<Contact> contacts;
  ...

強化

コレクションタイプを定義すると、強化により次のことが保証されます。

  • すべてのリスト/セット初期化が削除される
  • リスト/セットは常に Ebean により初期化される (null にならない)

リスト/セット初期化が削除される

// initialisation of the new ArrayList() is removed

@OneToMany(mappedBy="customer")
List<Contact> contacts = new ArrayList<Contact>;

// you can declare an un-initialised List if you wish
// and there is no actual difference to an initialised one
// because enhancement will always initialise it

@OneToMany(mappedBy="customer")
List<Contact> contacts;

Kotlin では、通常は null にならないように定義する必要があります。

// kotlin: contacts type not nullable
@OneToMany(mappedBy = "customer")
var contacts: MutableList<Contact> = ArrayList()

リスト/セットは常に初期化される (null にならない)

Ebean は、次のものをサポートするためにリスト/セットの初期化を制御する必要があります。

  • 遅延ロード
  • リスト/セットが削除を認識する必要がある @PrivateOwned のサポート
  • リスト/セットが追加と削除を認識する必要がある @ManyToMany のサポート

強化により、リスト/セットにアクセスするたびに Ebean が常に初期化し、必要に応じて追加/削除をリッスンするようにリスト/セットをセットアップします (@PrivateOwned および @ManyToMany)。

これにより、リスト/セットへのアクセスは常に null になりません。

// it "looks" like contacts could be null ...

@OneToMany(mappedBy="customer")
List<Contact> contacts;

public void addContact(Contact contact) {
  // but actually contacts will never be null here
  if (contacts == null) {
    contacts = new ArrayList<>();
  }
  contacts.add(contact);
}

強化は 永続コレクション のすべての GETFIELD 命令を置き換え、必要に応じてリスト/セットが初期化されるようにするコードに置き換えます。

@OneToMany(mappedBy="customer")
List<Contact> contacts;

public void addContact(Contact contact) {
  // this is safe to write as contacts
  // will never be null here
  contacts.add(contact);
}