5 분 소요

데이터베이스 스키마 자동 생성

hibernate.ddl.auto

  • create: 기존 테이블 삭제 후 다시 생성 (DROP + CREATE)
  • create-drop: create와 같으나 종료시점에 테이블 DROP
  • update: 변경분만 반영 (운영DB에는 사용하면 안됨)
  • validate: 엔티티와 테이블이 정상 매핑되었는지만 확인
  • none: 사용하지 않음

주의

🚨 운영 장비에는 절대 create, create-drop, update 사용하면 안된다.

  • 개발 초기 단계는 create 또는 update
  • 테스트 서버는 update 또는 validate
  • 스테이징과 운영 서버는 validate 또는 none

운영 서버 같은 경우에는, 쿼리 스크립트를 짜서 직접 생성하는 것이 좋다!

필드와 컬럼 매핑

매핑 어노테이션

  • @Column: 컬럼 매핑
  • @Temporal: 날짜 타입 매핑
  • @Enumerated: enum 타입 매핑
  • @Lob: BLOB, CLOB 매핑 (varchar를 넘어서는 큰 컨텐츠)
  • @Transient: 특정 필드를 컬럼에 매핑하지 않음 (매핑 무시)

연관관계 매핑 기초

단방향 연관관계

스크린샷 2022-08-02 오후 11 15 16

Member.java

@Entity
public class Member {

  @Id @GenerateValue
  private Long id;

  @Column(name = "USERNAME")
  private String username;

  @ManyToOne
  @JoinColumn(name = "TEAM_ID")
  private Team team;
}

Team.java

@Entity
public class Team {

  @Id @GenerateValue
  private Long id;

  @Column(name = "NAME")
  private String name;
}

단방향 연관관계에서는
member가 속한 team을 조회할 수는 있지만,
team에 어떤 member가 속해있는지는 조회할 수 없다.

양방향 연관관계

스크린샷 2022-08-02 오후 11 15 25

Member는 단방향과 같다.

Team.java

@Entity
@Getter
@Setter
public class Team {

  @Id @GenerateValue
  private Long id;

  @Column(name = "NAME")
  private String name;

  @OneToMany(mappedBy = "team") // 🌟 나는 연관관계 거울
  List<Member> members = new ArrayList<Member>();
}

양방향 연관관계에서는
member가 속한 team을 조회할 수도 있고,
team에 어떤 member가 속해있는지도 조회할 수 있다.

이때, mappedBy 옆에는 연관관계 주인이 가지고 있는 “변수명”을 적어준다. (Member가 가지고 있는 Team의 변수명은 team이다.)

연관관계 주인 (Owner)

양방향 매핑 규칙

  • 객체의 두 관계 중 하나를 연관관계의 주인으로 지정
  • 연관관계의 주인만이 외래 키를 관리 (등록, 수정)
    - 주인이 아닌 쪽은 읽기만 가능
  • 주인은 mappedBy 속성 사용 X
    - 주인이 아니면 mappedBy 속성으로 주인 지정

누구를 주인으로?

  • 외래 키가 있는 곳을 연관관계 주인으로 정해라 🌟
    - 외래 키는 더 작은 집합에 있다. (1:N에서의 N쪽!)
  • 여기서는 Member.team이 연관관계의 주인
  • 비즈니스 로직을 기준으로 연관관계 주인을 선택하면 안된다. (더 중요하다고 연관관계 주인 -> X)

스크린샷 2022-08-02 오후 11 21 12

양방향 매핑 시 가장 많이 하는 실수

  • 연관관계 주인에 값을 세팅해야 하는데, 거울 쪽에 세팅하는 경우
    -> 연관관계 거울은 읽기 전용이기 때문에 값이 들어가지 않는다.


순수한 객체 관계를 고려하면, 양쪽 다 값을 설정해야 한다.

  • 연관관계 편의 메서드를 생성하자
  • 양방향 매핑시에 무한루프를 조심하자
    - 예: toString(), lombok, JSON 생성 라이브러리
    - 컨트롤러에서 Entity를 반환하지 말자! 🚨 (Entity를 컨트롤러에서 반환할 때에는 Dto로 반환하자)

연관관계 편의 메서드

Member.java

@Entity
@Getter
@Setter
public class Member {

  @Id @GenerateValue
  private Long id;

  @Column(name = "USERNAME")
  private String username;

  @ManyToOne
  @JoinColumn(name = "TEAM_ID")
  private Team team; 

  public void changeTeam(Team team){ // 🌟 
    this.team = team;
    team.getMembers().add(this);
  }
}

편의 메서드의 네이밍을 setTeam()이 아닌 changeTeam()과 같이 한다면,
단순한 getter, setter 관례에 의한 것이 아니라 무언가 중요한 것임을 나타낼 수 있다.

Team.java

@Entity
@Getter
@Setter
public class Team {

  @Id @GenerateValue
  private Long id;

  @Column(name = "NAME")
  private String name;

  @OneToMany(mappedBy = "team") 
  List<Member> members = new ArrayList<Member>();

  public void addMember(Member member){ // 🌟 
    member.setTeam(this);
    members.add(member);
  }
}

🚨 연관관계 메서드는 Member와 Team 둘 중에 한 곳에만 넣어야 한다! (changeTeam()addMember() 중 하나만 정의!)

