4장 엔티티 매핑

sua·2023년 5월 4일
0

JPA를 사용하는 데 가장 중요한 일은 엔티티와 테이블을 정확히 매핑하는 것 -> 매핑 어노테이션을 숙지하고 사용해야 한다.
[대표 어노테이션 4가지 분류]

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

-> 이번 장에서는 테이블 매핑, 기본 키 매핑, 필드와 컬럼 매핑에 대해 알아본다.

4.1 @Entity

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

@Entity 속성 정리

속성기능기본값
nameJPA에서 사용할 엔티티 이름을 지정한다. 보통 기본값인 클래스 이름을 사용한다. 만약 다른 패키지에 이름이 같은 엔티티 클래스가 있다면 이름을 지정해서 충돌하지 않도록 해야 한다.설정하지 않으면 클래스 이름을 그대로 사용한다. (예: Member)

@Entity 적용 시 주의 사항

  • 기본 생성자는 필수다(파라미터가 없는 public 또는 protected 생성자)
    JPA가 엔티티 객체를 생성할 때 기본 생성자를 사용 -> 생성자가 반드시 있어야 한다. 자바는 생성자가 하나도 없으면 기본 생성자를 자동으로 만든다.
    but, 임의의 생성자를 만들면 자바는 기본 생성자를 자동으로 만들지 않는다. -> 기본 생성자를 직접 만들어야 한다.
  • final 클래스, enum, interface, inner 클래스에는 사용할 수 없다.
  • 저장할 필드에 final을 사용하면 안 된다.

4.2 @Table

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

@Table 속성 정리

속성기능기본값
name매핑할 테이블 이름엔티티 이름을 사용한다.
catalogcatalog 기능이 있는 데이터베이스에서 catalog를 매핑한다.
schemaschema 기능이 있는 데이터베이스에서 schema를 매핑한다.
uniqueConstraints(DDL)DDL 생성 시에 유니크 제약조건을 만든다. 2개 이상의 복합 유니크 제약조건도 만들 수 있다. 참고로 이 기느은 스키마 자동 생성 기능을 사용해서 DDL을 만들 때만 사용된다.

4.3 다양한 매핑 사용

