JPA マッピングカタログ

この記事は [Java EE Advent Calendar 2016 - Qiita](http://qiita.com/advent-calendar/2016/javaee) の３日目の記事です。

昨日は @n_agetsu さんの [コネクションプールの挙動差分によるバグ顕在化](http://n-agetsuma.hatenablog.com/entry/2016/12/02/000000) でした。
明日は @lbtc_xxx さんです。


[以前 JPA のマッピングについて勉強したときのメモ](http://qiita.com/opengl-8080/items/265f9f66a65e966678cb) を書いたことがあるのですが、これはマッピングの仕組みや意味を理解することが主目的でした。
そのため、後でマッピングの方法を探そうとしたときに、ノイズ（不要な情報）が多いなぁと思っていました。

そこで、今回は「あのマッピングどうするんだっけ」と思ったときに Java の実装と DB の構造がパッと分かるようにすることを目標にして、いろいろなマッピングを一覧にしてみました。

また、うまく動くものだけでなく、**うまく動かないもの**、**実現できないもの**もまとめています。
できることだけでなく、できないことも早々に分かればハマる時間を減らせると思うので。
うまく動かない可能性があったり、実装によって挙動が異なるなど何かしら注意が必要なものについては、先頭に <i class="fa fa-warning"></i> というアイコンをつけています。

<del><span class="small"><i class="fa fa-warning"></i> 多すぎじゃね？！</span></del>

# 動作確認環境
## JPA 実装
- EclipseLink 2.6.4
- Hibernate 5.2.2

## データベース
- MySQL (mysql  Ver 14.14 Distrib 5.5.28, for Win64 (x86))



# 基本型
## プリミティブ型・および `String` などの基本的な型
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/88f23631-b59b-5613-b27d-ef0903aea107.jpeg)

```java:EntityAlpha.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 javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;

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

    @Column(name="string_value")
    private String stringValue;

    @Column(name="boolean_value")
    private boolean booleanValue;

    @Column(name="int_value")
    private int intValue;

    @Column(name="double_value")
    private double doubleValue;

    @Column(name="big_integer_value")
    private BigInteger bigIntegerValue;

    @Column(name="big_decimal_value")
    private BigDecimal bigDecimalValue;

    @Column(name="date_value")
    @Temporal(TemporalType.DATE)
    private Date dateValue;

    @Column(name="datetime_value")
    @Temporal(TemporalType.TIMESTAMP)
    private Date dateTimeValue;
}
```

## enum
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/415cdfc2-a433-dfc1-7ea6-22bb6931986d.jpeg)

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

public enum EnumAlpha {
    ALPHA,
    BETA,
    GAMMA,
}
```

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

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
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="ordinal_enum_value")
    private EnumAlpha ordinalEnumValue;

    @Column(name="string_enum_value")
    @Enumerated(EnumType.STRING) // name() が返した文字列で保存
    private EnumAlpha stringEnumValue;
}
```

- `ordinal` の場合は `0` 始まり

## List
### ソート指定なし
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/a29ab458-2cdb-c7a5-2e14-372bd0ce34f8.jpeg)

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

import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
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.Table;
import java.io.Serializable;
import java.util.List;

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

    private String name;

    @ElementCollection(fetch=FetchType.EAGER)
    @CollectionTable(
        name="list_table",
        joinColumns=@JoinColumn(name="table_alpha_id")
    )
    @Column(name="value")
    private List<String> list;
}
```


### ソート指定あり（インデックス値を保存）
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/4ffb35a4-a21a-92e0-aca4-69b51c5cbd9a.jpeg)

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

import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
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.OrderColumn;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.List;

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

    private String name;

    @ElementCollection(fetch=FetchType.EAGER)
    @CollectionTable(
        name="list_table",
        joinColumns=@JoinColumn(name="table_alpha_id")
    )
    @OrderColumn(name="index_value") // ★
    @Column(name="value")
    private List<String> list;
}
```

### <i class="fa fa-warning"></i>ソート指定あり（リスト内の値でソート）
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/ba8fb615-f0f4-1240-9344-0abde737121f.jpeg)

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