어느 곳에 정의할 지는 마음대로 하면 된다. (꼭 연관관계 주인에 정의해야하는건 아니다.)
1 쪽에 정의할 지 N 쪽에 정의할 지는 개발하는 애플리케이션의 상황마다 다르기 때문이다.

양방향 매핑 정리

  • 실무에서 JPA 모델링을 할 때, 단방향 매핑만으로 처음에 설계를 끝내야 한다. 🌟
  • 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐이다.
  • JPQL에서 역방향으로 탐색할 일이 많음
  • 단방향 매핑을 잘 하고 양방향은 필요할 때 추가해도 된다. (테이블에 영향을 주지 않음)

다양한 연관관계 매핑

연관관계 매핑 시 고려해야할 사항

  • 다중성
  • 단방향, 양방향
  • 연관관계 주인

다대일 [N:1]

다대일 단방향

  • 가장 많이 사용하는 연관관계
  • 다대일의 반대는 일대다

다대일 양방향

  • 외래 키가 있는 쪽이 연관관계 주인
  • 양쪽을 서로 참조하도록 개발

일대다 [1:N]

일대다 단방향

  • 일대다(1:N) 단방향은 1 쪽이 연관관계 주인
  • 테이블의 일대다 관계는 항상 N 쪽에 외래 키가 있음
  • 객체와 테이블의 차이 때문에 반대편 테이블의 외래 키를 관리하는 특이한 구조
  • @JoinColumn을 쪽 사용해야 함. 그렇지 않으면 조인 테이블 방식을 사용함 (중간에 테이블을 하나 추가함)
  • 일대다 단방향 매핑의 단점
    - 엔티티가 관리하는 외래 키가 다른 테이블에 있음 😱
    - 연관관계 관리를 위해 추가로 UPDATE SQL 실행
  • 일대다 단방향 매핑 보다는 다대일 양방향 매핑을 사용하자! 🌟

일대다 양방향

  • 이런 매핑은 공식적으로 존재 X
  • @JoinColumn(name=table_id, insertable=false, updateable=false)
  • 읽기 전용 필드를 사용해서 양방향처럼 사용하는 방법
  • 다대일 양방향을 사용하자

일대일 [1:1]

  • 일대일 관계는 그 반대도 일대일
  • 주 테이블이나 대상 테이블 중에 외래 키 선택 가능
  • 외래 키에 데이터베이스 유니크(UNI) 제약조건 추가

일대일: 주 테이블에 외래 키 단방향

Locker.java

@Entity
@Getter
@Setter
public class Locker {

  @Id @GenerateValue
  private Long id;

  private String name;
}

Member.java

@Entity
@Getter
@Setter
public class Member {

  @Id @GenerateValue
  private Long id;

  @Column(name = "USERNAME")
  private String username;

  @OneToOne
  @JoinColumn(name = "LOCKER_ID")
  private Locker locker;
}
  • 다대일(@ManyToOne) 단방향 매핑과 유사

🤔 추후에 비즈니스 룰이 변경된다면?

현재는 하나의 회원이 하나의 사물함만 사용할 수 있지만,
미래에 하나의 회원이 여러 사물함을 사용할 수 있도록 비즈니스 룰이 변경된다면,

“주 테이블에 외래 키 단방향”을 사용하는 것보다
“대상 테이블에 외래 키 양방향”을 사용하는게 변경에 용이하다.
(대상(LOCKER) 테이블의 UNI 제약조건만 빼면 여러 멤버를 인서트 할 수 있다.)

하지만, JPA를 하는 사람들 입장에서는 “주 테이블에 외래 키 단방향”을 사용하는게 실무에 편하다.
각각 장단점이 있다..
(영한님은 먼 미래를 생각하지 않고 “주 테이블에 외래 키 단방향”을 선호한다고 한다.)

일대일: 주 테이블에 외래 키 양방향

Member는 단방향과 같다.

Locker.java

@Entity
@Getter
@Setter
public class Locker {

  @Id @GenerateValue
  private Long id;

  private String name;

  @OneToOne(mappedBy= "locker") // 🌟
  private Member member;
}
  • 다대일 양방향 매핑처럼 외래 키가 있는 곳이 연관관계의 주인
  • 반대편은 mappedBy 적용

일대일: 대상 테이블에 외래 키 단방향

  • 단방향 관계는 JPA 지원 X
  • 양방향 관계는 지원

일대일: 대상 테이블에 외래 키 양방향

  • 사실 일대일 주 테이블에 외래 키 양방향과 매핑 방법으 같음

일대일 정리

  • 주 테이블에 외래 키
    • 주 객체가 대상 객체의 참조를 가지는 것 처럼
 주 테이블에 외래 키를 두고 대상 테이블을 찾음
    • 객체지향 개발자 선호
    • JPA 매핑 편리
    • 장점: 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인 가능
    • 단점: 값이 없으면 외래 키에 null 허용
  • 대상 테이블에 외래 키
    • 대상 테이블에 외래 키가 존재
    • 전통적인 데이터베이스 개발자 선호
    • 장점: 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조 유지
    • 단점: 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시 로딩됨(프록시는 뒤에서 설명)

다대다 [N:N]

  • 관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없음
  • 연결 테이블을 추가해서 일대다, 다대일 관계로 풀어내야함
  • 객체는 컬렉션을 사용해서 객체 2개로 다대다 관계 가능


💛 개인 공부 기록용 블로그입니다. 👻

맨 위로 이동하기