JPA 시작하기 장에서 개발하던 회원 관리 프로그램에 다음 요구사항이 추가되었다.

  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
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }

    public RoleType getRoleType() {
        return roleType;
    }

    public void setRoleType(RoleType roleType) {
        this.roleType = roleType;
    }

    public Date getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }

    public Date getLastModifiedDate() {
        return lastModifiedDate;
    }

    public void setLastModifiedDate(Date lastModifiedDate) {
        this.lastModifiedDate = lastModifiedDate;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

package jpabook.start;

public enum RoleType {
    ADMIN, USER
}

회원 코드를 분석해보자.

  1. roleType : 자바의 enum을 사용해서 회원의 타입을 구분했다. 일반 회원은 USER, 관리자는 ADMIN이다. 이렇게 자바의 enum을 사용하려면 @Enumerated 어노테이션으로 매핑해야 한다.
  2. createdDate, lastModifiedDate : 자바의 날짜 타입은 @Temporal을 사용해서 매핑한다.
  3. description : 회원을 설명하는 필드는 제한이 없다. 따라서 데이터베이스의 VARCHAR 타입 대신에 CLOB 타입으로 저장해야 한다. @Lob을 사용하면 CLOB, BLOB 타입을 매핑할 수 있다.

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

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

스키마 자동 생성 기능을 사용해보자.
먼저 persistence.xml에 다음 속성을 추가한다.

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

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

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

hibernate.show_sql속성을 true로 설정하면 콘솔에 실행되는 테이블 생성 DDL을 출력할 수 있다.

이제 애플리케이션을 실행해서 콘솔에 DDL이 출력되는지 보자.



실행된 결과를 보면 기존 테이블을 삭제하고 다시 생성한 것을 알 수 있다.
그리고 방금 추가한 roleType은 VARCHAR 타입으로, craetedDate, lastModifiedDate는 TIMESTAMP 타입으로, description은 CLOB 타입으로 생성되었다.
자동 생성되는 DDL은 지정한 데이터베이스 방언에 따라 달라진다. 만약 오라클 데이터베이스용 방언을 적용했다면 varchar 대신에 varchar2 타입이, integer 대신에 number 타입이 생성된다.

스키마 자동 생성 기능을 사용하면 개발자가 테이블을 직접 생성하는 수고를 덜 수 있다. 하지만 운영 환경에서 사용할 만큼 완벽하지는 않으므로 개발 환경에서 사용하거나 매핑을 어떻게 해야 하는지 참고하는 정도로만 사용하는 것이 좋다.
스키마 자동 생성하기는 엔티티와 테이블을 어떻게 매핑해야 하는지 알려주는 가장 훌륭한 하급 도구다.

hibernate.hbm2ddl.auto 속성

옵션설명
create기존 테이블을 삭제하고 새로 생성한다. DROP + CREATE
create-dropcreate 속성에 추가로 애플리케이션을 종료할 때 생성한 DDL을 제거한다. DROP + CREATE + DROP
update데이터베이스 테이블과 엔티티 매핑정보를 비교해서 변경 사항만 수정한다.
validate데이터베이스 테이블과 엔티티 매핑정보를 비교해서 차이가 있으면 경고를 남기고 애플리케이션을 실행하지 않는다. 이 설정은 DDL을 수정하지 않는다.
none자동 생성 기능을 사용하지 않으려면 hibernate.hbm2ddl.auto 속성 자체를 삭제하거나 유효하지 않은 옵션 값을 주면 된다(참고로 none은 유효하지 않은 옵션 값이다.

개발 환경에 따른 속성 추천 전략

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

이름 매핑 전략 변경하기

hibernate.ejb.naming_strategy 속성을 사용하면 이름 매핑 전략을 변경할 수 있다.
org.hibernate.cfg.ImprovedNamingStrategy 클래스를 사용하면 테이블 명이나 컬럼 명이 생략 되면 자바의 카멜 표기법을 테이블의 언더스코어 표기법으로 매핑한다.

<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy"/>

이 속성을 사용해서 테이블을 생성하면 언더스코어 표기법으로 매핑된다.


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에 문자의 크기를 지정할 수 있다.

생성된 DDL의 NAME 컬럼을 보면 not null 제약 조건이 추가되었고, varchar(10)으로 문자의 크기가 10자리로 제한된 것을 확인할 수 있다.

이번에는 유니크 제약조건을 만들어주는 @TableuniqueConstraints 속성을 알아보자.

@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; // 이름

    // 매핑 정보가 없는 필드
    private Integer age; // 나이
    ...
}


생성된 DDL을 보면 유니크 제약조건이 추가되었다. 앞서 본 @Columnlengthnullable 속성을 포함해서 이런 기능들은 단지 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다.
따라서 스키마 자동 생성 기능을 사용하지 않고 직접 DDL을 만든다면 사용할 이유가 없다. 그래도 이 기능을 사용하면 개발자가 엔티티만 보고도 손쉽게 다양한 제약 조건을 파악할 수 있는 장점이 있다.
JPA에는 이처럼 애플리케이션의 실행 동작에는 영향을 주지 않지만, 자동 생성되는 DDL을 위한 기능들도 있다.


4.6 기본 키 매핑

@Entity
public class Member {
    @Id
    @Column(name = "ID")
    private String id; 
    ...
}

지금까지 위 코드 처럼 @Id 어노테이션만 사용해서 회원의 기본키를 애플리케이션에서 직접 할당했다. 데이터베이스가 생성해주는 값을 기본키로 사용하려면 예로 들어 MySQL의 AUTO_INCREMENT 같은 기능을 사용해서 생성된 값을 기본 키로 사용하려면 JPA가 제공하는 데이터베이스 기본키 생성 전략을 이용해야 한다.

  • 직접 할당 : 기본키를 애플리케이션에서 직접 할당
  • 자동 생성 : 대리키 사용 방식
    • IDENTITY : 기본 키 생성을 데이터베이스에 위임
    • SEQUENCE : 데이터베이스 시퀀스를 사용해서 기본키를 할당
    • TABLE : 키 생성 테이블을 사용. 키 생성용 테이블을 하나 만들어두고 마치 시퀀스처럼 사용하는 방법 -> 모든 데이터베이스에서 사용할 수 있음

-> 자동 생성 전략이 다양한 이유는 데이터베이스 벤더마다 지원하는 방식이 다르기 때문이다.
기본 키를 직접 할당하려면 @Id만 사용, 자동 생성 전략을 사용하려면 @Id + @GeneratedValue를 추가하고 원하는 키 생성 전략 선택

4.6.1 기본 키 직접 할당 전략

기본 키를 직접 할당하려면 아래 코드와 같이 @Id로 매핑하면 된다.

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

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

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

@Id 적용 가능 자바 타입

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

4.6.2 IDENTITY 전략

IDENTITY는 기본키 생성을 데이터베이스에 위임하는 전략이다. 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용한다.
예를 들어 MySQL의 AUTO_INCREMENT 기능은 데이터베이스가 기본키를 자동으로 생성해준다. 이를 수행하는 다음 예제를 보자.

CREATE TABLE BOARD (
	ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    DATA VARCHAR(255)
);

INSERT INTO BOARD(DATA) VALUES('A');
INSERT INTO BOARD(DATA) VALUES('B');

테이블을 생성할 때 기본키 컬럼인 ID에 AUTO_INCREMENT를 추가했다. 이제 데이터베이스에 값을 저장할 때 ID 컬럼을 비워두면 데이터베이스가 순서대로 값을 채워준다.

BOARD 테이블 결과

IDDATA
1A
2B

결과를 보면 ID 컬럼에 자동으로 값이 입력된 것을 확인할 수 있다.

사용 방법

IDENTITY 전략은 지금 설명한 AUTO_INCREMENT를 사용한 예제처럼 데이터베이스에 값을 저장하고 나서야 기본키 값을 구할 수 있을 때 사용한다.
IDENTITY 전략을 사용하려면 @GeneratedValuestrategy 속성 값을 GenerationType.IDENTITY로 지정하면 된다.
이 전략을 사용하면 jPA는 기본키 값을 얻어오기 위해 데이터베이스를 추가로 조회한다.

아래 코드를 살펴보자.
IDENTITY 매핑 코드

@Entity
public class Board {
	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    ...
}

IDENTITY 사용 코드

public static void logic(EntityManager em) {
	Board board = new Board();
    em.persist(board);
    System.out.println("board.id = " + board.getId());
}
// 출력: baord.id = 1

위 코드를 보면 em.persist()를 호출해서 엔티티를 저장한 직후에 할당된 식별자 값을 출력했다. 출력된 값 1은 저장 시점에 데이터베이스가 생성한 값을 JPA가 조회한 것이다.


4.6.3 SEQUENCE 전략

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

CREATE TABLE BOARD (
	ID BIGINT NOT NULL PRIMARY KEY,
    DATA VARCHAR(255)
)

// 시퀀스 생성
CREATE SEQUENCE BOARD_SEQ START WITH 1 INCREMENT BY 1;

[시퀀스 매핑 코드]

@Entity
@SequenceGenerator(
	name = "BOARD_SEQ_GENERATOR",
    sequenceName = "BOARD_SEQ", // 매핑할 데이터베이스 시퀀스 이름
    initialValue = 1, allocationSize = 1)
public class Board {
	@Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "BOARD_SEQ_GENERATOR")
    private Long id;
    ...
}

