Post

[자바 ORM 표준 JPA 프로그래밍 - 기본편] 섹션 05. 엔티티 매핑

[자바 ORM 표준 JPA 프로그래밍 - 기본편] 섹션 05. 엔티티 매핑

1. 객체와 테이블 매핑


엔티티 매핑은 객체와 테이블, 필드와 컬럼, 기본 키, 연관관계 매핑을 모두 해줄 수 있고, 아래와 같은 애너테이션을 사용하면 된다.

  • 객체와 테이블 매핑: @Entity, @Table
  • 필드와 컬럼 매핑: @Column
  • 기본 키 매핑: @Id
  • 연관관계 매핑: @ManyToOne, @JoinColumn


@Entity가 붙은 클래스는 JPA가 관리하는 클래스로 엔티티라고 한다. 엔티티는 기본 생성자가 필수이다.(public or protected) final 클래스나 enum, interface, inner 클래스는 사용할 수 없으며 저장할 필드에 final을 사용할 수도 없다.


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


JPA는 DDL을 애플리케이션 실행 시점에 자동으로 생성할 수 있다. 이렇게 생성한 DDL은 개발 환경에서만 사용해야하며 운영 환경에서는 사용하지 않거나, 적절히 다듬은 후 사용해야 한다.

옵션으로는 아래와 같이 5가지가 있다.

  • create: 기존 테이블 삭제 후 다시 생성
  • create-drop: create와 같으나 종료 시점에 테이블 DROP
  • update: 변경분만 반영(운영 DB 사용 X)
  • validate: 엔티티와 테이블이 정상 매핑되었는지만 확인
  • none: 사용하지 않음(관례상 none이라고 쓰는 것으로 위 4가지에 매핑되지 않으면 다 none이다.)

개발 초기 단계에서는 create 또는 update, 테스트 서버는 update 또는 validate, 스테이징과 운영 서버는 validate 또는 none을 활용할 수 있으며, 운영 장비에는 절대 create, create-drop, update를 사용하면 안된다.


아래와 같이 create 옵션을 줄 수 있다.


애플리케이션을 실행하면 Member 테이블이 DROP된 후 새롭게 생성되는 것을 볼 수 있다.


H2에서도 기존 멤버들이 모두 사라진 것을 볼 수 있다.


Memberage 필드를 추가한 후 다시 애플리케이션을 실행하면 age 컬럼이 추가된 멤버 테이블이 새롭게 생성되는 것을 볼 수 있다.


H2에서도 AGE 컬럼이 생긴 것을 확인할 수 있다. 엔티티의 변경이 잦은 개발 단계에서 활용하기 좋다.


create-drop 옵션도 아래와 같이 주면 되며 애플리케이션을 실행하면 종료 후 테이블이 DROP되는 것을 볼 수 있다.


H2에서도 멤버 테이블이 조회되지 않는다.


update 옵션도 아래와 같이 주면 되며,


애플리케이션을 실행하면 idname 컬럼만 있는 테이블이 생성된다.


엔티티에 age 필드를 추가하고 애플리케이션을 실행하면 아래와 같이 ALTER문을 통해 age 컬럼이 추가되는 것을 볼 수 있다.


H2에서도 세 컬럼이 모두 잘 조회된다.


여기서 age 필드를 삭제하고 애플리케이션을 실행하면 아무 쿼리도 출력되지 않는다. 기존 필드를 삭제한다고 컬럼을 삭제하지는 않는다.


@Column 애너테이션의 속성으로 유니크 제약조건이나 길이를 명시할 수도 있다.


3. 필드와 컬럼 매핑


아래와 같은 요구사항에 대해 엔티티를 작성해보자.

  • 회원은 일반 회원과 관리자로 구분해야 한다.
  • 회원 가입일과 수정일이 있어야 한다.
  • 회원을 설명할 수 있는 필드가 있어야 한다. 이 필드는 길이 제한이 없다.


회원 권한은 아래와 같은 enum 클래스로 작성할 수 있다.


회원 엔티티는 아래와 같이 작성할 수 있다.

  • @Column의 옵션으로 필드명과 별개로 컬럼명을 지정할 수 있다.
  • @Enumerated에서 EnumType.STRING으로 열거 타입을 문자열로 저장할 것을 설정할 수 있다.
  • @Temporal에서 날짜/시간 필드의 타입을 설정할 수 있다.
  • @Lob로 길이 제한 없는 설명 필드를 추가할 수 있다.


애플리케이션을 실행하면 아래와 같이 스키마가 생성되는 것을 볼 수 있다.