import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
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.OrderBy;
import javax.persistence.OrderColumn;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.List;

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

    private String name;

    @ElementCollection(fetch=FetchType.EAGER)
    @CollectionTable(
        name="list_table",
        joinColumns=@JoinColumn(name="table_alpha_id")
    )
    @OrderBy("value DESC") // ★
    @Column(name="value")
    private List<String> list;
}
```

```text:@OrderByに設定する値の書式
@OrderBy("orderby_list")

orderby_list ::= orderby_item [, orderby_item]*
orderby_item ::= [property_or_field_name] [ASC|DESC]
```

JPA 仕様書の 11.1.42 参照。

EclipseLink は `value` を書かなくても動いたが、 Hibernate は `value` を書かないとエラーになった。
仕様的には、 EclipseLink が正解？

### <i class="fa fa-warning"></i>関連テーブルを使う
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/dea502b7-3bfc-3b8e-083c-90a5d102e484.jpeg)

`@CollectionTable` に `@JoinTable` の `inverseJoinColumns` 相当のものがないので無理っぽい？

## Map
### 基本
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/bc8ce36b-3ef8-60e5-d4e4-35759d10ea1a.jpeg)

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

import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
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.MapKeyColumn;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Map;

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

    private String name;

    @ElementCollection(fetch=FetchType.EAGER)
    @CollectionTable(
        name="map_table",
        joinColumns=@JoinColumn(name="table_alpha_id")
    )
    @MapKeyColumn(name="map_key")
    @Column(name="map_value")
    private Map<String, String> map;
}
```

### キーが enum
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/1ca97715-c6ea-6dac-0765-088fd6a7ccd5.jpeg)

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

import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.MapKeyColumn;
import javax.persistence.MapKeyEnumerated;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Map;

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

    private String name;

    @ElementCollection(fetch=FetchType.EAGER)
    @CollectionTable(
        name="map_table",
        joinColumns=@JoinColumn(name="table_alpha_id")
    )
    @MapKeyEnumerated(EnumType.STRING) // ★
    @MapKeyColumn(name="map_key")
    @Column(name="map_value")
    private Map<EnumAlpha, String> map;
}
```

### <i class="fa fa-warning"></i>Map&lt;String, List&lt;String>> みたいなこと
無理っぽい

[Java Persistence/Relationships - Wikibooks, open books for an open world](https://en.wikibooks.org/wiki/Java_Persistence/Relationships#Nested_Collections.2C_Maps_and_Matrices)

> JPA does not support nested collection relationships

# 埋め込み可能クラス
## 基本
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/cea1137c-4f88-9527-d032-2009c09fb9c3.jpeg)

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

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="table_alpha")
public class EntityAlpha implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Embedded
    private EmbeddableAlpha embeddableAlpha;
}
```
```java:EmbeddableAlpha.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 String value;
}
```


## List
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/2bc3795f-3b2a-1ff0-ff8c-6cbc3147f8a7.jpeg)

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

import javax.persistence.CollectionTable;
import javax.persistence.ElementCollection;
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.Table;
import java.io.Serializable;
import java.util.List;

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

    private String name;

    @Embedded
    @ElementCollection(fetch=FetchType.EAGER)
    @CollectionTable(
        name="list_table",
        joinColumns=@JoinColumn(name="table_alpha_id")
    )
    private List<EmbeddableAlpha> list;
}
```

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

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

@Embeddable
public class EmbeddableAlpha implements Serializable {
    @Column(name="list_value")
    private String value;
}
```


## <i class="fa fa-warning"></i>Map（値）
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/557662cf-356b-8635-7f4f-a63d224419a4.jpeg)

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

import javax.persistence.CollectionTable;
import javax.persistence.ElementCollection;
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.MapKeyColumn;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Map;

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

    private String name;

    @ElementCollection(fetch=FetchType.EAGER)
    @CollectionTable(
        name="map_table",
        joinColumns=@JoinColumn(name="table_alpha_id")
    )
    @MapKeyColumn(name="map_key")
    private Map<String, EmbeddableAlpha> map;
}
```

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

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

@Embeddable
public class EmbeddableAlpha implements Serializable {
    @Column(name="map_value")
    private String value;
}
```