먼저 사용할 데이터베이스 시퀀스를 매핑해야 한다. 위 코드에서는 @SequenceGenerator를 사용해서 BOARD_SEQ_GENERATOR라는 시퀀스 생성기를 등록했다. 그리고 sequenceName 속성의 이름으로 BOARD_SEQ를 지정했는데 JPA는 이 시퀀스 생성기를 실제 데이터베이스의 BOARD_SEQ 시퀀스와 매핑한다.
다음으로 키 생성 전략을 GenerationType.SEQUENCE로 설정하고 generator = "BOARD_SEQ_GENERATOR" 로 방금 등록한 시퀀스 생성기를 선택했다. 이제부터 id 식별자 값을 BOARD_SEQ_GENERATOR 시퀀스 생성기가 할당한다.

[시퀀스 사용 코드]

public static void logic(EntityManager em) {
	Board board = new Board();
    em.persist(board);
    System.out.println("board.id = " + board.getId());
}
// 출력: baord.id = 1

시퀀스 사용 코드는 IDENTITY 전략과 같지만 내부 동작 방식이 다르다. SEQUENCE 전략은 em.persis()를 호출할 때 먼저 데이터베이스 시퀀스를 사용해서 식별자를 조회한다. 그리고 조회한 식별자를 엔티티에 할당한 후에 엔티티를 영속성 컨텍스트에 저장한다. 이후 트랜잭션을 커밋해서 플러시가 일어나면 엔티티를 데이터베이스에 저장한다.
반대로 IDENTITY 전략은 먼저 엔티티를 데이터베이스에 저장한 후에 식별자를 조회해서 엔티티의 식별자에 할당한다.


