JPA를 사용하는 데 가장 중요한 일은 엔티티와 테이블을 정확히 매핑하는 것이다.

다음은 대표 어노테이션들이다.

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

@Entity

@Entity가 붙은 클래스는 JPA가 관리하는 것으로, 엔티티라 부른다.

  • 기본 생성자는 필수다. (파라미터가 없는 public 또는 protected 생성자)
  • final 클래스, enum, interface, inner 클래스에는 사용할 수 없다.
  • 저장할 필드에 final을 사용하면 안 된다.
속성기능기본값
nameJPA에서 사용할 엔티티 이름을 지정한다. 보통 기본값을 사용한다. 다른 패키지에 이름이 같은 엔티티 클래스가 있으면 이름을 지정해서 충돌하지 않도록 해야 한다.클래스 이름을 그대로 사용한다.

@Table

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

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

다양한 매핑 사용

  • @Enumerated: 자바의 enum을 사용할 떄 매핑
  • @Temporal: 자바의 날짜 타입

DDL 생성 기능

@Entity(name="Member")
@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;
}
  • NAME 컬럼에 not null 제약조건을 추가하고 varchar(10)으로 문자의 크기가 10자리로 제한되었다.
  • 유니크 제약조건이 추가된다.
    • ALTER TABLE MEMBER ADD CONSTRAINT NAME_AGE_UNIQUE UNIQUE (NAME, AGE)

이런 기능들은 단지 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다. 직접 DDL을 만든다면 사용할 이유가 없다. 그래도 이 기능을 사용하면 엔티티만 보고도 손쉽게 다양한 제약 조건을 파악할 수 있는 장점이 있다.

기본 키 매핑

  • 직접 할당: 기본 키를 애플리케이션에서 직접 할당한다.
  • 자동 생성: 대리 키 사용 방식
    • IDENTITY: 기본 키 생성을 데이터베이스에 위임한다.
    • SEQUENCE: 데이터베이스 시퀀스를 사용해서 기본 키를 할당한다.
    • TABLE: 키 생성 테이블을 사용한다.
  • 자동 생성 전략이 다양한 이유는 데이터베이스 벤더마다 지원하는 방식이 다르기 때문이다.
    • 오라클은 시퀀스를 제공하지만 MySQL은 시퀀스를 제공하지 않는다.

기본 키 직접 할당 전략

  • @Id 적용 가능 자바 타입
    • 자바 기본형, 자바 래퍼형, String
    • java.util.Date, java.sql.Date, java.math.BigDecimal, java.math.BigInteger

IDENTITY 전략

  • 데이터베이스에 엔티티를 저장해서 식별자 값을 획득한 후 영속성 컨텍스트에 저장한다.
  • 데이터베이스에 값을 저장하고 나서야 기본 키 값을 구할 수 있을 때 사용한다.
  • 예) MySQL의 AUTO_INCREMENT 기능은 데이터베이스가 기본 키를 자동으로 생성해준다.
  • MySQL, PostgreSQL, SQL Server, DB2 에서 사용한다.
@Entity
public class Board {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    ...
}
  • JDBC3에 추가된 Statement.getGeneratedKeys()를 사용하면 데이터를 저장하면서 동시에 생성된 기본 키 값도 얻어 올 수 있다.
  • IDENTITY 식별자 생성 전략은 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있으므로 em.persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달된다. 따라서 이 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다.

SEQUENCE 전략

  • 데이터베이스 시퀀스에서 식별자 값을 획득한 후 영솟ㄱ성 컨텍스트에 저장한다.
  • 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트다.
  • 이 전략은 시퀀스를 지원하는 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용할 수 있다.
  • @SequenceGenerator는 @GeneratedValue 옆에 사용해도 된다.
@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;
}
속성기능기본값
name식별자 생성기 이름필수
sequenceName데이터베이스에 등록되어 있는 시퀀스 이름hibernate_sequence
initialValueDDL 생성 시에만 사용됨. 시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정한다.1
allocationSize시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨)50
catalog, schema데이터베이스 catalog, schema 이름 