<span id="map_value_embeddable">上記実装は、検索だけなら EclipseLink, Hiberante 共に動作した。</span>
しかし、更新になると EclipseLink がうまく動作しないケースが発生した。

いろいろ試したところ、どうも値が同じエントリが複数存在する場合の動作があやしい。
キー違いで同じ値のエントリを追加した場合は `INSERT` が実行されない。
直接 DB にキー違いで同じ値のエントリを追加した場合は、逆にエントリを片方削除しても `DELETE` が実行されない。

つまり、 DB が

|table_alpha_id|map_key|map_value|
|---|---|---|
|1  |foo|TEST|

の状態だと、値が `TEST` のエントリを追加しても `INSERT` が発行されず、

|table_alpha_id|map_key|map_value|
|---|---|---|
|1  |foo|TEST|
|1  |bar|TEST|

のような状態だと `key=foo`, `key=bar` のいずれかのエントリを `remove()` で削除しても `DELETE` が発行されない。
（両方とも `remove()` したら `DELETE` が実行された）

Hibernate だと、うまく動いている。
EclipseLink の場合は、 `Map` の値の方を基本型（プリミティブ型や `String` など）にすればうまく動くようになった。。。

バグだろぉ...

## Map（キー）
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/460459b3-6aaa-8b4b-2e17-bd98b5223e02.jpeg)

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

import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
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.Table;
import java.io.Serializable;
import java.util.Map;

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

    private String name;

    @ElementCollection(fetch=FetchType.EAGER)
    @CollectionTable(
        name="map_table",
        joinColumns=@JoinColumn(name="table_alpha_id")
    )
    @Column(name="map_value")
    private Map<EmbeddableAlpha, String> map;
}
```

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

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

@Embeddable
public class EmbeddableAlpha implements Serializable {
    @Column(name="map_key")
    private String value;
}
```



## 入れ子
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/eea44c7b-9212-9361-f2fb-6af10bb261bc.jpeg)

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

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="table_alpha")
public class EntityAlpha implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Embedded
    private EmbeddableAlpha alpha;
}
```

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

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

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

    @Embedded
    private EmbeddableBeta beta;
}
```

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

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

@Embeddable
public class EmbeddableBeta implements Serializable {
    @Column(name="beta_value")
    private String value;
}
```

## マッピング定義の上書き
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/2c497269-d769-6da9-0ef4-2ee59e930530.jpeg)

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

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="table_alpha")
public class EntityAlpha implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Embedded
    private EmbeddableAlpha foo;
    @Embedded
    @AttributeOverride(name="value", column=@Column(name="bar"))
    private EmbeddableAlpha bar;
}
```

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

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

@Embeddable
public class EmbeddableAlpha implements Serializable {
    @Column(name="foo")
    private String value;
}
```

- `@AttributeOverride` で設定を上書きできる。
- `name` に上書きするプロパティ名、 `column` に上書き内容を設定。
- 複数指定する場合は `@AttributeOverrides` を使用する。

## エンティティ→埋め込み可能クラス→エンティティ
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/0dc05a8c-d8c3-e5ff-4ea3-a8b618f7234c.jpeg)

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

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="table_alpha")
public class EntityAlpha implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Embedded
    private EmbeddableAlpha alpha;
}
```

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

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import java.io.Serializable;

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

    @OneToOne(cascade=CascadeType.ALL)
    @JoinColumn(name="table_beta_id")
    private EntityBeta beta;
}
```

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

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_beta")
public class EntityBeta implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    private String name;
}
```



# エンティティ
## <i class="fa fa-warning"></i>ID を埋め込み可能クラスにする
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/f2c2536e-4a80-38d1-a5ae-4dd4599b523f.jpeg)

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

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

