[JPA] 자바 ORM 표준 JPA 프로그래밍 4장

xyzw·2023년 6월 5일
0

Spring

목록 보기
12/22

4.1 @Entity

JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 어노테이션을 필수로 붙여야 한다.

속성

  • name
    JPA에서 사용할 엔티티 이름을 지정한다.
    따로 설정하지 않으면 클래스 이름을 그대로 사용한다. 만약 다른 패키지에 이름이 같은 엔티티 클래스가 있다면 이름을 지정해서 충돌하지 않도록 해야 한다.

주의사항

  • 기본 생성자는 필수다. (파라미터가 없는 public 또는 protected 생성자)
    JPA가 엔티티 객체를 생성할 때 기본 생성자를 사용하므로 이 생성자는 반드시 있어야 한다.

  • final, enum, interface, inner 클래스에는 사용할 수 없다.

  • 저장할 필드에 final을 사용하면 안 된다.

4.2 @Table

엔티티와 매핑할 테이블을 지정한다. @Table을 생략하면 매핑한 엔티티 이름을 테이블 이름으로 사용한다.

속성

  • name
    매핑할 테이블 이름을 지정한다. 기본값은 엔티티 이름이다.

  • catalog
    catalog 기능이 있는 데이터베이스에서 catalog를 매핑한다.

  • schema
    schema 기능이 있는 데이터베이스에서 schema를 매핑한다.

4.3 다양한 매핑 사용

회원 관리 프로그램에 다음 요구사항이 추가되었다.
1. 회원은 일반 회원과 관리자로 구분해야 한다.
2. 회원 가입일과 수정일이 있어야 한다.
3. 회원을 설명할 수 있는 필드가 있어야 한다. 이 필드는 길이 제한이 없다.

package jpabook.start;

import javax.persistence.*;
import java.util.Date;

@Entity
@Table(name="MEMBER")
public class Member {

    @Id
    @Column(name = "ID")
    private String id;

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

    private Integer age;

    //=== 추가
    @Enumerated(EnumType.STRING)
    private RoleType roleType;

    @Temporal(TemporalType.TIMESTAMP)
    private Date createdDate;

    @Temporal(TemporalType.TIMESTAMP)
    private Date lastModifiedDate;

    @Lob
    private String description;

    //Getter, Setter
	...
}

package jpabook.start;

public enum RoleType {
    ADMIN, USER
}

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

JPA는 데이터베이스 스키마를 자동으로 생성하는 기능을 지원한다. 클래스의 매핑 정보를 보면 어떤 테이블에 어떤 컬럼을 사용하는지 알 수 있다. JPA는 이 매핑 정보와 데이터베이스 방언을 사용해서 데이터베이스 스키마를 생성한다.

스키마 자동 생성 기능

persistence.xml에 다음 속성을 추가한다.

<property name="hibernate.hbm2ddl.auto" value="create" />

이 속성을 추가하면 애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성한다.

참고로 다음 속성을 추가하면 콘솔에 실행되는 테이블 생성 DDL을 출력할 수 있다.

<property name="hibernate.show_sql" value="true" />

애플리케이션을 실행하면 콘솔에 다음 DDL이 출력되면서 실제 테이블이 생성된다.

Hibernate:
	drop table MEMBER if exists
Hibernate:
	create table MEMBER (
    	ID varchar(255) not null,
        NAME varchar(255),
        age integer,
        roleType varchar(255),
        createdDate timestamp,
        lastModifiedDate timestamp,
        description clob,
        primary key (ID)
    )

실행 결과를 보면 기존 테이블을 삭제하고 다시 생성한 것을 알 수 있다.
그리고 방금 추가한 roleType은 VARCHAR 타입으로, createDate, lastModifiedDate는 TIMESTAMP 타입으로, description은 CLOB 타입으로 생성되었다.

스키마 자동 생성 기능을 사용하면 애플리케이션 실행 시점에 데이터베이스 테이블이 자동으로 생성되므로 개발자가 테이블을 직접 생성하는 수고를 덜 수 있다.
하지만 스키마 자동 생성 기능이 만든 DDL은 운영 환경에서 사용할 만큼 완벽하지는 않으므로 개발 환경에서 사용하거나 매핑을 어떻게 해야 하는지 참고하는 정도로만 사용하는 것이 좋다.

hibernate.hbm2ddl.auto 속성

  • create
    기존 테이블을 삭제하고 새로 생성한다. DROP + CREATE

  • create-drop
    create 속성에 추가로 애플리케이션을 종료할 때 생성한 DDL을 제거한다. DROP + CREATE + DROP

  • update
    데이터베이스 테이블과 엔티티 매핑정보를 비교해서 변경 사항만 수정한다.

  • validate
    데이터베이스 테이블과 엔티티 매핑정보를 비교해서 차이가 있으면 경고를 남기고 애플리케이션을 실행하지 않는다. 이 설정은 DDL을 수정하지 않는다.

  • none
    자동 생성 기능을 사용하지 않으려면 hibernate.hbm2ddl.auto 속성 자체를 삭제하거나 유효하지 않은 옵션 값을 주면 된다.

HBM2DDL 주의사항

운영 서버에서 create, create-drop, update처럼 DDL을 수정하는 옵션은 절대 사용하면 안 되고, 오직 개발 서버나 개발 단계에서만 사용해야 한다. 이 옵션들은 운영 중인 데이터베이스의 테이블이나 컬럼을 삭제할 수 있다.

개발 환경에 따른 추천 전략

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

