🍉
JPAの@OneToOneとは
@OneToOne(一対一)
- 一対一関係はその反対も一対一である。
- 1:1なのでFKにデータベース
UNIQUE
制約を追加する必要がある - つまり注テーブルと対象テーブルの内どちらでもFKを置くことができる
- 二通り考えておく必要がある
- 注テーブルにFKを置いた場合
- 対象テーブルにFKを置いた場合
1. 注テーブルにFKを置いた場合
片方向
- ここで注テーブル(FKを持っている)は
Member
である -
JpaMain
は無視してください。
Member.java
@Entity
@Getter @Setter
@NoArgsConstructor
@Table(name = "member", uniqueConstraints = {@UniqueConstraint(name = "member_locker", columnNames = {"locker_id"})}) // FKにUNIQUE制約追加
public class Member {
// ...
@OneToOne
@JoinColumn(name = "locker_id")
private Locker locker;
}
Locker.java
@Entity
@Setter @Getter
@Table(name = "locker") // springbootを使う場合小文字に自動変換されるのでname指定無要
public class Locker {
@Id
@Column(name = "locker_id")
private Long id;
private String name;
}
- DDL自動生成機能を使っても基本
@OneToOne
アノテーションだけではUNIQUE
制約を自動的に追加してくれない。@Table
のUNIQUE制約オプションを使うかデータベースに直接指定する必要がある。
@Table(name = "member", // テーブルのname指定
uniqueConstraints = {
@UniqueConstraint(
name = "member_locker", // UNIQUEキーのname指定
columnNames = {"locker_id"} // UNIQUE制約を追加するカラム名
)})
- もちろん、読みやすさの観点からデータベースにもオブジェクトにも併記して置いた方がいい
- テータベースを一々確認しなくてもオブジェクトを見るだけで把握できる。
- 一対一は多対一(@ManyToOne)と類似している。
双方向
- 多対一双方向みたいにFKがある所がリレーションのオーナー
- 反対側には
mappedBy
を適用
Locker.java
@Entity
@Table(name = "locker")
public class Locker {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "locker_id")
private Long id;
private String name;
@OneToOne(mappedBy = "locker")
private Member member;
}
Member.java
@Entity
@Table(name = "member", uniqueConstraints = {@UniqueConstraint(name = "member_locker", columnNames = {"locker_id"})}) // FKにUNIQUE制約追加
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "member_id")
private Long id;
private String username;
@OneToOne
@JoinColumn(name = "locker_id")
private Locker locker;
}
注テーブルにFKを置いた場合のまとめ
- 主オブジェクトが対象オブジェクトの参照を持つみたいに主テーブルにFKを置いて対象テーブルを探す。
- JPAマッピング管理が楽のためOOP開発者が選り好みする。
- 長所: 主テーブルを照会するだけで対象テーブルにデータがあるかどうか確認できる
-
短所: 値がないとFKに
null
を許さなければ成らない
2. 対象テーブルにFKを置いた場合
片方向(提供しない)
* JPAが提供しないマッピング
双方向
- 1:1主テーブルにFK双方向とマッピングする方法は一緒。
Locker.java
@Entity
@Table(name = "locker", uniqueConstraints = {@UniqueConstraint(name = "locker_member", columnNames = {"member_id"})}) // FKにUNIQUE制約追加
public class Locker {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "locker_id")
private Long id;
private String name;
@OneToOne
@JoinColumn(name = "member_id")
private Member member;
}
Member.java
@Entity
@Table(name = "member")
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "member_id")
private Long id;
private String username;
@OneToOne(mappedBy = "member")
private Locker locker;
}
対象テーブルにFKまとめ
- 対象テーブルにFKが存在
- 伝統的なDBAが選り好みする
- 今はMemberが一つしかLockerを持たないが後で政策が変わり沢山持たれることになると、DBAとしてはUNI制約だけ消せば済む。
- 長所:主テーブルと対象テーブルの関係を1:1から一対多に関係を変更してもテーブルの構造を維持することが出来る。
-
短所:Proxy機能の限界でLazy Loadingに設定しても常にEager Loadingされる。
- Proxyに関しては後で説明します。
次(@ManyToMany)
Discussion