@Embeddable
public class EmbeddableId implements Serializable {
    @Column(name="id")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long value;
}
```

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

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

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

    private String name;

    @PrePersist
    void setupId() {
        this.id = new EmbeddableId();
    }
}
```

- Entity 側の ID フィールドが `null` だと、 `persist()` 時にエラーが発生する。

```text:IDがnullの場合に発生する例外
■Hibernate の場合
javax.persistence.PersistenceException: org.hibernate.id.IdentifierGenerationException: null id generated for:class sample.jpa.EntityAlpha
	at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:147)

■Eclipse Link の場合
Caused by: Exception [EclipseLink-71] (Eclipse Persistence Services - 2.6.4.v20160829-44060b6): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: A NullPointerException was thrown while setting the value of the instance variable [value] to the value [5].
Internal Exception: java.lang.NullPointerException
Mapping: org.eclipse.persistence.mappings.DirectToFieldMapping[value-->table_alpha.id]
Descriptor: RelationalDescriptor(sample.jpa.EmbeddableId --> [DatabaseTable(table_alpha)])
	at org.eclipse.persistence.exceptions.DescriptorException.nullPointerWhileSettingValueThruInstanceVariableAccessor(DescriptorException.java:1321)
```

- 埋め込み可能クラス（`EmbeddableId`）のインスタンスまでは作ってくれないみたいなので、事前に ID の値が空のインスタンスを生成してあげる必要があるっぽい。
- フィールド宣言に直接 `new` して代入しておいても良いが、 `@PrePersist` でセットするほうが意味的にも合致していて自然なのでそっちの方が良い気がする。

[spring - null id generated for composite PK - Stack Overflow](http://stackoverflow.com/questions/26320983/null-id-generated-for-composite-pk)

## <i class="fa fa-warning"></i>Map
どれも EclipseLink で動かしたときに [Map の値を埋め込み可能クラスにした場合](#map_value_embeddable) と同じ問題が発生した。

Hibernate は問題なく動く。

結構致命的な気がするんだけど、何か間違っているのだろうか。。。

### <i class="fa fa-warning"></i>キーが基本型で値がエンティティ
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/de773171-2cee-8fda-7aa8-09877c945c07.jpeg)

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

import javax.persistence.CascadeType;
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.MapKeyColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Map;

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

    private String name;

    @OneToMany(cascade=CascadeType.ALL)
    @JoinTable(
        name="alpha_beta_map",
        joinColumns = @JoinColumn(name="table_alpha_id"),
        inverseJoinColumns = @JoinColumn(name="table_beta_id")
    )
    @MapKeyColumn(name="map_key")
    private Map<String, EntityBeta> map;
}
```

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

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_beta")
public class EntityBeta implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
}
```

### <i class="fa fa-warning"></i>キーが埋め込み可能クラスで値がエンティティ
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/fa126c75-359d-c335-dd9c-9f04bdb79c6e.jpeg)


```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.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Map;

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

    private String name;

    @OneToMany(cascade=CascadeType.ALL)
    @JoinTable(
        name="alpha_beta_map",
        joinColumns = @JoinColumn(name="table_alpha_id"),
        inverseJoinColumns = @JoinColumn(name="table_beta_id")
    )
    private Map<EmbeddableAlpha, EntityBeta> map;
}
```

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

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_beta")
public class EntityBeta implements Serializable {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;

    private String name;
}
```

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

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

@Embeddable
public class EmbeddableAlpha implements Serializable {
    @Column(name="map_key")
    private String value;
}
```

### <i class="fa fa-warning"></i>キーがエンティティで値が埋め込み可能クラス
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/c51eaa7f-99ad-788c-f20f-6c862e9d549f.jpeg)

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

import javax.persistence.CollectionTable;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.MapKeyJoinColumn;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Map;

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

    private String name;

    @ElementCollection
    @CollectionTable(
        name="alpha_beta_map",
        joinColumns=@JoinColumn(name="table_alpha_id")
    )
    @MapKeyJoinColumn(name="table_beta_id")
    private Map<EntityBeta, EmbeddableAlpha> map;
}
```

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

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

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

    private String name;

    @Override
    public boolean equals(Object obj) {
        return obj instanceof EntityBeta
                && Objects.equals(this.id, ((EntityBeta) obj).id);
    }

    @Override
    public int hashCode() {
        return this.id == null ? 0 : this.id.hashCode();
    }
}
```

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

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

