지난 포스트에 이어서, 일대일 연관관계 시 대상 테이블에 외래키를 저장하는 형태에 대하여 알아보도록 하겠습니다.
일대일 연관관계를 회원(Member)과 해당 회원의 사물함(Locker)을 예시로 설명해보도록 하겠습니다.
일대일 (1:1)
관계는 양쪽이 서로 하나의 관계만 갖습니다.일대일 관계에는 주 테이블과 대상 테이블이 관계를 맺습니다.
이런 연관관계는 JPA 에서 지원하지 않습니다.
이런 비즈니스 로직이 있을때는 아래의 방법으로 연관관계를 생성합니다.
위 관계를 통해서 객체 및 테이블 모델링을 한 결과는 아래와 같습니다.
해당 객체 모델링을 코드로 나타내어 보도록 하겠습니다.
Member 클래스 (일대일
에서 주 테이블
에 해당합니다.)
public class Member {
@Id
@Column(name = "MEMBER_ID")
private Long id;
@OneToOne
@JoinColumn(name = "LOCKER_ID")
private Locker locker;
@Column(name = "USERNAME")
private String username;
// Getter, Setter, Constructor
}
Locker 클래스 (일대일
에서 대상 테이블
에 해당합니다.)
public class Locker {
@Id
@Column(name = "LOCKER_ID")
private Long id;
@Column(name = "NAME")
private String name;
@OneToOne(mappedBy = "locker")
private Member member;
// 순수한 객체를 고려한 연관관계 편의 메서드
public void setMember(Member member) {
if (this.member != null) {
this.member.setLocker(null);
}
this.member = member;
if (member != null) {
member.setLocker(this);
}
}
// Getter, Setter, Constructor
}
대상 테이블에 외래키를 저장하는 양방향 관계
일 경우, 주 테이블에 외래키를 저장하는 양방향 관계
일 경우와 반대로 코드를 작성해주시면 됩니다.
연관관계를 등록, 수정, 삭제, 조회하는 예제를 통해 연관관계를 어떻게 사용하는지 알아보겠습니다.
// 회원 저장
Member member = new Member(0L, "회원");
entityManager.persist(member);
// 사물함 저장
Locker locker = new Locker(0L, "사물함");
locker.setMember(member); // 연관관계 설정
entityManager.persist(locker);
JPA에서 엔티티를 저장할 때 연관된 모든 엔티티는 영속 상태이어야 합니다.
위 코드를 실행하였을 때, 실행되는 SQL 은 아래와 같습니다.
INSERT INTO MEMBER (MEMBER_ID, USERNAME) VALUES (0, "회원");
INSERT INTO LOCKER (LOCKER_ID, NAME, MEMBER_ID) VALUES (0, "사물함", 0);
객체그래프 탐색
member.getLocker() 을 사용해서 member 와 연관된 locker 엔티티를 조회할 수 있습니다.
Member member = entityManager.find(Member.class, 0L);
Locker locker = member.getLocker();
객체지향 쿼리 (JPQL) 사용
String jpql = "select m.locker from Member m where m.id = :memberId";
Locker locker = entityManager
.createQuery(jpql, Locker.class)
.setParameter("memberId", 0L)
.getSingleResult();
SELECT l.LOCKER_ID, l.NAME
FROM LOCKER l
JOIN MEMBER m ON l.LOCKER_ID = m.LOCKER_ID
WHERE m.MEMBER_ID = 0;
객체그래프 탐색
locker.getMember() 을 사용해서 locker 를 소유하고 있는 엔티티를 조회할 수 있습니다.
Locker locker = entityManager.find(Locker.class, 0L);
Member member = locker.getMember();
객체지향 쿼리 (JPQL) 사용
String jpql = "select l.member from Locker l where l.id = :lockerId";
Member member = entityManager
.createQuery(jpql, Member.class)
.setParameter("lockerId", 0L)
.getSingleResult();
SELECT m.MEMBER_ID, m.LOCKER_ID, m.USERNAME
FROM Member m
JOIN Locker l ON m.LOCKER_ID = l.LOCKER_ID
WHERE l.LOCKER_ID = 0;
사물함의 소유를 신규 회원으로 수정해보도록 하겠습니다.
Member member = new Member(1L, "신규 회원");
entityManager.persist(member);
Locker locker = entityManager.find(Locker.class, 0L);
locker.setMember(member);
UPDATE LOCKER
SET MEMBER_ID = 1, ...
WHERE
LOCKER_ID = 0;
회원의 사물함 기간이 종료되어, 소유하고 있던 사물함과의 연관관계를 제거해보도록 하겠습니다.
Locker locker = entityManager.find(Locker.class, 0L);
locker.setMember(null);
UPDATE LOCKER
SET MEMBER_ID = null, ...
WHERE
LOCKER_ID = 0;
사물함을 사용하지 못하게 되어, 사물함 객체를 삭제해보도록 하겠습니다.
Locker locker = entityManager.find(Locker.class, 0L);
entityManager.remove(locker);
DELETE
FROM LOCKER
WHERE LOCKER_ID = 0;