course/inflearn

[JPA 프로그래밍 기본편] 엔티티 매핑

hjkim0502 2022. 8. 31. 01:16

객체와 테이블 매핑

@Entity // JPA가 관리하는 엔티티, 동명의 클래스 없다면 이름 설정 X
//@Table(name = "USER") // 클래스명이 DB 테이블명과 불일치 한다면 매핑 가능
public class Member {
  • 기본 생성자 필수
  • final 클래스, enum 클래스, 중첩 클래스, 인터페이스 사용 X
  • 저장할 필드에 final 사용 X

 

DB 스키마 자동 생성: 애플리케이션 실행 시점에 DDL 자동 생성 (테이블 만들어 놓고 시작)

  • 객체 중심적으로 매핑해놓으면 JPA가 알아서 테이블은 만들어 줌
  • DB 방언 설정으로 적절한 DDL 생성 -> 개발 장비에서만 사용
    • 운영 서버에서는 사용하지 않거나, 잘 다듬어서 사용
옵션 설명
create 기존 테이블 삭제 후 다시 생성 (DROP -> CREATE)
create-drop 기존 테이블 삭제 후 다시 생성 + 종료 시점에 테이블 삭제 (DROP -> CREATE -> DROP)
update 변경 부분만 반영 (ALTER)
validate 엔티티와 테이블의 정상 매핑 여부 확인
none(관례상의 표현) 미사용
// resources/META-INF/persistence.xml

<property name="hibernate.hbm2ddl.auto" value="create" />
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.hbm2ddl.auto" value="validate" />
<property name="hibernate.hbm2ddl.auto" value="none" />
  • update 옵션에서 필드를 추가하면 반영되지만 필드를 없앤 것은 반영되지 않음
  • 주의! 운영 장비에는 절대 create, create-drop, update 사용 X
    • (로컬) 개발 초기 단계에는 create, update
    • (여럿이 쓰는) 테스트 서버에는 update, validate
    • 스테이징과 운영 서버는 validate, none
    • 가급적 테스트와 운영 단계에서도 사용하지 않는 것이 권장됨 (직접 스크립트를 작성하여 다듬기)
  • DDL 생성 기능은 DDL 자동 생성에만 영향을 주고, JPA 실행 로직에는 영향이 없다
    • @Table의 name 속성을 바꾸는 것은 실행 로직에 영향이 있지만,
    • @Column의 nullable, length, unique... 등은 실행 로직에 영향 주지 않는다

필드와 컬럼 매핑

  • 일반 회원, 관리자 구분
  • 회원 가입일과 수정일
  • 회원 설명 필드(길이 제한 X)
@Id
private Long id;

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

private Integer age;

@Enumerated(EnumType.STRING) // ORDINAL 사용 X (순서에 의한 문제 발생 가능)
private RoleType roleType;

@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;

@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;

// 최신 버전은 @Temporal 없이 아래와 같이 날짜 타입 매핑하면 됨
private LocalDate localDate;
private LocalDateTime localDateTime;

@Lob // 큰 데이터 (문자면 CLOB, 나머지는 BLOB)
private String description;

@Transient // 매핑하지 않음
private Integer temp;

 

기본 키 매핑

@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
  • 직접 할당: @Id만 사용
  • 자동 생성: @Id와 @GeneratedValue 사용
    • AUTO: 방언에 따라 자동 지정 (기본값) 
    • IDENTITY: 기본 키 생성을 데이터베이스에 위임 (예: MySQL의 Auto Increment)
      • DB에 INSERT문이 실행된 후 ID값을 알 수 있으므로 em.persist() 하는 시점에 필요한 ID 값이 없다
      • 따라서 예외적으로 persist() 하는 시점에 즉시 INSERT문이 실행되어 DB에서 식별자 조회
    • SEQUENCE: 유일한 값을 순서대로 생성하는 시퀀스 오브젝트를 활용 (예: 오라클 시퀀스)
      • persist() 할 때 시퀀스 DB에서 ID값만 불러와서 영속성 컨텍스트에 엔티티 영속
      • 따라서 IDENTITY와 다르게 쿼리들을 버퍼링했다가 커밋할 때 한번에 전달 가능
      • 그럼에도 영속할 때마다 DB와 통신한다는 성능 이슈 때문에 allocationSize = 50 이 기본값
        • 미리 DB에 50까지 만들어 놓고 ID가 50까지 증가하는 동안은 그 값을 메모리에서 불러옴
        • ID값을 DB를 통해 불러오는 경우가 획기적으로 줄며, 동시성 문제도 해결 가능
    • TABLE: 키 생성 전용 테이블을 만들어 데이터베이스 시퀀스 흉내내는 전략
      • 시퀀스와 마찬가지로 allocationSize를 통해 성능 최적화 
      • 장점: 모든 DB에 적용 가능
      • 단점: 성능
@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

 

@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;

 

@Entity
@TableGenerator(
		name = "MEMBER_SEQ_GENERATOR",
		table = "MY_SEQUENCES",
		pkColumnValue = “MEMBER_SEQ", allocationSize = 1)
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE,
    generator = "MEMBER_SEQ_GENERATOR")
    private Long id;
  • 기본 키 제약 조건: 값 존재, 유일, 불변
  • 권장: Long 형 + 대체키(↔자연키) + 키 생성전략
    • 예) 주민등록번호 등의 비즈니스 관련 식별자(자연키)를 PK로 사용하다 바꾸어야 하는 경우 큰 문제가 된다

실전예제

@Entity
@Table(name = "ORDERS") // ORDER가 예약어로 걸려있는 DB에서는 이렇게 테이블명을 바꿔줘야 함
public class Order {

    @Id @GeneratedValue
    @Column(name = "ORDER_ID")
    private Long id;

    @Column(name = "MEMBER_ID") // 객체 설계를 테이블 설계에 맞춘 방식 -> 객체 그래프 탐색 불가능
    private Long memberId;
  • 객체 설계를 테이블 설계에 맞추지 않고 객체 지향적으로 할 수 있게 연관관계 매핑하는 법을 배워야 한다
  • (보통 DB 컬럼명은 카멜보다는 스네이크 형태로 작성하며, 스프링 부트는 이것을 자동 지원해준다)

출처: 인프런 김영한님 JPA 프로그래밍 - 기본편