@Embeddable
public class EmbeddableAlpha implements Serializable {
    @Column(name="map_value")
    private String value;
}
```

エンティティが Map のキーに使われている場合、 cascade は無理っぽい。

> When the collection is a java.util.Map, the cascade element and the orphanRemoval element
apply to the map value.
> コレクションが `java.util.Map` の場合、 `cascade` と `orphanRemoval` は Map の値に適用される。

11.1.40 OneToMany Annotation (P.473)

ということで、 `EntityAlpha` を登録する前に `EntityBeta` を `persist()` しておく必要がある。

```java:実装イメージ
// 先に EntityBeta を persist()
EntityBeta beta = new EntityBeta("beta");
entityManager.persist(beta);
entityManager.flush(); // DB に反映しておかないと、 alpha_beta_map テーブルを登録するときに table_beta にデータが無くて外部参照制約違反でエラーになる

// persist() 済みの EntityBeta をキーに使う
Map<EntityBeta, EmbeddableAlpha> map = new HashMap<>();
map.put(beta, new EmbeddableAlpha("map value"));

// EntityAlpha に設定して persist()
alpha.setMap(map);
entityManager.persist(alpha);
```

### <i class="fa fa-warning"></i>キーも値もエンティティ
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/73b7f8ef-1e44-d915-f067-26e1ff62edc0.jpeg)

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

import javax.persistence.CascadeType;
import javax.persistence.CollectionTable;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.MapKeyJoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Map;

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

    private String name;

    @OneToMany(cascade=CascadeType.ALL)
    @JoinTable(
        name="alpha_beta_gamma",
        joinColumns=@JoinColumn(name="table_alpha_id"),
        inverseJoinColumns=@JoinColumn(name="table_gamma_id")
    )
    @MapKeyJoinColumn(name="table_beta_id")
    private Map<EntityBeta, EntityGamma> map;
}
```

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

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

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

    private String name;

    @Override
    public boolean equals(Object obj) {
        return obj instanceof EntityBeta
                && Objects.equals(this.id, ((EntityBeta) obj).id);
    }

    @Override
    public int hashCode() {
        return this.id == null ? 0 : this.id.hashCode();
    }
}
```

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

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_gamma")
public class EntityGamma implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    private String name;
}
```

## １対１
### 単方向
#### ID が Long
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/1d23774c-f75f-7041-c0b9-6ee85221a7e8.jpeg)

```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;

    private String name;

    @OneToOne(cascade=CascadeType.ALL)
    @JoinColumn(name="beta_id")
    private EntityBeta beta;
}
```

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

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_beta")
public class EntityBeta implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    private String name;
}
```

#### 主キーが複合キー
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/088104c5-df3d-765f-e628-99ff6b35efc4.jpeg)

```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.JoinColumns;
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;

    private String name;

    @OneToOne(cascade=CascadeType.ALL)
    @JoinColumns({
        @JoinColumn(name="beta_key_1", referencedColumnName="key_1"),
        @JoinColumn(name="beta_key_2", referencedColumnName="key_2")
    })
    private EntityBeta beta;
}
```

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

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

@Entity
@Table(name="table_beta")
public class EntityBeta implements Serializable {
    @Id
    @Column(name="key_1")
    private String key1;

    @Id
    @Column(name="key_2")
    private String key2;

    private String name;
}
```

#### <i class="fa fa-warning"></i>ID が埋め込み可能クラス
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/9acf3f65-7a3b-033a-d916-0e805fbcea30.jpeg)

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

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 EmbeddableId id;

    private String name;

    @OneToOne(cascade=CascadeType.ALL)
    @JoinColumn(name="beta_id", referencedColumnName="id")
    private EntityBeta beta;

    @PrePersist
    private void prePersist() {
        this.id = new EmbeddableId();
    }
}
```

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

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 EmbeddableId id;

    private String name;

    @PrePersist
    private void prePersist() {
        this.id = new EmbeddableId();
    }
}
```

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

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