4.5 DDL 생성 기능

회원 이름은 필수로 입력되어야 하고, 10자를 초과하면 안 된다는 제약 조건이 추가되었다. 스키마 자동 생성하기를 통해 만들어지는 DDL에 이 제약조건을 추가해보자.

@Entity
@Table(name="MEMBER")
public class Member {

    @Id
    @Column(name = "ID")
    private String id;

    @Column(name = "NAME", nullable = false, length = 10)
    private String username;
	...
}

@Column 속성

  • nullable
    false로 지정하면 자동 생성되는 DDL에 not null 제약 조건을 추가할 수 있다.

  • length
    자동 생성되는 DDL에 문자의 크기를 지정할 수 있다.

create table MEMBER (
	ID varchar(255) not null,
    NAME varchar(10) not null,
    ...
    primary key (ID)
)

생성된 DDL의 NAME 컬럼에 not null 제약 조건이 추가되었고, 문자의 크기가 10자리로 제한되었다.

@Table 속성

  • uniqueConstraints
    유니크 제약 조건을 만들어준다.
@Entity
@Table(name="MEMBER", uniqueConstraints = {@UniqueConstraint( 
        name = "NAME_AGE_UNIQUE",
        columnNames = {"NAME", "AGE"} )})
public class Member {

    @Id
    @Column(name = "ID")
    private String id;

    @Column(name = "NAME", nullable = false, length = 10)
    private String username;
	...
}

생성된 DDL은 다음과 같다.

ALTER TABLE MEMBER
	ADD CONTRAINT NAME_AGE_UNIQUE UNIQUE (NAME, AGE)

4.6 기본 키 매핑

@Entity
public class Member {

    @Id
    @Column(name = "ID")
    private String id;
    ...
}

기본 키를 애플리케이션에서 직접 할당하는 대신에 데이터베이스가 생성해주는 값(ex. MySQL의 AUTO_INCREMENT 기능)을 사용하는 방법을 알아보자.

JPA가 제공하는 데이터베이스 기본 키 생성 전략

  • 직접 할당: 기본 키를 애플리케이션에서 직접 할당한다.
  • 자동 생성: 대리 키 사용 방식
    • IDENTITY: 기본 키 생성을 데이터베이스에 위임한다.
    • SEQUENCE: 데이터베이스 시퀀스를 사용해서 기본 키를 할당한다.
    • TABLE: 키 생성 테이블을 사용한다.

SEQUENCE나 IDENTITY 전략은 사용하는 데이터베이스에 의존한다.
TABLE 전략은 키 생성용 테이블을 하나 만들어두고 마치 시퀀스처럼 사용하는 방법이다. 이 전략은 테이블을 활용하므로 모든 데이터베이스에서 사용할 수 있다.

기본 키를 직접 할당하려면 @Id만 사용하면 되고, 자동 생성 전략을 사용하려면 @Id에 @GeneratedValue를 추가하고 원하는 키 생성 전략을 선택하면 된다.

기본 키 직접 할당 전략

기본 키를 직접 할당하려면 @Id로 매핑하면 된다.

@Id
@Column(name = "ID")
private String id;

@Id 적용 가능 자바 타입

  • 자바 기본형
  • 자바 래퍼(Wrapper)형
  • String
  • java.util.Date
  • java.sql.Date
  • java.math.BigDecimal
  • java.math.BigInteger

기본 키 직접 할당 전략은 em.persist()로 엔티티를 저장하기 전에 애플리케이션에서 기본 키를 직접 할당하는 방법이다.

Board board = new Board();
board.setId("id1");  //기본 키 직접 할당
em.persist(board);

IDENTITY 전략

기본 키 생성을 데이터베이스에 위임하는 전략으로, 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용한다.

IDENTITY 전략을 사용하려면 @GeneratedValue의 strategy 속성 값을 GenerationType.IDENTITY로 지정하면 된다.

SEQUENCE 전략

데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트다. SEQUENCE 전략은 이 시퀀스를 사용해서 기본 키를 생성한다. 이 전략은 시퀀스를 지원하는 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용할 수 있다.

TABLE 전략

키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략이다. 이 전략은 테이블을 사용하므로 모든 데이터베이스에 적용할 수 있다.

AUTO 전략

선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다. 예를 들어 오라클을 선택하면 SEQUENCE를, MySQL을 선택하면 IDENTITY를 사용한다.

@GeneratedValue.strategy의 기본값을 AUTO다.

AUTO 전략의 장점은 데이터베이스를 변경해도 코드를 수정할 필요가 없다는 것이다. 특히 키 생성 전략이 아직 확정되지 않은 개발 초기 단계나 프로토타입 개발 시 편리하게 사용할 수 있다.

기본 키 매핑 정리

영속성 컨텍스트는 엔티티를 식별자 값으로 구분하므로 엔티티를 영속 상태로 만들려면 식별자 값이 반드시 있어야 한다.

em.persist()를 호출한 직후에 발생하는 일

  • 직접 할당: em.persist()를 호출하기 전에 애플리케이션에서 직접 식별자 값을 할당해야 한다. 만약 식별자 값이 없으면 예외가 발생한다.
  • SEQUENCE: 데이터베이스 시퀀스에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장한다.
  • TABLE: 데이터베이스 시퀀스 생성용 테이블에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장한다.
  • IDENTITY: 데이터베이스에 엔티티를 저장해서 식별자 값을 획득한 후 영속성 컨텍스트에 저장한다.

0개의 댓글