LoginSignup
26
32

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-03-30

目的

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

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

環境

STS
Java8
Spring Boot
Hibernate

構造

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

元(単一)
t_mitsumori_hdrテーブル
HeaderEntity ドメイン

関連テーブル(多)
t_mitsumori_dtlテーブル
MitsumoriDtl ドメイン

手順

ドメインクラス

ソース例

inport等略されているためこのままで動作するコードではありません、要点だけご確認ください
- 1側

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;

}

 説明

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 = 関連ドメインクラス)

余談)この辺で、[どっちがどっちだ!?] でパニクる


  • 多側
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でテーブルとこのドメインクラスを紐づける

    @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関連テーブルの入るプロパティ名();]

元ドメインクラスでの定義
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "headerEntity")
    private List<MitsumoriDtl> mitsumoriDtl;

に従って、
mitsumoriDtlがListクラスで取得されてくる。

        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を実行すると、関連テーブル含めて永続化が行われる

参考

26
32
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
32