@Embeddable
public class EmbeddableId implements Serializable {
    @Column(name="id")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long value;
}
```

- ID が埋め込み可能クラスのオブジェクトと関連を定義する場合は、 `@JoinColumn` の `referencedColumnName` を指定しないといけない点に注意。
- EclipseLink ならこれで動くが、 Hibernate だと INSERT 時に `table_alpha.beta_id` がセットされない。
    - 検索だけならどちらも OK。
- そもそも、 `EmbeddedId` でリレーションを持たせることは仕様上サポート外っぽい。。。

> Relationship mappings defined within an embedded id class are **not supported**.

11.1.17 EmbeddedId Annotation (P.444)

### 双方向
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/4844fa65-0eb4-522a-b8a1-2680d2047837.jpeg)

```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;

    private String name;

    @OneToOne(cascade=CascadeType.ALL)
    @JoinColumn(name="beta_id")
    private EntityBeta beta;
}
```

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

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

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

    private String name;

    @OneToOne(mappedBy="beta")
    private EntityAlpha alpha;
}
```

## １対多
### 単方向
#### 結合テーブルを使用する
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/938a7c81-4705-f622-22a4-32dfeaf10aea.jpeg)

```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.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.List;

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

    private String name;

    @OneToMany(cascade=CascadeType.ALL)
    @JoinTable(
        name="alpha_beta",
        joinColumns=@JoinColumn(name="alpha_id"),
        inverseJoinColumns=@JoinColumn(name="beta_id")
    )
    private List<EntityBeta> betaList;
}
```

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

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_beta")
public class EntityBeta implements Serializable {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;

    private String name;
}
```

#### <i class="fa fa-warning"></i>結合テーブルを使用しない
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/94c343be-cc42-4dcd-777f-f5cba56a9453.jpeg)

```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.OneToMany;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

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

    private String name;

    @OneToMany(cascade=CascadeType.ALL)
    // ※使っているライブラリと table_beta.alpha_id が NULL 不可か
    //   どうかで、 nullable を指定する必要が出てくる（詳細後述）
    @JoinColumn(name="alpha_id")
    private List<EntityBeta> betaList;
}
```

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

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_beta")
public class EntityBeta implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    private String name;
}
```

- 自分が確かめた限りでは、これが確実に動くのは<font color="red">**検索のみ**</font>。
- `table_beta.alpha_id` が NULL 不可の場合、上記実装だと INSERT 時にエラーになる（`table_beta.alpha_id` に NULL をセットしようとしてエラーになる）。
    - Hibernate であれば、 `@JoinColumn` に `nullable=false` を設定することで INSERT, や DELETE も動くようになる。
    - Eclipse Link では、 `nullable=false` を設定してもやはり INSERT でエラーになった。
- `table_beta.alpha_id` が NULL 可の場合は、 Eclipse Link, Hibernate ともに上記実装で INSERT, DELETE が動作した。

### 双方向
#### 結合テーブルを使用する
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/8e590219-1586-41b7-0a8e-150fb2a906c9.jpeg)

```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.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.List;

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

    private String name;

    @OneToMany(cascade=CascadeType.ALL)
    @JoinTable(
        name="alpha_beta",
        joinColumns=@JoinColumn(name="table_alpha_id"),
        inverseJoinColumns=@JoinColumn(name="table_beta_id")
    )
    private List<EntityBeta> betaList;
}
```

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

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import java.io.Serializable;

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

    private String name;

    @ManyToOne
    @JoinTable(
        name="alpha_beta",
        joinColumns=@JoinColumn(name="table_beta_id"),
        inverseJoinColumns=@JoinColumn(name="table_alpha_id")
    )
    private EntityAlpha alpha;
}
```

#### <i class="fa fa-warning"></i>結合テーブルを使用しない
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/1dcf3d07-29ee-c51b-4840-3d4309368ec1.jpeg)

```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.OneToMany;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.List;

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

    private String name;

    @OneToMany(cascade=CascadeType.ALL, mappedBy = "alpha")
    private List<EntityBeta> betaList;
}
```

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

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

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

    private String name;

    @ManyToOne
    @JoinColumn(name="table_alpha_id")
    private EntityAlpha alpha;
}
```

