JPA と DDD の関係で僕が思っていること

## このスライドについて
このスライドは、 [JJUG CCC 2016 Fall](http://www.java-users.jp/ccc2016fall/) でお話ししたときに使用したスライドです。

---

## 自己紹介
<img src="https://qiita-image-store.s3.amazonaws.com/0/28302/63d28421-104b-3066-2712-768e6aded5f7.jpeg" width="200px" class="pull-right"/>

- opengl-8080
- 主に Qiita で技術メモを書いたり
- 関西の SIer 勤務

---
## 今日話すこと
- JPA と DDD の関係について思っていること
- JPA で DDD のパターンを実装するとどうなるか

---
## JPAとDDDの関係で思っていること
- 最初は、 JPA に対してあまり良いイメージはなかった
- DDD を学ぶにつれて、徐々にイメージが変わっていった
- なぜ変わっていったのか、どう変わっていったのか

---
## JPAでDDDのパターンを実装
- エンティティ・値オブジェクトなどを JPA で実装する
- 仕様上の限界、実装ごとの現実

---
## JPAとDDDの関係で思っていること

---
## JPA へのイメージの変化
- DB アクセスライブラリ[^1]との出会い
    - DB アクセスってこうやるのかぁ
- JPA との出会い
    - JPA 使いづらいなぁ
- DDD との出会い
    - あれ、 JPA って。。。

[^1]: 「O/Rマッパー」と書くと厳密な定義とかでいろいろ面倒なマサカリが来そうなので、DB アクセスライブラリと濁すなど

---
## DB アクセスライブラリとの出会い
- 新人の頃に関わった案件で、初めて DB アクセスライブラリと出会う
- JDBC を軽くラップした感じのシンプルなライブラリ
    - SQL は直接記述
    - １レコードを１オブジェクトにマッピング
    - カラム名とフィールド名を一致させて自動マッピング
        - 一致させられない場合はアノテーションで明示
    - 基本型（プリミティブ型, `String`, `Date` など）はそのまま使用

```java:イメージ
// テーブルとマッピングするためのクラス
public class Foo {
  private Long id;
  private String value;
  @ColumnMapping("user_name")
  private String userName;

  // Getter, Setter
}

// 検索
String sql = "select * from foo_table where id=?";
Foo foo = dbAccessLibrary.find(Foo.class, sql, 1L);
```

---
## DB アクセスライブラリの使われ方
- テーブルに対応するオブジェクトはデータの入れ物
    - フィールドと Getter, Setter のみ
    - Manager, Service と呼ばれるクラス達が値を Get してビジネスロジックを実現
- テーブル１つに対して、次の２つのクラスを作る
    - 入れ物クラス
    - DB アクセス用クラス（Dao）
- クラス間に関連はない
    - DB 上外部参照しているテーブルの ID を持ち、その値を取得して明示的に検索する
- 変更を DB に反映する場合は、テーブルごとの Dao を使って明示的に `update()`, `delete()` を呼ぶ


![最初のDBアクセスライブラリ.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/c71dc9a4-04bb-ad3e-855f-8695c64ffa79.jpeg)


```java:外部参照を辿って明示的に検索
Long barId = foo.getBarId();
Bar bar = barDao.findById(barId);
```

```java:テーブルごとに変更を明示的に反映する
foo.setValue("xxxx");
fooDao.update(foo);

Bar bar = barDao.findById(foo.getBarId());
bar.setName("yyyy");
barDao.update(bar);
```

---
## 当時の私「DB アクセスってこうするのかぁ」

---
## 最初に覚えた DB アクセスのイメージ
- １テーブル、１クラス
- １レコード、１オブジェクト
- フィールドの型は基本型（プリミティブ型、 `String`, `Date` など）
- テーブルごとに Dao を作る
- DB に変更を反映する場合は、プログラマが明示的に記述する
- SQL は直接書く

---
## JPA との出会い
- Java EE の勉強をしていて JPA に出会う
- DB アクセスの標準仕様ということで勉強を開始


---
## JPA の第一印象
- 仕様が大きくて、覚えることが多い
- 個々の仕様も、難解だったり複雑な印象

---
## 大量のアノテーション
- `@Entity`
- `@Table`
- `@Column`
- `@Id`
- `@GeneratedValue`
- `@JoinColumn`
- `@JoinTable`
- `@ElementCollection`
- `@CollectionTable`
- `@OneToOne`
- `@OneToMany`
- `@ManyToMany`
- `@ManyToOne`
- `@Embeddable`
- `@Embedded`
- `@EmbeddableId`
- etc...

---
## (；´д｀)ぇぇ・・・<br><span class="small">名前のマッピングくらいでいいんじゃないの...？<span>

---
## 複雑な Entity のライフサイクル

```
                    ○      ◎
                new |      ^ GC
                    v      |
                   +---------+
                   |   NEW   | <--------------+
                   +---------+                |
   find/JPQL             |                    |
○----------------------+ |                    |
                       | | persist            |
                       v v                    |
+---------+  merge   +---------+  remove   +---------+
|         | -------> |         | --------> |         |
|DETACHED | <------- | MANAGED |           | REMOVED |
|         |  clear   |         | <-+       |         |
+---------+  detach  +---------+   |       +---------+
                       ^    |      |
                       |    +------+
                       |      refresh
                       |      setter
                       v
                    +-------------+
                    +-------------+
                    |   Database  |
                    +-------------+
```

[JPAエンティティのライフサイクルをテキストで書いてみた by making@github - Qiita ](http://qiita.com/making@github/items/f87a21f9e5a5d6544138)

※表記を英語にしています

---
## (；´д｀) 複雑...<br><span class="small">今まではそんなのなかったのに...</span>

---
## JPQL
```sql
    SELECT bar.id
      FROM Foo foo
INNER JOIN foo.barList bar
     WHERE foo.id = :id
```

SQL とは似て非なる、独自のクエリ言語

---
## (；´д｀) え、新しい言語覚えるの？！<br><span class="small">SQL 書かせてよ...</span>

---
## DB に更新結果を反映する方法
- 明示的な UPDATE は行わない
- インスタンスのフィールドを書き換えると、自動的に変更内容が DB に反映される
- CASCADE を指定すれば、関連する Entity の登録・削除が連動する

---
## (；´д｀) なにそれこわい<br><span class="small">なんでそんなことを...</span>

---
## 最初に覚えた DB アクセスのイメージ
- １テーブル、１クラス
- １レコード、１オブジェクト
- フィールドの型は基本型（プリミティブ型、 `String`, `Date` など）
- テーブルごとに Dao を作る
- DB に変更を反映する場合は、プログラマが明示的に記述する
- SQL は直接書く

※再掲

---
## これまでと全然違う！

---
## JPA に対する初期のイメージ
- 当然のように「使いづらい」と感じる
- それでも「標準仕様だからと覚えておこう」という後ろ向きな姿勢
- もっとシンプルな仕様を追加して、選択できるようにして欲しい

---
## DDD との出会い
- [ドメインモデル貧血症](http://bliki-ja.github.io/AnemicDomainModel/)を知る
    - ビジネスロジックを持たないクラス
    - Getter, Setter だけのデータクラス
    - あれ...見覚えがあるぞ...？
- 現状に疑問を持ち始める
- オブジェクト指向についての情報を集めていたら DDD と出会う
- Domain Driven Design (ドメイン駆動設計)

---
## DDD で出会ったパターン
- エンティティ
- 値オブジェクト
- 集約
- リポジトリ

---
## エンティティ
- ドメインをモデリングしていると、「<font color="red">属性</font>」ではなく「<font color="blue">同一性</font>」で識別するオブジェクトがあることに気付く
- エンティティは<font color="red">属性</font>が変化しても<font color="blue">同一性</font>は変わらない
    - 人は身長や名前などの<font color="red">属性</font>が変化しても、「その人」であるという<font color="blue">同一性</font>は変わらない
- DDD では、そのようなオブジェクトを**<font color="blue">エンティティ</font>**と呼び、他のオブジェクトと区別する
- 同一性を識別するための仕組みが必要になる
    - よくあるのは一意な連番を割り当てる

---
## 値オブジェクト
- エンティティとは逆に、同一性を<font color="red">持たない</font>オブジェクト
- 属性が一致していれば同じ物として扱える
- 例：金額、数量、名前、etc...
- システムが対象とする領域（ドメイン）を記述する基本的な語彙をオブジェクトで表現する
- インスタンスを共有できるようにするため、不変に設計することが望ましい

---
## 集約
![集約.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/261f668f-3a69-de15-d12f-bcf7db89dac0.jpeg)

- １つのエンティティをルートとする、オブジェクト<span class="small">（エンティティと値オブジェクト）</span>の論理的なまとまり
- 集約内部へのアクセスはルートエンティティを介してのみ行えるようにする
    - 集約内の不変条件[^2]を守る
    - 誤ったデータアクセス・変更を防ぐ
    - 集約内の変更を１回の操作（ルートエンティティのメソッド呼び出し）で完結させる
- 一種のカプセル化
- 集約内のオブジェクトは、一括で登録・削除されなければならない


[^2]: 常に満たされなければならない条件・ビジネスルール。例えば、「限度額を超えて買い物かごに商品を入れられない」、とか。

---
## リポジトリ
- 永続化層とのやり取りを抽象化するクラス
- ルートエンティティを取得するための手段を提供する
    - ルートエンティティ以外のオブジェクトは、ルートエンティティを介して取得する（集約）
- ドメインから永続化層の関心事を切り離す

---
## JPA は DDD を意識している...？
- エンティティと `@Entity`
- 値オブジェクトと `@Embeddable`
- ルートエンティティから関連を辿る
- 集約とリポジトリ、 JPA のユニットオブワーク

### <span class="small">というよりも、 JPA と DDD の両方がオブジェクト指向的なプログラミングスタイルを意識しているから一致しているように見える？</span>

---
## エンティティと `@Entity`
### DDD のエンティティ
- 同一性を実現するため、一意に識別するための方法を定義する

### JPA の Entity
- `@Id` などのアノテーションで一意となる項目を定義する

> Every entity must have a primary key.[^3]
> 
> （訳）
> 全ての Entity は、一意なキーを持たなければならない。

[^3]: JPA 2.1 仕様書 P.29 「2.4 Primary Keys and Entity Identity」

---
## 値オブジェクトと `@Embeddable`
### DDD の値オブジェクト
- 同一性を持たない
- 基本型をラップしてドメインの基本的な語彙を表現する
- 他の値オブジェクトやエンティティと関連を持てる

### JPA の `@Embeddable` （埋め込み可能クラス）
- 一意なキーは定義しない

> Instances of these classes, unlike entity instances, do not have persistent identity of their own.[^4]
> 
> （訳）
> これら（埋め込み可能クラス）のインスタンスは、 Entity のインスタンスとは異なり、永続化のための一意なキーを持ちません。

- 基本型をラップしたオブジェクトを定義できる
- 他の埋め込み可能クラスや Entity と関連を持てる

> An embeddable class may be used to represent the state of another embeddable class.
> An embeddable class may contain a relationship to an entity or collection of entities.[^4]
> 
> （訳）
> 埋め込み可能クラスは、他の埋め込み可能クラスの状態を表現するために使用できる。
> 埋め込み可能クラスには、 Entity や Entity のコレクションの関連が含まれることがある。

[^4]: JPA 2.1 仕様書 P.40「2.5 Embeddable Classes」

---
## ルートエンティティから関連を辿る
### DDD の関連
- 集約のルートエンティティから、オブジェクトの関連を辿って必要なオブジェクトにアクセスする

### JPA の関連
- アノテーションで Entity 間の関連を定義
    - `@OneToOne`, `@OneToMany`, `@ManyToOne`, `@ManyToOne`, `@ManyToMany`
- DB とのマッピングもアノテーションで宣言
    - `@JoinTable`, `@JoinColumn`
- 各種定義情報に従って JPA が自動で関連する Entity を取得していく
    - クライアントは、普通にオブジェクトのフィールドにアクセスするだけ
    - DB アクセスは JPA が隠蔽する

---
## 集約とユニットオブワーク
### DDD の集約
- エンティティをルートとしたオブジェクトのまとまり
- リポジトリからルートエンティティを取得し、ビジネスロジックを実行して状態を変更させる
- ビジネスルールなどは集約内部のエンティティや値オブジェクトがチェックする

**これを冒頭で紹介したようなシンプルな DB アクセスライブラリで実装しようとしたときに、手が止まった―――**

---
## 集約とリポジトリをシンプルな DB アクセスライブラリで実現する
![シンプルなDBアクセスライブラリでリポジトリを実装する.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/e144d5de-19b5-48aa-abf7-79320775814c.jpeg)

```java:例えばこんな感じになる？
// エンティティ
public class Foo {
    private Long id;
    private String value;
    private Bar bar;
    // ビジネスロジック...
}

public class Bar {
    private Long id;
    private String value;
    // ビジネスロジック...
}

// テーブルとマッピングするためのクラス
class FooTable {
    Long id;
    String value;
    Long barId;
}

class BarTable {
    Long id;
    String value;
}

public class FooRepository {
    // JDBC をラップした架空の DB アクセスライブラリ
    private MyJdbcWrapper jdbc;

    // 検索
    public Foo find(Long id) {
        // Foo と
        FooTable fooTable = this.jdbc.select(
            FooTable.class,
            "SELECT * FROM FOO_TABLE WHERE ID=?",
            id
        );

        // Bar のデータを検索して
        BarTable barTable = this.jdbc.select(
            BarTable.class,
            "SELECT * FROM BAR_TABLE WHERE ID=?",
            fooTable.barId
        );

        // Entity に詰めて返す
        Bar bar = new Bar(barTable.id, barTable.value);
        return new Foo(fooTable.id, fooTable.value, bar);
    }

    // 登録
    public void persist(Foo foo) {
        // 先に Bar を登録して ID を採番し
        BarTable barTable = new BarTable();
        barTable.value = foo.getBar().getValue();

        this.jdbc.insert(barTable);
        foo.getBar().setId(barTable.id);

        // 採番された Bar の ID を使って Foo を登録
        FooTable fooTable = new FooTable();
        fooTable.value = foo.getValue();
        fooTable.barId = barTable.id;

        this.jdbc.insert(fooTable);
        foo.setId(fooTable.id);
    }

    // 更新
    public void update(Foo foo) {
        // Foo と
        FooTable fooTable = new FooTable();
        fooTable.id = foo.getId();
        fooTable.value = foo.getValue();
        fooTable.barId = foo.getBar().getId();

        this.jdbc.update(fooTable);

        // Bar の情報を Entity の値で更新
        BarTable barTable = new BarTable();
        barTable.id = foo.getBar().getId();
        barTable.value = foo.getBar().getValue();

        this.jdbc.update(barTable);
    }

    // 削除
    public void remove(Foo foo) {
        // 外部参照の都合上、先に Bar を消して、
        BarTable barTable = new BarTable();
        barTable.id = foo.getBar().getId();
        
        this.jdbc.delete(barTable);

        // 次に Foo を消す
        FooTable fooTable = new FooTable();
        fooTable.id = foo.getId();

        this.jdbc.delete(fooTable);
    }
}
```

---
## 集約の一部を更新することの難しさ
前述の実装には問題がある。

```java:再掲
public void update(Foo foo) {
    // Foo と
    FooTable fooTable = new FooTable();
    fooTable.id = foo.getId();
    fooTable.value = foo.getValue();
    fooTable.barId = foo.getBar().getId();

    this.jdbc.update(fooTable);

    // Bar の情報を Entity の値で更新
    BarTable barTable = new BarTable();
    barTable.id = foo.getBar().getId();
    barTable.value = foo.getBar().getValue();

    this.jdbc.update(barTable);
}
```

### 問題点
`bar` が別のインスタンスに差し変わる可能性がある場合、このままだとダメ

![集約の一部を変更することの難しさ.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/8bfce544-6cd8-a1b6-31ec-8893c5914fd9.jpeg)

- 集約のルート（`Foo`）は更新でも、集約の内部（`Bar`）は単純な更新ではない可能性がある
- 関連が `bar` のコレクションになると、さらに複雑
    - 一部の要素が削除、一部の要素は更新、一部の要素は登録
- `bar` がさらに別のオブジェクトと関連を持っていたら...
- いずれにしても、**更新前の状態がどうたったか**という情報が必要になる
    - 「更新前の状態」と「更新後の状態」を比較し、必要な処理を決定しなければならない

---
## 解決方法を色々考えた...（１）
変更前の `foo` と変更情報を持ったオブジェクトをリポジトリに渡す？

```java
public void update(Foo foo, UpdateData data) {
  Foo before = this.deepCopy(foo);
  foo.callBusinessLogic(/* data から必要な情報を渡す */);
  // before と foo を比較して、必要な更新処理を実行
}
```

ビジネスロジックの実行がリポジトリの中になって不自然...

---
## 解決方法を色々考えた...（２）
変更前の情報は DB から再検索する？
<span class="small">※排他ロックされている前提</span>

```java
public void update(Foo after) {
    Foo before = this.find(after.getId());
    // before と after を比較して、必要な更新処理を実行
}
```

二度手間感...
でもやむを得ない...？

---
## 解決方法を色々考えた...（３）
検索した時点の変更前のインスタンスのコピーをキャッシュしておいて、変更後のインスタンスと差分を取る？

```java
public Foo find(Long id) {
    Foo foo = ...;
    FooRepository.putCache(id, this.deepCopy(foo));
    return foo;
}

public void update(Foo after) {
    Foo before = FooRepository.getCache(after.getId());
    // before と after を比較して、必要な更新処理を実行
}
```

え...それって JPA がやってることを自力で実装してるやん...

---
## JPA のユニットオブワーク
- Entity のインスタンスは `EntityManager` によってライフサイクルが管理される
- Managed 状態になったインスタンスは、トランザクションがコミットされるときにインスタンスに加えた変更が自動的に DB に反映される

```java:JPAでリポジトリを実装した場合
public class FooRepository {
    private EntityManager em; // DI などで注入

    public Foo find(Long id) {
        return this.em.find(Foo.class, id);
    }

    public void persist(Foo foo) {
        this.em.persist(foo);
    }

    public void remove(Foo foo) {
        this.em.remove(foo);
    }
}
```

- `find(Long)` で検索した `Foo` のインスタンスは `EntityManager` によって管理されている
- `Foo` のインスタンスへの変更は JPA が自動的に検出して DB に反映するので、 `update()` はそもそも不要になる
- あたかもエンティティのインスタンスをメモリ上に保持したコレクションであるかのように、リポジトリが振る舞う
    - リポジトリの実装をただの `Map` の実装に差し替えられる

```java:コレクションでCRUD
// 登録
Map<Long, Foo> map = new HashMap<>();
map.put(1L, foo);

// 検索
Foo foo = map.get(1L);

// 更新
foo.setValue("xxx");
foo.getBar().setValue("yyy");

// 削除
map.remove(foo);
```

```java:リポジトリでCRUD
// 登録
repository.persist(foo);

// 検索
Foo foo = repository.find(1L);

// 更新
foo.setValue("xxx");
foo.getBar().setValue("yyy");

// 削除
repository.remove(foo);
```

> グローバルアクセスを必要とするオブジェクトの各型に対して、あるオブジェクトを生成し、その型のすべてのオブジェクトで構成されるコレクションが、**メモリ上にあると錯覚させることができるようにすること**。[^5]

[^5]: エリックエヴァンスのドメイン駆動設計 P.151 リポジトリ

---
## JPA に対するイメージの変化
- 最初のイメージ
    - <font color="red">重厚で複雑で使いにくい</font>
- 最近のイメージ
    - <font color="blue">オブジェクト指向に徹底的に振り切っている</font>
    - データベースに関する関心事がクライアントの実装に漏れないように徹底している
    - 大量で複雑なアノテーション
        - DB とオブジェクトとの変換ルールをすべてアノテーションで表現できるようにした結果...？
        - 変換ルールさえ定義しておけば、あとは JPA が裏で勝手にやる
    - JPQL
        - オブジェクトモデルに対してクエリを実行できるようにしている
    - ユニットオブワーク
        - DB を隠蔽し、メモリ上のオブジェクトを操作しているかのような感覚

---
## 前半まとめ
### JPA に対するイメージの変化
- DB アクセスライブラリとの出会い
    - データ中心で非オブジェクト指向
    - DB アクセスライブラリは、 JDBC のシンプルなラッパー程度の認識
- JPA との出会い
    - それまでの DB アクセスライブラリのイメージを覆す、複雑で重厚な仕様
    - 使いづらいイメージ
- DDD との出会い
    - 非オブジェクト指向スタイルなイメージで JPA を見ていたため、使いづらいと誤解していた<span class="small">（と思ってる） </span>
    - オブジェクト指向を徹底しようとすると、 JPA の機能が力を発揮する<span class="small">（と期待している） </span>

---
## JPA で DDD のパターンを実装するとどうなるか（後半）
- DDD で紹介されているいくつかのパターンを JPA で実装する
    - エンティティ
    - 値オブジェクト
    - 集約
- JPA の仕様上、どうしても理想通りできない部分がある
    - JPA の標準仕様に従った場合の限界を紹介
    - しかし、 JPA の実装によっては可能なものもある
        - ※実際に動かした結果であり、各ライブラリが明確にサポートしているかどうかまでは調べられていません、すみません m(__)m

---
## 前提
以下の環境で動作確認をしました

- JPA の実装(ver 2.1 相当)
    - EclipseLink 2.6.4
    - Hibernate 5.2.2
    - Open JPA 2.4.1 (一部だけ)
        - JPA 2.1 未サポート
- データベース
    - MySQL (mysql  Ver 14.14 Distrib 5.5.28, for Win64 (x86))

---
## エンティティを実装する
```java
package sample.jpa;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;

@Entity
@Table(name="table_alpha")
public class EntityAlpha implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Column(name="alpha_name")
    private String name;
}
```

- Getter, Setter は不要
    - フィールドを直接参照するか、アクセサ経由で参照するかを選択できる
- フィールドは `private` 可。

> The persistent state of an entity is accessed by the persistence provider runtime either via JavaBeans style property accessors (“property access”) or via instance variables (“field access”).[^6]
> 
> （訳）
> エンティティの永続化された状態は、 JavaBeans スタイルのプロパティアクセサ経由（プロパティアクセス）か、インスタンス変数経由（フィールドアクセス）のいずれかで、永続化プロバイダのランタイムによってアクセスされる。
> 
> The instance variable of a class must be **private**, protected, or package visibility independent of whether field access or property access is used.[^6]
> 
> （訳）
> クラスのインスタンス変数は、フィールドアクセス・プロパティアクセスのどちらを使うかに関係なく、 `private`, `protected` またはパッケージプライベートでなければならない。

[^6]: JPA 2.1 仕様書 P.24「2.2 Persistent Fields and Properties」

---
## 制約１：コンストラクタ
- 次の条件をすべて満たすコンストラクタが必要
    - 引数なし
    - 可視性が `protected` か `public`

> The entity class must have a **no-arg constructor**.
> The no-arg constructor must be **public** or **protected**.[^7]
>
> （訳）
> エンティティクラスは、引数なしのコンストラクタを持っていなければならない。
> 引数なしのコンストラクタは、 `public` か `protected` でなければならない。

[^7]: JPA 2.1 仕様書 P.23「The Entity Class」

```java:JPAの仕様に沿ったエンティティの実装
...

@Entity
@Table(name="table_alpha")
public class EntityAlpha implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Column(name="alpha_name")
    private String name;

    // 引数ありのコンストラクタを定義した場合は...
    public EntityAlpha(String name) {
        this.name = name;
    }

    // 引数なしのコンストラクタを定義しなければならない
    protected EntityAlpha() {}
}
```

- 外部から間違って使われると嫌な場合は `@Deprecated` でアノテートしておくとか...

### 実際のところ
||引数なしのコンストラクタ|可視性 private|
|---|---|---|
|EclipseLink|必須|<font color="blue">可</font>|
|Hibernate   |必須|<font color="blue">可</font>|
|Open JPA    |必須|<font color="red">不可</font>|

```java:EclipseLinkなどならこう書ける
...

@Entity
@Table(name="table_alpha")
public class EntityAlpha implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Column(name="alpha_name")
    private String name;

    public EntityAlpha(String name) {
        this.name = name;
    }

    // private にできる
    private EntityAlpha() {}
}
```

---
## 制約２：IDを埋め込み可能クラスにする
- ID を埋め込み可能クラスにすることは可能
    - `@Id` の代わりに `@EmbeddedId` でアノテートする
- ただし、<font color="red">関連がサポートされなくなる</font>

> Relationship mappings defined within an embedded id class are <font color="red">not supported</font>.[^8]
> 
> （訳）
> 埋め込みID クラスで定義された関連のマッピングは、サポートされない。

[^8]: JPA 2.1 仕様書 P.444「11.1.17 EmbeddedId Anootation」

### 実際のところ
ID を埋め込み可能クラスにしたときの挙動

||検索|CASCADE|
|---|---|---|
|EclipseLink|<font color="blue">可</font>|<font color="blue">可</font>|
|Hibernate   |<font color="blue">可</font>|<font color="red">不可</font>|


### EclipseLink で実装するとこんな感じ
![IDを埋め込み可能クラスにした場合.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/87f1afc4-3052-ae15-8f09-07cb21b2a98f.jpeg)


```java:EntityAlpha.java
package sample.jpa;

import sample.Id;

import javax.persistence.CascadeType;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import java.io.Serializable;

@Entity
@Table(name="table_alpha")
public class EntityAlpha implements Serializable {
    @EmbeddedId
    private Id<EntityAlpha> id;

    private String name;
    
    @OneToOne(cascade=CascadeType.ALL)
    @JoinColumn(
        name="table_beta_id",
        referencedColumnName="id"
    )
    private EntityBeta beta;
    
    @PrePersist
    private void setupId() {
        this.id = new Id<>();
    }
}
```

```java:EntityBeta.java
package sample.jpa;

import sample.Id;

import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import java.io.Serializable;

@Entity
@Table(name="table_beta")
public class EntityBeta implements Serializable {
    @EmbeddedId
    private Id<EntityBeta> id;

    private String name;

    @PrePersist
    private void setupId() {
        this.id = new Id<>();
    }
}
```

```java:Id.java
package sample;

import javax.persistence.Embeddable;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import java.io.Serializable;

@Embeddable
public class Id<T> implements Serializable {
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;
}
```

ポイントは次の２つ

1. `@JoinColumn` の `referencedColumnName` を指定
2. `@PrePersist` で ID クラスのインスタンスを `new` する

Hibernate は、 INSERT の CASCADE がうまく動いてくれなかった（外部参照キーがセットされない）

---
## そもそも ID を埋め込み可能クラスにしたときに CASCADE は必要か？
![集約.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/06abd68a-c57f-22e1-b3b5-f0b4e97c6e89.jpeg)

- 埋め込み可能クラス（値オブジェクト）にしたい
    - 型として意識したい
    - その値が単独でやり取りされることがある

- ID が単独でやり取りされるのは、ルートエンティティの ID くらい<span class="small">（のはず）</span>
    - 集約内のエンティティは関連を辿って取得するので、 ID を明示して使うことは基本的にない<span class="small">（はず）</span>
    - 他のルートエンティティを参照することがあるが、集約が別なので CASCADE は不要<span class="small">（のはず）</span>



---
## 値オブジェクトを実装する
```java
package sample.jpa;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import java.io.Serializable;

@Embeddable
public class EmbeddableAlpha implements Serializable {
    @Column(name="embeddable_value")
    private int value;

    public EmbeddableAlpha(int value) {
        this.value = value;
    }

    @Override
    public boolean equals(Object obj) {
        return (obj instanceof EmbeddableAlpha)
               && this.value == ((EmbeddableAlpha)obj).value;
    }

    @Override
    public int hashCode() {
        return this.value;
    }

    protected EmbeddableAlpha() {}
}
```

- 値が等しければ同じであると判断できるように、 `equals()` と `hashCode()` を実装
    - Lombok の `@EqualsAndHashCode` を使うと楽
    - `@Value` はフィールドを `final` にしてしまうので NG（詳細次ページ）
    - `@Data` は Getter, Setter を生成してしまうので良くない
- コンストラクタやフィールドアクセスに関する制約は Entity と同じ

---
## 制約３：フィールドを final にできない
- 値オブジェクトは不変に設計すべきなので、できれば `final` にできたほうが良い
- しかし、仕様上 Entity と 埋め込み可能クラスのフィールドは `final` にできない
- Setter や副作用のあるメソッドを定義しないようにすれば、不変にすること自体は可能

> <font color="red">No</font> methods or persistent instance variables of the entity class may <font color="red">be final</font>.[^9]
> 
> （意訳）
> エンティティクラスのメソッドまたはインスタンス変数を `final` にすることはできません。

[^9]: JPA 2.1 仕様書 P.23「2.1 The Entity Class」

### 実際のところ
||フィールドの final|
|---|---|
|EclipseLink|<font color="blue">可</font>|
|Hibernate   |<font color="blue">可</font>|
|Open JPA    |<font color="red">不可</font>|

```java:EclipseLinkなら値オブジェクトをこう実装できる
...

@Embeddable
public class EmbeddableAlpha implements Serializable {
    @Column(name="embeddable_value")
    private final int value; // final にできる

    public EmbeddableAlpha(int value) {
        this.value = value;
    }

    @Override
    public boolean equals(Object obj) {
        return (obj instanceof EmbeddableAlpha)
               && this.value == ((EmbeddableAlpha)obj).value;
    }

    @Override
    public int hashCode() {
        return this.value;
    }

    private EmbeddableAlpha() { // private にできる
        this.value = -1;
    }
}
```

### <span class="small">`final` なのに値が JPA によって書き換えられるのがとてもキモイけど、一応動く</span>

---
## 制約４：埋め込み可能クラスをそのままの形で JPQL で比較することはできない
埋め込み可能クラスを、中の値を取り出さずにそのまま比較できると嬉しい

```java:理想
String jpql =
    "SELECT alpha"
  + "  FROM EntityAlpha alpha"
  + " WHERE alpha.embeddableValue = :embeddableValue";

TypedQuery<EntityAlpha> query
    = entityManager.createQuery(jpql, EntityAlpha.java);

// これは仕様上不可
query.setParameter("embeddableValue", new EmbeddableValue("FOO"));
```

でも仕様上は、、、

> Only equality/inequality comparisons over <font color="blue">enums are required to be supported</font>.
> Comparisons over instances of embeddable class or map entry types are <font color="red">not supported</font>. [^10]
> 
> （訳）
> enum の等価/不等価の比較だけはサポートされなければなりません。
> 埋め込み可能クラス、または `Map` エントリ型のインスタンスの比較はサポートされません。

[^10]: JPA 2.1 仕様書 P207「4.12 Equality and Comparison Semantics」

なので、現実はこう↓

```java:現実
String jpql =
    "SELECT alpha"
  + "  FROM EntityAlpha alpha"
  + " WHERE alpha.embeddableValue.value = :value";

TypedQuery<EntityAlpha> query
    = entityManager.createQuery(jpql, EntityAlpha.java);

query.setParameter("value", "FOO");
```

### 実際のところ
||等価比較のサポート|
|---|---|
|EclipseLink|<font color="blue">可</font>|
|Hibernate   |<font color="blue">可</font>|

- ただし、大小比較などは、 EclipseLink はサポートしていない（詳細次ページ）
- 埋め込み可能クラスの比較は、対象の値が直接比較される模様
    - `equals()` をオーバーライドしても、 JPQL 実行時に呼ばれない

---
## 埋め込み可能クラスを JPQL でどこまで使用できるか
![JPQLと埋め込み可能クラス.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/58914cb0-9285-754f-917f-794b3e3e95d7.jpeg)

EclipseLink で IN で使用した場合、エラーにはならない。しかし比較が正しく行われないので注意。


---
## 集約と CASCADE
- 集約内部のエンティティはアトミックに更新されなければならない
    - 登録のときは一緒に登録される
    - 削除のときは一緒に削除される
- JPA だと、 CASCADE で指定できる

![集約とCASCADE.png](https://qiita-image-store.s3.amazonaws.com/0/28302/d4258b47-4ace-7e48-fb69-62156d6d7791.png)

```java:EntityAlpha.java
package sample.jpa;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import java.io.Serializable;

@Entity
@Table(name="table_alpha")
public class EntityAlpha implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    // CASCADE を指定する
    @OneToOne(cascade=CascadeType.ALL)
    @JoinColumn(name="table_beta_id")
    private EntityBeta beta;

    ...
}
```

```java:EntityBeta.java
package sample.jpa;

...

@Entity
@Table(name="table_beta")
public class EntityBeta implements Serializable {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;

    // CASCADE を指定しない
    @OneToOne
    @JoinColumn(name="table_gamma_id")
    private EntityGamma gamma;

    ...
}
```

```java:EntityGamma.java
package sample.jpa;

...

@Entity
@Table(name="table_gamma")
public class EntityGamma implements Serializable {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;

    ...
}
```

---
## 実装例（モデル）
ここまでの話を全てまとめつつ、もうちょっと具体的な実装例を紹介

![JPA実装例_Java.png](https://qiita-image-store.s3.amazonaws.com/0/28302/97b0fe41-e24e-868b-65d5-262699aec0d3.png)

![JPA実装例_DB.jpg](https://qiita-image-store.s3.amazonaws.com/0/28302/863f20f0-4289-9fe6-1db9-59b2ee496715.jpeg)

実際はもっと複雑になるだろうけど、ここは感触を伝えるため簡略なモデルで

---
## 実装例（コード）
```java:注文
package order;

import javax.persistence.CascadeType;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Date;
import java.util.List;

@Entity
@Table(name="order_requests")
public class OrderRequest implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Embedded
    private DeliveryDate deliveryDate;

    // 同じ集約内なので CASCADE を指定
    @OneToMany(
        cascade=CascadeType.ALL,
        fetch=FetchType.EAGER
    )
    @JoinTable(
        name="order_request_relations",
        joinColumns=@JoinColumn(
            name="order_requests_id"
        ),
        inverseJoinColumns=@JoinColumn(
            name="order_request_details_id"
        )
    )
    private List<OrderRequestDetail> orderRequestDetails;

    public OrderRequest(
        DeliveryDate deliveryDate,
        List<OrderRequestDetail> orderRequestDetails
    ) {
        this.deliveryDate = deliveryDate;
        this.orderRequestDetails = orderRequestDetails;
    }

    // 引数なしで protected 以上のコンストラクタが必要
    protected OrderRequest() {}
}
```

```java:納期
package order;

import lombok.EqualsAndHashCode;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import java.io.Serializable;
import java.util.Date;

@Embeddable
@EqualsAndHashCode
public class DeliveryDate implements Serializable {
    @Temporal(TemporalType.DATE)
    @Column(name="delivery_date")
    private Date date; // final にできない

    public DeliveryDate(Date date) {
        this.date = date;
    }

    // 引数なしで protected 以上のコンストラクタが必要
    protected DeliveryDate() {}
}
```

```java:注文明細
package order;

import item.Item;
import item.ItemName;
import item.ItemUnitPrice;

import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import java.io.Serializable;

@Entity
@Table(name="order_request_details")
public class OrderRequestDetail implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;
    // 別の集約なので、 CASCADE は指定しない
    @OneToOne
    @JoinColumn(name="items_id")
    private Item item;
    @Embedded
    private ItemName itemName;
    @Embedded
    private ItemUnitPrice itemUnitPrice;
    @Embedded
    private Quantity quantity;

    public OrderRequestDetail(Item item, Quantity quantity) {
        this.item = item;
        this.itemName = item.getName();
        this.itemUnitPrice = item.getUnitPrice();
        this.quantity = quantity;
    }

    // 引数なしで protected 以上のコンストラクタが必要
    protected OrderRequestDetail() {}
}
```

```java:商品名称
package item;

import lombok.EqualsAndHashCode;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import java.io.Serializable;

@Embeddable
@EqualsAndHashCode
public class ItemName implements Serializable {
    @Column(name="item_name")
    private String value; // final にできない

    public ItemName(String value) {
        this.value = value;
    }

    // 引数なしで protected 以上のコンストラクタが必要
    protected ItemName() {}
}
```

```java:商品
package item;

import javax.persistence.AttributeOverride;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;

@Entity
@Table(name="items")
public class Item implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    // カラム名のマッピングを上書き
    @Embedded
    @AttributeOverride(
        name="value",
        column=@Column(name="name")
    )
    private ItemName name;

    @Embedded
    @AttributeOverride(
        name="value",
        column=@Column(name="unit_price")
    )
    private ItemUnitPrice unitPrice;

    public Item(ItemName name, ItemUnitPrice unitPrice) {
        this.name = name;
        this.unitPrice = unitPrice;
    }

    // 引数なしで protected 以上のコンストラクタが必要
    protected Item() {}

    ...
}
```

---
## (；´д｀)ｱﾉﾃｰｼｮﾝ邪魔...

---
## XML で定義する（XML 定義ファイル）
- アノテーションが邪魔すぎてドメインを汚染すると思う場合は、 XML でマッピングを定義することもできる

```xml:persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
       xmlns="http://xmlns.jcp.org/xml/ns/persistence"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
                 http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

  <persistence-unit name="SampleUnit" transaction-type="RESOURCE_LOCAL">
    <!-- クラスパス上の場所を指定 -->
    <mapping-file>mapping.xml</mapping-file>

    ...
  </persistence-unit>
</persistence>
```

```xml:mapping.xml
<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings
     xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm
               http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd"
     version="2.1">
  <description>JPA Sample</description>

  <persistence-unit-metadata>
    <!-- アノテーションの定義を無視する -->
    <xml-mapping-metadata-complete />
        
    <persistence-unit-defaults>
      <!-- デフォルトのアクセス方法をフィールドにする -->
      <access>FIELD</access>
    </persistence-unit-defaults>
  </persistence-unit-metadata>

  <entity class="order.OrderRequest">
    <table name="order_requests" />

    <attributes>
      <id name="id">
        <generated-value strategy="IDENTITY" />
      </id>

      <embedded name="deliveryDate" />

      <one-to-many name="orderRequestDetails" fetch="EAGER">
        <cascade>
          <cascade-all/>
        </cascade>
        <join-table name="order_request_relations">
          <join-column name="order_requests_id" />
          <inverse-join-column name="order_request_details_id" />
        </join-table>
      </one-to-many>
    </attributes>
  </entity>

  <embeddable class="order.DeliveryDate">
    <attributes>
      <basic name="date">
        <column name="delivery_date" />
        <temporal>DATE</temporal>
      </basic>
    </attributes>
  </embeddable>

  <entity class="order.OrderRequestDetail">
    <table name="order_request_details" />

    <attributes>
      <id name="id">
        <generated-value strategy="IDENTITY" />
      </id>

      <one-to-one name="item">
        <join-column name="items_id" />
      </one-to-one>

      <embedded name="itemName" />

      <embedded name="itemUnitPrice" />

      <embedded name="quantity" />
    </attributes>
  </entity>

  <embeddable class="order.Quantity">
    <attributes>
      <basic name="value">
        <column name="quantity" />
      </basic>
    </attributes>
  </embeddable>

  <embeddable class="item.ItemName">
    <attributes>
      <basic name="value">
        <column name="item_name" />
      </basic>
    </attributes>
  </embeddable>

  <embeddable class="item.ItemUnitPrice">
    <attributes>
      <basic name="value">
        <column name="item_unit_price" />
      </basic>
    </attributes>
  </embeddable>

  <entity class="item.Item">
    <table name="items" />

    <attributes>
      <id name="id">
        <generated-value strategy="IDENTITY" />
      </id>

      <embedded name="name">
        <attribute-override name="value">
          <column name="name" />
        </attribute-override>
      </embedded>

      <embedded name="unitPrice">
        <attribute-override name="value">
          <column name="unit_price" />
        </attribute-override>
      </embedded>
    </attributes>
  </entity>
</entity-mappings>
```

---
## XMLで定義する（Java コード）
JPA に関する依存関係は全てなくなる

```java:注文
package order;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

public class OrderRequest implements Serializable {
    private Long id;
    private DeliveryDate deliveryDate;
    private List<OrderRequestDetail> orderRequestDetails;

    public OrderRequest(DeliveryDate deliveryDate, List<OrderRequestDetail> orderRequestDetails) {
        this.deliveryDate = deliveryDate;
        this.orderRequestDetails = orderRequestDetails;
    }

    protected OrderRequest() {}
}
```

```java:納期
package order;

import lombok.EqualsAndHashCode;

import java.io.Serializable;
import java.util.Date;

@EqualsAndHashCode
public class DeliveryDate implements Serializable {
    private Date date;

    public DeliveryDate(Date date) {
        this.date = date;
    }

    protected DeliveryDate() {}
}
```

```java:注文明細
package order;

import item.Item;
import item.ItemName;
import item.ItemUnitPrice;

import java.io.Serializable;

public class OrderRequestDetail implements Serializable {
    private Long id;
    private Item item;
    private ItemName itemName;
    private ItemUnitPrice itemUnitPrice;
    private Quantity quantity;

    public OrderRequestDetail(Item item, Quantity quantity) {
        this.item = item;
        this.itemName = item.getName();
        this.itemUnitPrice = item.getUnitPrice();
        this.quantity = quantity;
    }

    protected OrderRequestDetail() {}
}
```

---
## 後半まとめ
### 仕様上できること
- Getter, Setter は不要
- フィールドは `private` にできる
- CASCADE を利用することで、集約の登録・削除を一括で行える
- アノテーションが邪魔な場合は XML で定義できる

### 仕様上できないこと
- フィールドは `final` にできない
- `protected` or `public` で引数なしのコンストラクタが必要
- 一意キーを埋め込み可能クラスにした場合、関連は使用できない
- 埋め込み可能クラスを、そのままの形で JPQL で比較することはできない

### 実装（EclipseLink, Hibernate）による実際の動き
- フィールドは `final` にできる
- 引数なしのコンストラクタを `private` にできる
- 一意キーを埋め込み可能クラスにしても、検索はできる
    - 更新は、 EclipseLink なら動く
- 埋め込み可能クラスの JPQL での比較は、等価・不等価くらいなら可能

**注意**：実際に動かして検証しただけで、各々のライブラリの仕様を確認したわけではないので、ご利用は計画的に

### 教訓
RI(リファレンス実装) である EclipseLink が既に仕様と異なる動きをするので、「RI でできたから仕様通り」と思っているとポータビリティのない実装になってしまう可能性がある！

---
## 全体まとめ
- JPA はオブジェクト指向に振り切ったライブラリ
- DB アクセスを全力で隠蔽し、オブジェクトの世界だけで完結させようとしている<span class="small">（ように思える）</span>
- DDD のパターンを試そうとすると、 JPA の仕様上の制限により理想通りにいかないことがある
- けど、それっぽいところまではいける
- JPA の実装によっては制限が緩くなっており、理想に近づける
- ただし、標準仕様のメリットであるはずのポータビリティは失われるので、ご利用は計画的に

以上

---
## おまけ
- 個人的に、 JPA の鬼門の１つは「アノテーションによるマッピング」だと思っている
- あの大量のアノテーションに圧倒されて、やりたいマッピングを、どう定義すればいいのかすぐには分からない
- ということで、 JPA のアノテーションのマッピングのカタログっぽいものを書いてみました。
    - [JPA マッピングカタログ - Qiita](http://qiita.com/opengl-8080/items/5190ee953cfbab5ce3e9)