노트 정리/자바 ORM 표준 JPA 프로그래밍

4. 엔티티 매핑

DuL2 2022. 8. 24. 09:25

엔티티 매핑

객체와 테이블 매핑

@Entity

@Entity가 붙은 클래스는 JPA가 관리하며 엔티티라 한다. JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 어노테이션이 필수이다.

 

주의
기본 생성자는 필수(파라미터가 없응 public 또는 protected 생성자)
리플렉션같은 다양한 기술을 사용하기 때문에 기술 스펙으로서 기본 생성자가 필요함

final 클래스, enum, interface inner 클래스 사용 x
저장할 필드에 final 사용 X

 

@Entity 속성정리

@Entity(name):

  • JPA에서 사용할 엔티티 이름을 정할 때 사용함.
  • 기본값: 클래스 이름을 사용하며 가급적 기본값을 사용해야 헷갈리지 않는다.

@Table:

  • name: 매핑할 테이블 이름(기본값: 엔티티 이름 사용)
  • catalog: 데이터베이스 catalog 매핑
  • schema: 데이터베이스 schema 매핑
  • uniqueConstraints: DDL 생성 시에 유니크 제약 조건 생성

 

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

  • DDL을 애플리케이션 실행 시점에 자동 생성(실제 운영에서 사용하지 않고 Local에서 테스트할 때 사용.)
  • 테이블 중심 -> 객체 중심
  • DB 방언에 따라서 알맞게 SQL문을 작성해줌.
  • 아래 옵션을 통해 테이블을 자동 생성해줌.
<property name="hibernate.hbm2ddl.auto" value="create" />

실제로 실행 해보면 다음과 같이 기존에 엔티티로 된 테이블이 있다면 날리고 새로운 Table을 생성하는 DDL을 볼 수 있다.

실제 DB도 깨끗이 지워진다.

 

스키마 자동 생성 - 속성 종류

  • create: 기존 테이블 삭제 후 다시 생성(DROP + CREATE)
  • create-drop: create와 같으나 종료시점에 테이블 DROP
  • update: 변경분만 반영(운영DB에는 사용하면 안됨)
    • 컬럼 삽입 변경은 가능하지만 삭제 변경은 적용 안됨.
    • 경험 상 OnDelete, OnUpdate와 같은 옵션도 적용 안됨.
  • validate: 엔티티와 테이블이 정상 매핑되었는지만 확인
  • none: 사용하지 않음

DB 방언에따라 스키마 생성이 변경됨.

 

스키마 자동 생성 - 주의 사용 시점

  • 운영 장비에는 create, create-drop, update 절대 사용 금지
  • 개발 초기 단계는 create 또는 update
  • 테스트 서버는 update 또는 validate
    • 테스트를 위한 더미 데이터가 날라가면 안됨
  • 스테이징과 운영 서버는 validate 또는 none

 

DDL 생성 기능

@Column(nullable = false) 등과 같이 제약 조건을 생성 하는 DDL 생성 기능은 DDL을 자동 생성할 때만 영향을 주고 JPA의 실행 로직에는 영향을 주지 않는다.

 

필드와 컬럼 매핑

코드와 DDL을 통해 이해해보자.

    @Id
    private Long id;
    @Column(name = "name")  //테이블 컬럼 변경
    private String username;
    private Integer age;
    //DB에는 없는 enum 타입을 사용할 수 있게 해준다.
    @Enumerated(EnumType.STRING)
    private RoleType roleType;
    //날짜 타입
    @Temporal(TemporalType.TIMESTAMP) //옵션 종류: DATA, TIME, TIMESTAMP(날짜 + 시간)
    private Date createdDate;
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastModifiedDate;
    @Lob    //Large Object: 문자타입의 경우 clob, 나머지는 바이트로 매핑되서 blob
    private String description;
    @Transient  //DB 컬럼으로는 만들고 싶지 않을 때 사용.(예: 메모리에서 캐시 대용으로 사용하고 싶을 때)
    private int nothing;

@Column 파헤치기

속성 설명 기본값 나의 사용 경험(예시)
name 필드와 매핑할 테이블의 컬럼이름 클래스 엔티티 이름값 User가 DB 예약어라 name="Users"로 변경할 때 사용하였음.
insertable,
updatable
등록, 변경 가능 여부 true 글 작성 시간, 생성 시간을 각각 설정하고 싶을 때 설정했었음.
nullable null 값의 허용 여부를 설정한다. false로 설정하면 DDL 생성 시에 not null 제약 조건이 붙는다.   DDL도 나감. SQL나갈때 null인지도 체크해줘서 sql Exception이 터짐.
unique(DDL) @Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용한다.   유니크 제약조건 명이 랜덤값으로 정해져서 실무에서는 사용하지 않는다고함.(보통 Table 사용)
columnDefinition 데이터베이스 컬럼 정보를 직접 줄 수 있음. 필드의 자바 타입과 방언 정보 사용 varchar(100) default 'EMPTY'처럼 사용 가능.
length(DDL) 문자 길이 제약조건, String 타입에만 사용 255  
precision,      
scale(DDL) BigDecimal 타입(숫자가 엄청 큰 경우)에서 사용한다(BigIntegar 사용 가능)
precision은 소수점을 포함한 전체 자릿수를, scale은 소수의 자릿수
   