こちらは単方向の場合とは異なり、うまく動作するっぽい。

ただし、`EntityAlpha.betaList` から要素を削除したい場合は、単純に `List` から要素を `remove()` するだけだと `EntityBeta` 自体は削除されない。

例えば、以下のような感じで `remove()` した `EntityBeta` 自体を `EntityManager` の `remove()` で削除してあげる必要がある。

```java:例
entityAlpha.remove(entityBeta);   // betaList から削除
entityManager.remove(entityBeta); // entityBeta を削除
```



## 多対多
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/76a2578a-b897-9213-5dd2-df76e57158c6.jpeg)

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

import javax.persistence.CascadeType;
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.ManyToMany;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.List;

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

    private String name;

    @ManyToMany(cascade=CascadeType.ALL)
    @JoinTable(
        name="alpha_beta",
        joinColumns=@JoinColumn(name="table_alpha_id"),
        inverseJoinColumns=@JoinColumn(name="table_beta_id")
    )
    private List<EntityBeta> betaList;
}
```

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

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.List;

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

    private String name;

    @ManyToMany(mappedBy="betaList")
    private List<EntityAlpha> alphaList;
}
```

# 継承のマッピング
細かい仕様の話（`dtype` が何かとか）は、 [JavaEE使い方メモ（JPA その２ - マッピング）](http://qiita.com/opengl-8080/items/265f9f66a65e966678cb#%E7%B6%99%E6%89%BF%E3%81%AE%E3%83%9E%E3%83%83%E3%83%94%E3%83%B3%E3%82%B0) を参照。

## クラス階層に属するエンティティを１つのテーブルにマッピングする
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/7d2016bb-12c4-2bae-db76-fd59010de597.jpeg)

```java:EntityAlpha.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="alpha_beta_gamma")
public class EntityAlpha implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

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

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

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

@Entity
public class EntityBeta extends EntityAlpha implements Serializable {
    @Column(name="beta_code")
    protected String code;
}
```

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

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

@Entity
public class EntityGamma extends EntityBeta implements Serializable {
    @Column(name="gamma_value")
    private String value;
}
```


## <i class="fa fa-warning"></i>サブクラスごとにテーブルをマッピングする
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/742a82d6-e40a-3b38-bb24-aaa0aebfc1eb.jpeg)

```java:EntityAlpha.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.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
import java.io.Serializable;

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

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

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

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.io.Serializable;

@Entity
@Table(name="table_beta")
public class EntityBeta extends EntityAlpha implements Serializable {
    @Column(name="beta_code")
    protected String code;
}
```

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

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.io.Serializable;

@Entity
@Table(name="table_gamma")
public class EntityGamma extends EntityBeta implements Serializable {
    @Column(name="gamma_value")
    private String value;
}
```

- EclipseLink の場合は `dtype` カラムが必要。
- Hibernate の場合は不要（カラムが存在しても、 null になる）。

仕様書（P.58 2.12.2 Joined Subclass Strategy）を読む限り、 `dtype` カラムの有無については言及されていない。
つまり、仕様上は「カラムを設けろ」とも「設けるな」とも書かれていないということになるので、どちらも間違ってはいないということになるのか。。。（無くても識別は可能なので、 Hibernate のほうが合っている気はする）

ちなみに、同じく P.58 の「2.12.1 Single Table per Class Hierarchy Strategy」には `The Table has a column that serves as a discriminator column` と `dtype` についての言及がある。


## 関連
### SINGLE
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/fc7d8693-fb24-ecbf-d6df-cce87a15745d.jpeg)

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