@SequenceGenerator 속성 정리

속성기능기본값
name식별자 생성기 이름필수
sequenceName데이터베이스에 등록되어 있는 시퀀스 이름hibernate_sequence
initialValueDDL 생성 시에만 사용됨. 시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정한다.1
allocationSize시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨)50
catalog, schema데이터베이스 catalog, schema 이름

매핑할 DDL은 다음과 같다

create sequence [sequenceName]
start with [initialValue] increment by [allocationSize]

4.6.4 TABLE 전략

TABLE 전략은 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략이다. 이 전략은 모든 데이터베이스에 적용할 수 있다.
TABLE 전략을 사용하려면 먼저 아래처럼 키 생성 용도로 사용할 테이블을 만들어야 한다.

create table MY_SEQUENCES (
	sequence_name varchar(255) not null,
    next_val bigint,
    primary key ( sequence_name )
)

sequence_name 컬럼을 시퀀스 이름으로 사용하고 next_val 컬럼을 시퀀스 값으로 사용한다.

TABLE 전략 매핑 코드

@Entity
@TableGenerator(
	name = "BOARD_SEQ_GENERATOR",
    table = "MY_SEQUENCES",
    pkColumnValue = "BOARD_SEQ", allocationSize = 1) 
public class Board {
	@Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "BOARD_SEQ_GENERATOR")
    private Long id;
    ...
}

코드를 보면 먼저 @TableGenerator를 사용해서 테이블 키 생성기를 등록한다. 여기서는 BOARD_SEQ_GENERATOR라는 이름의 테이블 키 생성기를 등록하고 MY_SEQUENCES 테이블을 키 생성용 테이블로 매핑했다.
그런 다음 TABLE 전략을 사용하기 위해 GenerationType.TABLE을 선택했다. 그리고 @GeneratedValue.generator에 방금 만든 테이블 키 생성기를 지정했다. 이제부터 id 식별자 값은 BOARD_SEQ_GENERATOR 테이블 키 생성기가 할당한다.

TABLE 전략 매핑 사용 코드

public static void logic(EntityManager em) {
	Board board = new Board();
    em.persist(board);
    System.out.println("board.id = " + board.getId());
}
// 출력: baord.id = 1

TABLE 전략을 매핑한 사용 코드를 보면 TABLE 전략은 시퀀스 대신에 테이블을 사용한다는 것만 제외하면 SEQUENCE 전략과 내부 동작방식이 같다.

MY_SEQUENCES 결과 테이블

sequence_namenext_val
BOARD_SEQ2
MEMBER_SEQ10
PRODOCT_SEQ50

MY_SEQUENCES 테이블을 보면 @TableGenerator.pkColumnValue에서 지정한 "BOARD_SEQ"가 컬럼명으로 추가된 것을 확인할 수 있다. 이제 키 생성기를 사용할 때마다 next_val 컬럼 값이 증가한다. 참고로 MY_SEQUENCES 테이블에 값이 없으면 JPA가 값을 INSERT하면서 초기화하므로 값을 미리 넣어둘 필요는 없다.


@TableGenerator 속성 정리

속성기능기본값
name식별자 생성기 이름필수
table키생성 테이블명hibernate_sequences
pkColumnName시퀀스 컬럼명sequence_name
valueColumnName시퀀스 값 컬럼명next_val
pkColumnValue키로 사용할 값 이름엔티티 이름
initialValue초기 값, 마지막으로 생성된 값이 기준이다.0
allocationSize시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨)50
catalog, schema데이터베이스 catalog,schema 이름
uniqueConstraints(DDL)유니크 제약 조건을 지정할 수 있다.

4.6.5 AUTO 전략

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

AUTO 전략 매핑 코드

@Entity
public class Board {
	@Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
}

AUTO 전략의 장점은 데이터베이스를 변경해도 코드를 수정할 필요가 없다는 것이다. 특히 키 생성 전략이 아직 확정되지 않은 개발 초기 단계나 프로토타입 개발 시 편리하게 사용할 수 있다.
AUTO를 사용할 때 SEQUENCE나 TABLE 전략이 선택되면 시퀀스나 키 생성용 테이블을 미리 만들어 두어야 한다. 만약 스키마 자동 생성 기능을 사용한다면 하이버네이트가 기본값을 사용해서 적절한 시퀀스나 키 생성용 테이블을 만들어 줄 것이다.


