Spring Data JPA での、１対多関連付け

# 目的
一対多で関連付けられたテーブルを扱うための手順を、整理する
SQLなら瞬殺のことが、ぜんぜんわからず、
JPAの扱いでもがき苦しんだため、
とりあえずやっと動いた最小のモデルをまとめる

（ORマッパー難しい。泣ける。危うく発狂するところだった。）

# 環境
STS
Java8
Spring Boot
Hibernate


# 構造
以下のようなデータモデルを作りたい

元（単一）
t_mitsumori_hdrテーブル
HeaderEntity　ドメイン

関連テーブル（多）
t_mitsumori_dtlテーブル
MitsumoriDtl　ドメイン

# 手順

## ドメインクラス
### ソース例
***inport等略されているためこのままで動作するコードではありません、要点だけご確認ください***
- １側

```java
package com.example.domain;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity 
@Table(name = "t_mitsumori_hdr")
@ToString(exclude = "mitsumoriDtl")
public class HeaderEntity {
	
	
	@Id
	@GeneratedValue
	private Integer t_mitsumori_hdr_seq;
	@JsonIgnore
	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "headerEntity")
	private List<MitsumoriDtl> mitsumoriDtl;

}

```
###　説明
- Lambok関連の定義
@Data
@NoArgsConstructor
@AllArgsConstructor

- ドメインクラスの定義
@Entity 
@Table(name = "t_mitsumori_hdr")
@ToString(exclude = "mitsumoriDtl")

Tableで紐づくテーブル名を指定
ToStringは循環参照エラーを防ぐため、多側のドメインクラスを指定している。
このドメインクラスは、元ドメインクラスのプロパティとして、Listに格納される想定

```
@Id
@GeneratedValue
private Integer t_mitsumori_hdr_seq;
```
IdとGeneratedValueでこのプロパティが主キーであること、
値はJPAが生成することを指定

自動生成の動きとしては、
保存のためPOST時の段階では、主キー項目はnullの状態でコントローラに送り、.saveしてしまってよい

```
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "headerEntity")
private List<MitsumoriDtl> mitsumoriDtl;
```
featchにFetchType.EAGERを指定
ここで、データをDBから取得するとき、関連テーブルの方も一緒に取り出すかを指定している
Lazyの場合、取得しない
EAGERの場合、元を検索すると関連テーブルも格納されて取得される、関連テーブルは配列で取得されtくる

→正確には、Lazyは遅延ロードが行われている。
　元ドメインが取得selectされたタイミングでは、関連テーブルはまだ読み込まれない
　そこから、関連テーブルがアクセスされたタイミングでselectが関連関連テーブルに走る

mappedByに、関連先のドメイン（mitsumoriDtl）で指定されている元テーブルのプロパティ名を指定

まとめると
@OneToMany(cascade = 元が消えたら関連テーブルはどうするか, fetch = 一緒に取り出すか, mappedBy = 関連ドメインクラス)

*余談)この辺で、[どっちがどっちだ！？]　でパニクる*


----
- 多側

```java
package com.example.domain;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity 
@Table(name = "t_mitsumori_dtl")
public class MitsumoriDtl {
	@Id
	@GeneratedValue
	private Integer t_mitsumori_dtl_seq; 
	Integer t_mitsumori_hdr_seq; //見積HDRSEQ
	        
	@ManyToOne(fetch=FetchType.EAGER)
	@JoinColumn(nullable = false,insertable=false, updatable=false, name = "t_mitsumori_hdr_seq")
	private HeaderEntity headerEntity;
	

}

```
### 説明
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity 
@Table(name = "t_mitsumori_dtl")
同じ↑
Table nameでテーブルとこのドメインクラスを紐づける

```java
	@Id
	@GeneratedValue
	private Integer t_mitsumori_dtl_seq; 
	Integer t_mitsumori_hdr_seq; //元テーブルのキーにする
```
このドメインクラスの主キーと
元テーブルを特定するためのキーを保持するプロパティを用意しておく

```
@ManyToOne(fetch=FetchType.EAGER)
	@JoinColumn(nullable = false,insertable=false, updatable=false, name = "t_mitsumori_hdr_seq")
	private HeaderEntity headerEntity;
```
nameで元テーブルを特定するために、このドメインクラスで持っているプロパティを指定
（ここで指定したプロパティの値を元に、元テーブルへの検索がかかる）



## 利用

### ソース例

```
HeaderEntity entH = mitsumoriHdrService.findOne(id);
```
### 説明
mitsumoriHdrServiceは、リポジトリを操作する@Serviceクラスで、リポジトリクラスに検索を行っている
idに指定した値で検索をかけるが、このとき、ドメイン側の主キーに対して検索を行う
ドメインクラスでは、HeaderEntity(元クラス)のキーは以下だった

