[JPA] 자바 ORM 표준 프로그래밍 정리 #4

y__hi_2X·2022년 2월 18일
0

JPA

목록 보기
4/4

이 글은 김영한님의 '자바 ORM 표준 프로그래밍' 교재를 바탕으로 정리한 글입니다. :)

이번엔 JPA의 핵심 개념인 엔티티와 테이블 매핑에 대해 정리해보겠습니다~🤸

매핑 어노테이션

@Entity

JPA를 사용해서 테이블과 매핑할 클래스에 사용한다.

속성

  • name: JPA에서 사용할 엔티티 이름 지정한다. 설정하지 않으면 클래스 이름을 그대로 사용.

주의사항

  • 기본 생성자는 필수
  • final 클래스, inner 클래스, enum, interface에서 사용 불가
  • 저장 필드에 final 사용 불가

@Table

엔티티와 매핑할 테이블을 지정한다.

속성

  • name: 매핑할 테이블 이름(dafault: 엔티티 이름)
  • catalog: 해당 기능이 있는 데이터베이스에서 매핑
  • schema: 해당 기능이 있는 데이터베이스에서 매핑

스키마 자동 생성 설정

JPA는 클래스의 매핑 정보를 통해 각 데이터베이스에 맞게 스키마를 자동 생성하는 기능을 지원한다.

아래와 같은 설정을 통해 애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성할 수 있다.

spring:
  jpa:
    hibernate:
      ddl-auto: create

위와 같이 설정 후 실행 시 콘솔에 출력되는 스키마 생성 로그. 기존 테이블을 삭제하고 재생성한다.

drop table if exists member;

create table member (
    member_id bigint not null,
    city varchar(255),
    street varchar(255),
    zipcode varchar(255),
    username varchar(255),
    primary key (member_id)
)

hibernate.ddl-auto 속성

  • create : 기존 테이블을 삭제하고 새로 생성한다. DROP + CREATE
  • create-drop : create 속성에 추가로 애플리케이션 종료 시 생성한 DDL을 제거한다. DROP + CREATE + DROP
  • update : 테이블과 엔티티를 비교해 변경 사항을 수정한다.
  • validate : 테이블과 엔티티를 비교해 차이가 있으면 경고를 남기고 애플리케이션을 실행하지 않는다.
  • none : 자동 생성 기능 사용 X

    운영 환경에서 create, create-drop, update와 같이 DDL을 수정하는 옵션은 실사용되는 테이블이나 컬럼을 삭제할 수 있기 때문에 사용하면 안된다.

위 설정은 환경별로 알맞은 값을 사용해야 한다. 일반적으로 추천하는 상황별 전략은 아래와 같다.

  • 개발 초기: create, update
  • 초기화 상태로 테스트를 진행하는 개발자 환경과 CI 서버: create, create-drop
  • 테스트 서버: update, validate
  • 스테이징, 운영: validate, none

제약조건 추가

엔티티 관련 제약조건이 추가되는 경우 @Column의 속성을 이용해 이를 설정할 수 있다.

@Entity
public class Member {

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

	@Column(nullable = false, length = 20)
    private String username;
    ...
}

위와 같이 자동 생성되는 DDL의 username 컬럼에 not null 제약조건과 length를 지정할 수 있으며 생성된 DDL은 아래와 같다.

create table member (
       member_id bigint not null,
        city varchar(255),
        street varchar(255),
        zipcode varchar(255),
        username varchar(20) not null,
        primary key (member_id)
    )

@Table의 uniqueConstraints 속성을 통해 유니크 제약조건 부여도 가능한다.

@Entity
@Table(uniqueConstraints = {
        @UniqueConstraint(
                name = "ID_NAME_UNIQUE",
                columnNames = {"MEMBER_ID", "USERNAME"}
        )
})
public class Member {

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

	@Column(nullable = false, length = 20)
    private String username;
    ...
}

생성된 DDL은 아래와 같다.

alter table member 
       add constraint ID_NAME_UNIQUE unique (member_id, username)

위와 같은 기능들은 DDL을 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 끼치지 않는다. 그렇기에 스키마 자동 생성 기능을 사용하지 않는다면 위 설정을 할 필요는 없다.

기본키 매핑 전략

데이터베이스마다 기본 키를 생성하는 방식이 서로 달라 JPA는 이를 위해 기본 키 매핑 기능을 제공한다.

  • 직접 할당: 기본 키를 애플리케이션에서 직접 할당, 식별자 값 없을 경우 예외 발생.
      @Entity
      public class Member {
          @Id
          private Long id;
  • IDENTITY: 기본 키 생성을 데이터베이스에 위임. 엔티티를 저장한 직후 할당된 식별자 값을 엔티티에서 조회해 저장.
      @Entity
      public class Member {
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
  • SEQUENCE: 데이터베이스 시퀀스를 통해 기본 키 할당. 엔티티 저장 시 시퀀스를 사용해 식별자를 먼저 조회한 후 이를 데이터베이스에 저장한다.
      @Entity
      public class Member {
          @Id
          @GeneratedValue(strategy = GenerationType.SEQUENCE,
                                    generator = "MEMBER_SEQ_GENERATOR")
          private Long id;
  • TABLE: 키 생성용 테이블을 만들어 시퀀스처럼 사용. 내부 동작방식은 SEQUENCE 전략과 동일.
      @Entity
      public class Member {
          @Id
          @GeneratedValue(strategy = GenerationType.TABLE,
                                    generator = "MEMBER_SEQ_GENERATOR")
          private Long id;
  • AUTO: 데이터베이스에 따라 알맞은 전략 선택(Default / Oracle: SEQUENCE, MySQL: IDENTITY)

필드와 컬럼 매핑

@Enumerated

자바의 enum 타입을 매핑할 때 사용한다.

회원 별로 주소 정보를 가지고 있다고 가정할 때 주소 정보를 아래와 같이 JPA enum 어노테이션을 통해 표현할 수 있다.

@Entity
public class Member {
	@Id
    @GeneratedValue
    @Column(name="member_id")
    private Long id;

    private String username;
    
    @Embedded
    private Address address;
}

@Embeddable
public class Address {
    private String city;
    private String street;
    private String zipcode;
}

읽어주셔서 감사합니다~ :)

0개의 댓글