4.6.6 기본 키 매핑 정리

영속성 컨텍스트는 엔티티를 식별자 값으로 구분하므로 엔티티를 영속 상태로 만들려면 식별자 값이 반드시 있어야 한다. em.persist()를 호출한 직후에 발생하는 일을 식별자 할당 전략별로 정리하면 다음과 같다.

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

4.7 필드와 컬럼 매핑: 레퍼런스

JPA가 제공하는 필드와 컬럼 매핑용 어노테이션들을 레퍼런스 형식으로 정리해보자.

필드와 컬럼 매핑 분류

매핑 어노테이션설명
@Column컬럼을 매핑한다.
@Enumerated자바의 enum 타입을 매핑한다.
@Temporal날짜 타입을 매핑한다.
@LobBLOB, CLOB 타입을 매핑한다.
@Transient특정 필드를 데이터베이스에 매핑하지 않는다.
@AccessJPA가 엔티티에 접근하는 방식을 지정한다.

4.7.1 @Column

@Column은 객체 필드를 테이블 컬럼에 매핑한다.
속성 중에 name, nullable이 주로 사용되고 나머지는 잘 사용되지 않는 편이다.
insertable, updatable 속성은 데이터베이스에 저장되어 있는 정보를 읽기만 하고 실수로 변경하는 것을 방지하고 싶을 때 사용한다.

@Column 속성 정리

속성기능기본값
name필드와 매핑할 테이블의 컬럼 이름객체의 필드 이름
insertable(거의 사용x)엔티티 저장 시 이 필드도 같이 저장한다. false로 설정하면 이 필드는 데이터베이스에 저장하지 않는다. false 옵션은 읽기 전용일 때 사용한다.true
updatable(거의 사용x)엔티티 수정 시 이 필드도 같이 수정한다. false로 설정하면 데이터베이스에 수정하지 않는다. false 옵션은 읽기 전용일 때 사용한다.true
table(거의 사용x)하나의 엔티티를 두 개 이상의 테이블에 매핑할 때 사용한다. 지정한 필드를 다른 테이블에 매핑할 수 있다.현재 클래스가 매핑된 테이블
nullable(DDL)null 값의 허용 여부를 설정한다. false로 설정하면 DDL 생성 시에 not null 제약 조건이 붙는다.true
unique(DDL)@Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용한다. 만약 두 컬럼 이상을 사용해서 유니크 제약조건을 사용하려면 클래스 레벨에서 사용해야 한다.
columnDefinition(DDL)데이터베이스 컬럼 정보를 직접 줄 수 있다.필드의 자바 타입과 방언 정보를 사용해서 적절한 컬럼 타입을 생성한다.
length(DDL)문자 길이 제약조건, String 타입에만 사용한다.255
precision, scale(DDL)BigDecimal 타입에서 사용한다(BigInteger도 사용할 수 있다). precision은 소수점을 포함한 전체 자릿수를, scale은 소수의 자릿수다. 참고로 double, float타입에는 적용되지 않는다. 아주 큰 숫자나 정밀한 소수를 다루어야 할 때만 사용한다.precision=19,scale=2

-> 자바 기본 타입에 @Column을 사용하면 nullable = false로 지정하는 것이 안전하다.


4.7.2 @Enumerated

@Enumerated 속성 정리

속성기능기본값
value- EnumType.ORIDINAL : enum 순서를 데이터베이스에 저장
- EnumType.STRING: enum 이름을 데이터베이스에 저장
EnumType.ORDINAL

@Enumerated 사용 예

  1. enum 클래스는 다음과 같다.
enum RoleType {
	ADMIN, USER
}
  1. 다음은 enum 이름으로 매핑한다.
@Enumerated(EnumType.STRING)
private RoleType roleType;
  1. 다음 처럼 사용한다.
member.setRoleType(RoleType.ADMIN); // -> DB에 문자 ADMIN으로 저장됨
  • EnumType.ORDINAL은 enum에 정의된 순서대로 ADMIN은 0, USER는 1 값이 데이터 베이스에 저장된다.
    • 장점 : 데이터베이스에 저장되는 데이터 크기가 작다.
    • 단점 : 이미 저장된 enum의 순서를 변경할 수 없다.
  • EnumType.STRING은 enum 이름 그대로 ADMIN은 'ADMIN', USER는 'USER'라는 문자로 데이터베이스 저장된다.
    • 장점 : 저장된 enum의 순서가 바뀌거나 enum이 추가되어도 안전한다.
    • 단점 : 데이터베이스에 저장되는 데이터 크기가 ORDINAL에 비해서 크다.
      -> EnumType.STRING을 권장한다.