```
  @Id
    @GeneratedValue
    private Integer t_mitsumori_hdr_seq;
```
ため、t_mitsumori_hdr_seqに対して、idの値で検索がかかる


多側の関連テーブルは、この検索時に同時に検索される
元ドメインに、getterすると
entH.getMitsumoriDtl()
[元ドメインのオブジェクト.get関連テーブルの入るプロパティ名();]

```java:元ドメインクラスでの定義
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "headerEntity")
	private List<MitsumoriDtl> mitsumoriDtl;
```
に従って、
mitsumoriDtlがListクラスで取得されてくる。


```java
		HeaderEntity entH = mitsumoriHdrService.findOne(id);
		System.out.println("entH" + entH);
		System.out.println("entM" + entH.getMitsumoriDtl());
```

出力結果

```
entHHeaderEntity(t_mitsumori_hdr_seq=1, mitsumori_hdr_no=1, mitsumori_ymd=2015-01-01 00:00:00.0, kokyaku_kaisha_mei=123, hakko_kaisha_mei=123, hakko_kaisha_yubin_no=123, hakko_kaisha_todofuken=123, hakko_kaisha_jusho=123, hakko_kaisha_tel=123, hakko_kaisha_fax=123, anken_mei=123, nounyu_basho=123, nounyu_kigen=123, torihiki_joken=123, mitsumori_kigen=123, mitsumori_shokei_zeinuki=123, mitsumori_nebiki_gaku=123, mitsumori_goukei_zeinuki=123, shouhizei_ritsu=123.0, shouhizei_gaku=123, mitsumori_goukei_zeikomi=123, kenshu_ymd=2015-01-01 00:00:00.0, shiharai_ymd=2015-01-01 00:00:00.0, tokki_jiko=123, sys_create_user=123, sys_create_ymdhm=2015-01-01 00:00:00.0, sys_create_class=123, sys_update_user=123, sys_update_ymdhm=2015-01-01 00:00:00.0, sys_update_class=123, sys_delete_user=123, sys_delete_ymdhm=2015-01-01 00:00:00.0, sys_delete_class=123, sys_delete_no=123, sys_version=1)
entM[MitsumoriDtl(t_mitsumori_dtl_seq=1, mitsumori_hdr_no=123, mitsumori_dtl_no=123, mitsumori_dtl_mei=123, mitsumori_dtl_suryo=123.0, mitsumori_dtl_suryo_tani=123, mitsumori_dtl_tanka=123, mitsumori_dtl_gaku=123, mitsumori_dtl_biko=123, t_mitsumori_hdr_seq=1, sys_create_user=123, sys_create_ymdhm=2015-01-01 00:00:00.0, sys_create_class=123, sys_update_user=123, sys_update_ymdhm=2015-01-01 00:00:00.0, sys_update_class=123, sys_delete_user=123, sys_delete_ymdhm=2015-01-01 00:00:00.0, sys_delete_class=123, sys_delete_no=123, sys_version=1, headerEntity=HeaderEntity(t_mitsumori_hdr_seq=1, mitsumori_hdr_no=1, mitsumori_ymd=2015-01-01 00:00:00.0, kokyaku_kaisha_mei=123, hakko_kaisha_mei=123, hakko_kaisha_yubin_no=123, hakko_kaisha_todofuken=123, hakko_kaisha_jusho=123, hakko_kaisha_tel=123, hakko_kaisha_fax=123, anken_mei=123, nounyu_basho=123, nounyu_kigen=123, torihiki_joken=123, mitsumori_kigen=123, mitsumori_shokei_zeinuki=123, mitsumori_nebiki_gaku=123, mitsumori_goukei_zeinuki=123, shouhizei_ritsu=123.0, shouhizei_gaku=123, mitsumori_goukei_zeikomi=123, kenshu_ymd=2015-01-01 00:00:00.0, shiharai_ymd=2015-01-01 00:00:00.0, tokki_jiko=123, sys_create_user=123, sys_create_ymdhm=2015-01-01 00:00:00.0, sys_create_class=123, sys_update_user=123, sys_update_ymdhm=2015-01-01 00:00:00.0, sys_update_class=123, sys_delete_user=123, sys_delete_ymdhm=2015-01-01 00:00:00.0, sys_delete_class=123, sys_delete_no=123, sys_version=1))]

```

HeaderEntityが取得されてくる際
MitsumoriDto(関連テーブルが、配列で取得されてきている)

## 永続化
元クラスへsaveを実行すると、関連テーブル含めて永続化が行われる

#参考
http://etc9.hatenablog.com/entry/20090831/1251728605