HyeLog

[김영한_자바 ORM 표준 JPA 프로그래밍 - 기본편] 3. 엔티티 매핑(2) 본문

웹 개발/Spring Boot

[김영한_자바 ORM 표준 JPA 프로그래밍 - 기본편] 3. 엔티티 매핑(2)

shj718 2022. 6. 10. 18:24

※ 이번 실습 때 내가 설치한 H2 데이터베이스 버전 때문에 @GeneratedValue 가 제대로 작동하지 않아서, H2를 아예 삭제하고 1.4.200 버전으로 재설치했다. 재설치할 때 기존 H2로 인해 만들어진 파일들을 꼭 삭제해야 한다.

(참고: https://www.inflearn.com/questions/429626 , https://www.inflearn.com/questions/459129)

 

👩‍💻기본키(PK) 매핑

  • 직접 할당@id
  • 자동 생성@GeneratedValue

@GeneratedValue는 데이터베이스에서 PK가 자동으로 생성되게 하고 싶을 때 사용한다.

 

GenerationType은 3가지(TABLE / IDENTITY / SEQUENCE)가 있다. 디폴트는 AUTO로, 3가지 중 데이터베이스 방언에 맞는 것으로 자동 설정된다.

 

IDENTITY

IDENTITY는 MySQL의 AUTO_INCREMENT 라고 생각하면 된다.

@Entity
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name", nullable = false)
    private String username;

    // JPA 는 기본 생성자가 필수.
    public Member() {

    }

    // getter, setter 함수들
}

아래와 같이 username만 set해주면, id는 자동으로 증가해서 INSERT된 것을 볼 수 있다.

Member member = new Member();
member.setUsername("C");
em.persist(member);

transaction.commit();

 

SEQUENCE

SEQUENCE는 Oracle에서 사용한다. Table마다 시퀀스를 따로 관리해야 하기 때문에 @SequenceGenerator로 각각 이름을 지정한다.

⭐주의할 점은 allocationSize = 1 설정을 해주어야 1씩 증가한다. 설정하지 않으면, 50이 디폴트값이기 때문에 1번째 데이터의 id가 1일 때 2번째 데이터의 id는 52가 된다. allocationSize를 50~100으로 크게 설정해서 성능 최적화를 할 수도 있다.

@Entity
@SequenceGenerator(name = "MEMBER_SEQ_GENERATOR", sequenceName = "MEMBER_SEQ", initialValue = 1, allocationSize = 1)
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQ_GENERATOR")
    private Long id;
    
    // 생략
}

 

권장하는 기본키(PK) 매핑 전략

Long형 + 대체키(비즈니스와 관련 없는 값. 주민등록번호도 적절하지 않음) + 키 생성전략(Ex. UUID) 사용

 

👩‍💻 실전 예제

간단한 쇼핑몰을 예로 들어보자. DB의 Table은 다음과 같다.

(회원 : 주문 = 1 : N , 주문 : 상품 = N : M 관계이다. )

  • MEMBER 테이블
  • ORDERS 테이블 (MEMBER_IDFK로 가짐)
  • ITEM 테이블
  • ORDER_ITEM 테이블

이제 엔티티를 설계해야 한다. 만약, 엔티티를 DB 테이블과 완전히 똑같이 설계해서, Order 클래스(엔티티)의 필드에 MemberId를 넣으면 어떻게 될까?

@Entity
@Table(name = "ORDERS")
public class Order {
    
    @Column(name = "MEMBER_ID")
    private Long memberId;
    
    // 다른 필드는 생략
}

'주문을 한 회원 정보'를 가져오려면, 아래와 같이 memberId를 가져와서 또 그것으로 회원을 조회해야 한다.

Order order = em.find(Order.class, 1L);
Long memberId = order.getMemberId();

Member member = em.find(Member.class, memberId);

이렇게 데이터 중심 설계를 하는 것은 좋지 않다. 객체 중심적인 설계로 바꾸어보자.

@Entity
@Table(name = "ORDERS")
public class Order {
    
    private Member member;
    
    // 다른 필드는 생략
}

이렇게 Member 객체 자체를 필드로 가지게 되면, 문제가 해결된다.