TABLE 전략

  • 데이터베이스 시퀀스 생성용 테이블에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장한다.
  • TABLE 전략은 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 말ㄴ들어 데이터베이스 시퀀스를 흉내내는 전략이다. 이 전략은 테이블을 사용하므로 모든 데이터베이스에 적용할 수 있다.
  • TABLE 전략을 사요앟려면 키 생성 용도로 사용할 테이블을 만들어야 한다.

AUTO 전략

  • 선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다.
    • 오라클: SEQUENCE, MySQL: IDENTITY
  • 데이터베이스를 변경해도 코드를 수정하 필요가 없다

권장하는 식별자 선택 전략

  • 데이터베이스 기본 키는 다음 3가지 조건을 모두 만족해야 한다.
    1. null 값은 허용하지 않는다.
    2. 유일해야 한다.
    3. 변해선 안된다.
  • 전략 1. 자연 키
    • 비즈니스에 의미가 있는 키 (주민번호, 이메일, 전화번호)
  • 전략 2. 대리 키
    • 비즈니스와 관련 없는 임의로 만들어진 키, 대체 키로도 불린다. (오라클 시퀀스, auto_increment, 키생성 테이블 사용)
  • JPA는 모든 엔티티에 일관된 방식으로 대리 키 사용을 권장한다.

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

@Column

  • 객체 필드를 테이블 컬럼에 매핑한다. name, nullable이 주로 사용된다.
  • @Column 생략:
    • int 같은 자바 기본 타입에는 null 값을 입력할 수 없고 Integer 같은 객체 타입일 때만 null 값이 허용된다.
    • 따라서 기본 타입을 DDL로 생성할 때는 not null 제약 조건을 추가하는 것이 안전하다.
속성기능기본값
name필드와 매핑할 테이블의 컬럼 이름객체의 필드 이름
nullable(DDL)null 값의 허용 여부를 설정한다. false -> not nulltrue
unique(DDL)@Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용한다. 두 컬럼 이상 시 -> @Table.uniqueConstraints 사용 
columnDefinition(DDL)데이터베이스 컬럼 정보를 직접 줄 수 있다.필드의 자바 타입과 방언 정보를 사용해서 적절한 컬럼 타입을 생성한다.
lenegth(DDL)문자 길이 제약조건, String 타입에만 사용한다.255
precision, scale(DDL)BigDecimal 타입에서 사용한다. precision은 소수점을 포함한 전체 자릿수를, scale은 소수의 자릿수다. 참고로 double, float 타입에는 적용되지 않는다. 아주 큰 숫자나 정밀한 소수를 다루어야 할 때만 사용한다.precision=19, scale=2

@Enumerated

  • 자바의 enum 타입을 매핑할 때 사용한다.
  • EnumType.STRING을 권장한다.
속성기능기본값
value

EnumType.ORDINAL: enum 순서를 데이터베이스에 저장

EnumType.STRING: enum 이름을 데이터베이스에 저장

EnumType.ORDINAL

@Temporal

  • 날짜 타입을 매핑할 때 사용한다.
  • 자바의 Date 타입에는 년월일 시분초가 있지만 데이터베이스에는 date, time, timestamp(날짜와 시간)라는 세 가지 타입이 존재한다.
  • @Temporal을 생략하면 자바의 Date와 가장 유사한 timestamp로 정의된다.
속성기능기본값
value

TemporalType.DATE: 날짜, 데이터베이스 date 타입과 매핑(예: 2013-10-11)

TemporalType.TIME: 시간, 데이터베이스 time 타입과 매핑(예: 11:11:11)

TemporalType.TIMESTAMP: 날짜와 시간, 데이터베이스 timestamp 타입과 매핑(예: 2013-10-11 11:11:11)

TemporalType은 필수로 지정해야 한다.

@Lob

  • 데이터베이스 BLOB, CLOB 타입과 매핑한다.
  • 필드 타입이 문자면 CLOB, 나머지는 BLOB으로 매핑한다.

@Transient

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

@Access

  • JPA가 엔티티 데이터에 접근하는 방식을 지정한다.
  • 필드 접근:
    • AccessType.FIELD로 지정
    • 필드에 직접 접근한다. 필드 접근 권한이 private이어도 접근할 수 있다.
  • 프로퍼티 접근:
    • AccessType.PROPERTY로 지정한다.
    • getter를 사용한다.

+ 따끈한 최근 게시물