@Enumerated의 경우 기본 옵션이 EnumType.ORDINAL인데 이는 열거 타입에 정의된 순서를 저장하는 것이다.


아래와 같이 @Enumerated에 아무 옵션을 주지 않고,


일반 회원을 한 명 저장할 경우,


ROLETYPE0으로 저장되는 것을 볼 수 있다. 이는 USER라는 권한이 RoleType에서 첫 번째로 선언된 상수이기 때문이다.


DDL을 update로 설정한 후,


ADMIN인 회원을 추가로 저장하면,


ADMIN인 회원의 ROLETYPE1로 저장된 것을 볼 수 있다.


이렇듯 ORDINAL로 옵션을 줄 경우 아래와 같이 정의한 열거 타입이 변하지 않으면 상관없을 수 있지만,


만약 GUEST라는 타입이 추가될 경우 기존 정의 순서가 꼬이면서 정합성 문제가 발생할 수 있다.


웬만하면(무조건) 열거 타입은 문자열로 저장할 수 있게 아래처럼 설정해주자.


기존 테이블을 DROP하고 GEUST인 회원을 저장할 경우,


아래와 같이 문자열이 그대로 잘 저장되는 것을 볼 수 있다.


4. 기본 키 매핑


기본 키 매핑에 사용할 수 있는 애너테이션은 @Id, @GeneratedValue 두 가지가 있다. 직접 할당할 경우에는 @Id만 사용하면 되며 자동 생성을 사용하고 싶을 경우 @GeneratedValue도 사용하면 된다.

@GeneratedValue는 아래과 같은 4가지 옵션이 있다.

  • IDENTITY: 데이터베이스에 위임
  • SEQUENCE: 데이터베이스 시퀀스 오브젝트 사용
  • TABLE: 키 생성용 테이블 사용
  • AUTO: 방언에 따라 자동 지정(기본 값)


기본 키를 직접 지정할 경우 아래와 같이 @Id만 사용하면 된다.


멤버를 생성해서 저장할 경우 기본 키도 지정을 해줘야 한다.


설정한 값대로 잘 저장된 것을 볼 수 있다.


기본 키 생성을 데이터베이스에 위임할 경우 아래와 같이 설정해주면 된다.


기본 키를 지정하지 않고 멤버를 생성해서 저장해도 INSERT 쿼리가 나가는 것을 볼 수 있다.


ID1로 저장된 것을 볼 수 있다.


한 번 더 저장하면 다음 멤버는 2로 저장된 것을 볼 수 있다.


persist 앞뒤로 로그를 찍어보면 persist를 할 때 INSERT 쿼리가 나가는 것을 볼 수 있는데 이는 데이터베이스에 저장을 해야 기본 키를 알 수 있기 때문에 persist에서 쿼리를 날리는 것이다.(1차 캐시도 엔티티의 기본 키가 있어야 한다) 별도의 SELECT 쿼리 없이 저장한 회원의 기본 키를 JDBC가 응답으로 받는다.


덕분에 트랜잭션이 종료되기 전에 저장한 엔티티의 기본키를 조회할 수 있다.


역시 잘 저장된 것을 볼 수 있다.


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


요구사항 분석

  • 회원은 상품을 주문할 수 있다.
  • 주문 시 여러 종류의 상품을 선택할 수 있다.

기능 목록

  • 회원 기능
    • 회원등록
    • 회원조회
  • 상품 기능
    • 상품등록
    • 상품수정
    • 상품조회
  • 주문 기능
    • 상품주문
    • 주문내역조회
    • 주문취소


테이블과 엔티티는 아래와 같이 설계하고 매핑할 수 있다.


실전 예제를 위해 프로젝트를 새로 생성해준다.


회원 엔티티는 아래와 같이 설계할 수 있다.


주문 엔티티는 아래와 같이 설계할 수 있다.


주문 상태는 두 가지를 사용했다.


상품 엔티티는 아래와 같이 설계할 수 있다.


주문 상품이라는 다대다 관계를 풀어낸 엔티티는 아래와 같이 설계할 수 있다.


H2도 새로운 DB로 연결해줬다.


persitence.xml의 H2 경로도 맞춰서 수정해줬다.


실행하면 에러가 나며 테이블이 생성되지 않았는데 ORDER가 H2 예약어여서 실패했다. 매핑할 테이블명을 ORDERS로 지정해줬다.


애플리케이션 실행 시 테이블들이 잘 생성되는 것을 로그로 볼 수 있다.


H2에서 새로고침을 해보면 모든 테이블이 잘 생성된 것을 볼 수 있다.


Ref



This post is licensed under CC BY 4.0 by the author.