4.7.3 @Temporal

날짜 타입을 매핑할 때 사용한다.

@Temporal 속성 정리

속성기능기본값
value- TemporalType.DATE : 날짜, 데이터베이스 date 타입과 매핑(예: 2013-10-11)
- TemporalType.TIME: 시간, 데이터베이스 time 타입과 매핑(예: 11:11:11)
- TemporalType.TIMESTAMP : 날짜와 시간, 데이터베이스 timestamp 타입과 매핑(예: 2013-10-11 11:11:11)
TemporalType은 필수로 지정해야 한다.

@Temporal 사용 예

@Temporal(TemporalType.DATE)
private Date date; // 날짜

@Temporal(TemporalType.TIME)
private Date time; // 시간

@Temporal(TemporalType.TIMESTAMP)
private Date timestamp; // 날짜와 시간

//==생성된DDL==//
date date,
time time,
timestamp timestamp,

자바의 Date 타입에는 년월일 시분초가 있지만 데이터베이스에서는 date(날자), time(시간), timestamp(날짜와 시간)라는 세가지 타입이 별도로 존재한다.

데이터베이스 방언에 따라 생성되는 DDL은 다음과 같다.

  • datetime : MySQL
  • timestamp : H2, 오라클, PostgreSQL

4.7.4 @Lob

데이터베이스 BLOB, CLOB 타입과 매핑한다

속성 정리

@Lob에는 지정할 수 있는 속성이 없다. 대신에 매핑하는 필드 타입이 문자면 CLOB으로 매핑하고, 나머지는 BLOB으로 매핑한다.

  • CLOB : String, char[], java.sql.CLOB
  • BLOB : byte[], java.sql.BLOB

@Lob 사용 예

@Lob
private String lobString;

@Lob
private byte[] lobByte;

생성된 DDL은 다음과 같다.

// 오라클
lobString clob,
lobByte blob,

// MySQL
lobString longtext,
lobByte longblob,

// PostgreSQL
lobString text,
lobByte oid,

4.7.5 @Transient

이 필드는 매핑하지 않는다. 따라서 데이터베이스에 저장하지 않고 조회하지도 않는다. 객체에 임시로 어떤 값을 보관하고 싶을 때 사용한다.

@Transient
private Integer temp;

4.7.6 @Access

JPA가 엔티티 데이터에 접근하는 방식을 지정한다.
@Access를 설정하지 않으면 @Id의 위치를 기준으로 접근 방식이 설정된다.

  • 필드 접근 : AccessType.FIELD로 지정한다. 필드에 직접 접근한다. 필드 접근 권한이 private이어도 접근할 수 있다.
@Entity
@Acess(AcessType.FIELD)
public class Member {
	@Id
    private String id;
    
    private String data1;
    private String data2;
    ...
}

@Id가 필드에 있으므로 @Access(ACcessType.FIELD)로 설정한 것과 같다. 따라서 @Access는 생략해도 된다.

  • 프로퍼티 접근 : AccessType.PROPERTY로 지정한다. 접근자를 사용한다.
@Entity
@Acess(AcessType.PROPERTY)
public class Member {

    private String id;
    
    private String data1;
    private String data2;
    
    @Id
    public String getId() {
    	return id;
    }
    
    
    @Column
    public String getData1() {
    	return data1;
    }    
    
    public String getData2() {
    	return data2;
    }
}

@Id가 프로퍼티에 있으므로 @Access(AccessType.PROPERTY)로 설정한 것과 같다. 따라서 @Access는 생략해도 된다.

필드 접근 방식과 프로퍼티 접근 방식을 함께 사용할 수도 있다.

@Entity
public class Member {
	@Id
    private String id;
    
    @Transient
    private String firstName;
    
    @Transient
    private String lastName;
    
    @Access(AccessType.PROPERTY)
    public String getFullName() {
    	return firstName + lastName;
    }
}

@Id가 필드에 있으므로 기본은 필드 접근 방식을 사용하고 getFullName()만 프로퍼티 접근 방식을 사용한다. 따라서 회원 엔티티를 저장하면 회원 테이블의 FULLNAME 컬럼에 firstName + lastName 결과가 저장된다.

profile
가보자고

0개의 댓글