import javax.persistence.CascadeType;
import javax.persistence.Column;
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;

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

    @OneToOne(cascade=CascadeType.ALL)
    @JoinColumn(name="table_epsilon_id")
    protected EntityEpsilon epsilon;
}
```

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

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

@Entity
@Table(name="table_beta")
public class EntityBeta extends EntityAlpha implements Serializable {
    @Column(name="beta_code")
    protected String code;

    @OneToOne(cascade= CascadeType.ALL)
    @JoinColumn(name="table_zeta_id")
    protected EntityZeta zeta;
}
```

```java:EntityGamma.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_gamma")
public class EntityGamma implements Serializable {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToOne(cascade= CascadeType.ALL)
    @JoinColumn(name="table_alpha_id")
    private EntityAlpha alpha;
}
```

```java:EntityDelta.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_delta")
public class EntityDelta implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToOne(cascade=CascadeType.ALL)
    @JoinColumn(name="table_alpha_id")
    private EntityBeta beta;
}
```

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

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_epsilon")
public class EntityEpsilon implements Serializable {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;

    private String name;
}
```

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

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_zeta")
public class EntityZeta implements Serializable {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;

    private String name;
}
```


### <i class="fa fa-warning"></i>JOINED
![jpa.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/c94ffb0a-9f1e-0693-6a0f-bd547dd8e6d0.jpeg)

```java:EntityAlpha.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.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
import java.io.Serializable;

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

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

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

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.io.Serializable;

@Entity
@Table(name="table_beta")
public class EntityBeta extends EntityAlpha implements Serializable {
    @Column(name="beta_code")
    protected String code;
}
```

```java:EntityGamma.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_gamma")
public class EntityGamma implements Serializable {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToOne(cascade=CascadeType.ALL)
    @JoinColumn(name="table_alpha_id")
    private EntityAlpha alpha;
}
```

```java:EntityDelta.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_delta")
public class EntityDelta implements Serializable {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToOne(cascade=CascadeType.ALL)
    @JoinColumn(name="table_beta_id")
    private EntityBeta beta;
}
```

- Hibernate の方はこれで INSERT, UPDATE, DELETE が問題なく動く。
- EclipseLink は `EntityDelta` を `persist()` したときにエラーで落ちる。。。

```text:EclipseLinkでEntityDeltaをpersist()したときのエラー（抜粋）
[EL Fine]: INSERT INTO table_alpha (alpha_name, DTYPE) VALUES (?, ?)
	bind => [name(eclipselink), EntityBeta]

[EL Fine]: SELECT LAST_INSERT_ID()

[EL Fine]: INSERT INTO table_delta (NAME, table_beta_id) VALUES (?, ?)
	bind => [eclipselink, 9]

[EL Warning]: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.4.v20160829-44060b6): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`jpa`.`table_delta`, CONSTRAINT `dfk1` FOREIGN KEY (`table_beta_id`) REFERENCES `table_beta` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION)
Error Code: 1452
Call: INSERT INTO table_delta (NAME, table_beta_id) VALUES (?, ?)
	bind => [eclipselink, 9]
Query: InsertObjectQuery(EntityDelta(id=null, name=eclipselink, beta=EntityBeta(super=EntityAlpha(id=9, name=name(eclipselink)), code=code(eclipselink))))
:
:
```

`table_alpha` （親クラス用のテーブル）に INSERT した後に `table_beta` （子クラス用のテーブル）に INSERT せずに `table_delta` に INSERT しようとしている。

`table_delta` の FK 項目である `table_beta_id` に `table_alpha` に登録したときの ID (`9`) をセットしようとしているが、 `table_beta` にはレコードがまだないので外部参照制約違反で落ちている。

バグっぽいなぁ。。。

なお、エラーが起こった時の Java の実装は↓のような感じ。

```java
EntityBeta beta = new EntityBeta("name", "code");
EntityDelta delta = new EntityDelta("name", beta);
entityManager.persist(delta);
```

`EntityBeta` をあらかじめ `persist()` しておけば、エラーは起こらなくなり、その後検索や更新・削除は Hibernate 同様問題なく動作するようになる。