다. 참고로 double, float 타입에는 적용되지 않는다. precision=19,    
scale=2      

@Enumerated 사용 주의

주의! ORDINAL 사용X

그러고보니 ordinal로 사용했던 경험이 있다. 장르를 DB에 넣고 싶을 때 enum을 사용했는데 DB의 값을 보니 숫자로 매핑되었었다. 새롭게 카테고리가 추가된 경우, 즉 Enum 타입에서 열거한 값이 순서가 바뀌거나 했을 경우 밀리게 되면서 문제가 생긴다.

Enum 파헤치기 글을 작성하면서 향로님글에서 봤던 부분인데 개발할 때 이 문제를 놓쳤다.

public enum RoleType {
    USER, ADMIN; //-> GUEST, USER, ADMIN;

GUEST, USER, ADMIN; 로 바뀌면 기존 유저로 등록되어있던 값이 Guest로 변경됨

그렇기 때문에 디폴트인 ORDINAL 말고 STRING을 사용한다.

@Temporal

Date에서 날짜, 시간, 날짜 + 시간을 쪼개 DB에 넣어줘야 할 경우.

  • 과거 버전 사용해야 할 경우: @Temporal 사용
  • 최신 버전의 경우에는 LocalDate, LocalDateTime을 지원하기 때문에 생략 가능

 

기본 키 매핑

기본 키 매핑 방법

  • 직접 할당: @Id만 사용
  • 자동 생성:(@GeneratedValue)
    • IDENTITY: 데이터베이스에 위임, MYSQL
      • em.persist 될 때 id 값을 받아 올 수 있음.
        • 이유는 영속성화 되려면 id값이 존재해야 하는데 DB에 insert되야지만 값을 알 수 있기 때문이다.
        • id 값을 select하지 않아도 알게 되는데 jdbc에서 자동적으로 id값은 받아오기 때문임.
    • SEQUENCE: 데이터베이스 시퀀스 오브젝트 사용, ORACLE
      • @SequenceGenerator 필요: 테이블마다 sequence를 정해주고 싶을 경우
      • DB로 부터 call next value 하여 id 값을 받아온 후 쿼리가 나감.
    • TABLE: 키 생성용 테이블 사용, 모든 DB에서 사용
      • @TableGenerater 필요
      • 키 생성 전용 테이블을 만들어 DB 시퀀스를 흉내내는 전략, 모든 DB에 적용 가능하지만 성능이 안좋을 수 있음.
    • AUTO: 방언에 따라 자동 지정, 기본값 사용

 

SEQUENCE 전략

시퀀스에서는 next value를 찾아와서 persist하고, commit할 때 SQL문을 flush함.

TABLE 전략

 

권장하는 식별자 전략

  • 기본키 제약 조건: Not Null, Unique, 불변성
  • 먼 미래까지 변하면 안된다는 조건을 만족하는 자연키(비지니스에서 의미있는 값)를 찾기 힘듬.
  • 그렇기에 대리키(대체키)를 사용하자.
  • 권장: Long형(10억이 넘는 값 수용 가능) + 대체키 + 키 생성전략 사용

 

allocationSize를 이용한 성능 개선

매번 db에서 다음 id value를 받아오면 성능에 문제가 생기므로 allocationSize를 설정하여 size 값만큼 미리 받아와서 쓰게 된다. 예를들어 50만큼을 가져오게 되면 1부터 쓰게 된다.

예전 오라클 DB를 사용할때도 캐시 개념처럼 20만큼씩 Auto Increment 되어 받고 db가 꺼지면 캐시에 들어있던 남은 id 값들은 사라지고 21부터 사용할 수 있었던 개념인 것 같다.

 

실전 예제 - 1. 요구사항 분석과 기본 매핑

데이터 중심 설계의 문제점

  • 객체 설계를 테이블 설계에 맞추게 되면...
    • 테이블 외래키를 객체에 그대로 가져옴
    • 객체 그래프 탐색이 불가능
      • order를 찾으면 userId를 받아다가 다시 user를 검색해야함.
    • 참조가 없으므로 UML도 잘못됨

이 문제를 해결하기 위해 연관관계를 매핑을 한다.