Jpa/series
[jpa 3편] OneToOne 정리
무대포 개발자
2022. 6. 16. 22:51
728x90
반응형
목차는 jpa series 목차 에 있습니다.
source 는 Github 에 있습니다.
[jpa 3편] OneToOne 정리
OneToOne JoinColumn, mappedBy 상세 설명
- JoinColumn 은 조인 대상이 되는 정보를 명시하는 annotation 입니다.
- JoinColumn(name = "외래키 이름", referenceColumn = "조인할 컬럼")
- referenceColumn 이 설정 되지 않으면 기본 값으로 조인할 테이블의 PK 가 설정 됩니다.
- 여기서는 Address 테이블의 address_id 로 설정 됩니다. (아래 예시 참조)
- 여기서 주의할게 JoinColumn(name = "외래키 이름") 은 Foreign Key 의 이름입니다. 실제 조인할 컬럼은 referenceColumn 입니다.
- JoinColumn 을 가진 Member 테이블이 연관관계의 주인이 되며, address_id 는 Member 테이블에 생깁니다.
- mappedBy 는 연관관계 주인이 아닌 테이블에서 연관관계 주인 테이블과 맵핑하기 위해 사용합니다.
public class Member {
@OneToOne
@JoinColumn(name = "address_id")
private Address address;
}
public class Address {
@Id @Column(name = "address_id")
private Long id;
@OneToOne(mappedBy = "address")
private Member member;
}
insertable, updateable, cascade 정리
insertable
엔티티 저장시, 해당 필드도 같이 저장합니다. (default true)
false로 설정하면, insertable = false 로 설정한 필드는 저장할 때, DB에 저장하지 않습니다.
즉, false 옵션은 읽기 전용일 때, 사용합니다.
updatable
엔티티 수정시, 해당 필드도 같이 수정합니다. (default true)
false로 설정하면, updateable = false 로 설정한 필드는 수정할 때, DB 필드를 수정하지 않습니다.
즉, false 옵션은 읽기 전용일 때, 사용합니다.
cascade
cascade 는 영속성 전이를 의미합니다.
부모 엔티티가 영속화될 때 자식 엔티티도 같이 영속화 되거나, 부모 엔티티가 삭제될 때, 자식 엔티티도 삭제되는 등 영속성 전이 기능을 구현할 수 있습니다.
CascadeType.ALL: 모든 Cascade를 적용합니다.
CascadeType.PERSIST: 엔티티를 영속화할 때, 연관 엔티티도 함께 persist 합니다.
CascadeType.MERGE: 엔티티 상태를 Merge 할 때, 연관된 엔티티도 모두 병합합니다.
CascadeType.REMOVE: 엔티티를 제거할 때, 연관된 엔티티도 모두 제거합니다.
CascadeType.DETACH: 부모 엔티티를 detach() 하면, 연관 엔티티도 detach() 상태가 됩니다.
CascadeType.REFRESH: 상위 엔티티를 refresh 할 때, 연관된 엔티티도 새로고침 합니다.
OneToOne 특징 (lazy loading)
- 테이블을 조회할 때, 외래 키를 갖고 있는 연관관계 주인 entity 에서는 지연로딩이 동작하지만, mappedBy 로 연결된 테이블은 지연로딩이 동작하지 않습니다.
- 연관관계 주인이 아닌 entity 에서는 N + 1 쿼리가 발생합니다.
- 왜 이런 현상이 발생하는지 예시는 다음과 같습니다.
- Member 와 Address 는 1:1 관계라고 가정하겠습니다.
- 아래 예시를 보면 연관관계 주인은 Member 이기에 FK 를 Member 테이블에서 관리합니다. Member 테이블에 address_id 컬럼이 존재합니다.
- Member 테이블을 조회할 때는 쿼리가 1번만 발생하지만 Address 테이블을 조회할 때는 쿼리가 n+1 만큼 실행이 됩니다.
- 왜냐하면 Address 테이블에는 Member 에 대한 정보가 없기에 Member 테이블을 조회해야지만 Member 정보를 가져올 수 있기 때문입니다.
public class Member {
@OneToOne
@JoinColumn(name = "address_id")
private Address address;
}
public class Address {
@Id @Column(name = "address_id")
private Long id;
@OneToOne(mappedBy = "address")
private Member member;
}
OneToOne Example Source
- entity
@Entity
@Getter
@NoArgsConstructor
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
@Setter
private Long id;
@Column
private String name;
@Column
private String telNo;
@Column
private int age;
@OneToOne
@JoinColumn(name = "address_id")
private Address address;
public void setAddress(Address address) {
this.address = address;
this.address.setMember(this);
}
- test source
@Test
public void MEMBER_ONE_TO_ONE_테스트() throws Exception {
Member member = Member.builder()
.age(10)
.name("test")
.telNo("123")
.build();
Address address = Address.builder()
.addr("주소")
.build();
// 양방향 연결
member.setAddress(address);
memberRepository.save(member);
addressRepository.save(address);
// when
List<Member> list = memberRepository.findAll();
Address addr = list.get(0).getAddress();
// then
Assert.assertThat(addr.getAddr(), is("주소"));
}