BOOK 3 - 자바 ORM 표준 JPA 프로그래밍(4)

엔티티 매핑

@Entity

  • JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 어노테이션을 필수로 선언해야 한다.
  • @Entity 적용시 주의사항
    • 기본 생성자가 필수로 있어야 한다.
    • final 클래스, enum , interface , inner 클래스에는 사용할 수 없다.
    • 저장할 필드에 final 을 사용하면 안된다.

@Table

  • @Table 은 엔티티와 매핑할 테이블을 지정한다.

    • 생략하면 매핑한 엔티티 이름을 테이블 이름으로 사용한다.

@Access

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

다양한 Field 매핑

  • @Column : entity 필드와 table 컬럼 매핑
    • name : DB table의 컬럼 이름 지정
    • nullable : not null 제약 조건 지정
    • length : 문자 크기 지정
    • nullablelength 속성은 DDL 자동 생성때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다.
      • 즉, 스키마 자동 생성 기능을 사용하지 않는다면 위 속성들을 사용하지 않아도 되지만 어플리케이션 개발자가 쉽게 엔티티 제약 조건을 파악할 수 있는 장점이 있다.
  • @Id : primary key 매핑
  • @Enumerated : 자바의 enum 타입 필드와 매핑
    • EnumType.ORDINAL : enum 순서를 데이터베이스에 저장
    • EnumType.STRING : enum 이름을 데이터베이스에 저장
  • @Temporal : 자바의 날짜 타입 필드와 매핑
    • TemporalType.DATE : date 타입과 매핑
    • TemporalType.TIME : time 타입과 매핑
    • TemporalType.TIMESTAMP : timestamp 타입과 매핑
  • @Transient : 데이터베이스와 매핑하지 않는다.
    • 객체에 임시로 어떤 값을 보관하고 싶을 때 사용한다.

기본 키 매핑

  • 기본 키를 어플리케이션에서 직접 할당하는 대신에 데이터베이스가 생성해주는 값을 사용할 수 있다.
    • 예를 들어, MySQL의 AUTO_INCREMENT 기능
  • JPA가 제공하는 데이터베이스 기본 키 생성 전략
    • IDENTITY : 기본 키 생성을 데이터베이스에 위임한다.
    • SEQUENCE : 데이터베이스 시퀀스를 사용해서 기본 키를 할당한다.
    • TABLE : 키 생성 테이블을 사용한다.
    • MySQL은 시퀀스를 제공하지 않고 Oracle은 시퀀스를 제공하는 대신 AUTO_INCREMENT 같은 기능을 지원하지 않으므로 IDENTITYSEQUENCE 는 데이터베이스에 의존적이다.
  • 자동 생성 전략을 사용하려면 @Id 를 선언한 필드에 @GeneratedValue 를 추가하고 원하는 키 생성 전략을 지정하면 된다.

IDENTITY

  • 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용하는 전략이다.
@GeneratedValue(strategy=GenerationType.IDENTITY)
  • AUTO_INCREMENT 처럼 데이터베이스에 값을 저장하고 나서야 기본 키 값을 구할 수 있다.
    • 이 전략을 사용하면 JPA는 기본 키 값을 얻어오기 위해 데이터베이스를 추가로 조회한다.
em.persist(user);
System.out.println("user.id = " + user.getId());
// 출력 : user.id = 1
  • persist() 를 사용해서 엔티티를 저장한 직후 할당된 식별자 값을 조회한다.

  • 엔티티가 영속 상태가 되려면 식별자가 반드시 필요하다. IDENTITY 전략은 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있으므로 persist() 를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달되고, 기본 키 값을 조회한다.

    • 따라서 이 전략은 쓰기 지연이 동작하지 않는다.

SEQUENCE

  • 데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트이다.

  • 주로 Oracle, PostgreSQL, DB2, H2에서 사용하는 전략이다.

  • 다음과 같이 데이터베이스 시퀀스를 만들 수 있다.

    CREATE SEQUENCE USER_SEQ START WITH 1 INCREMENT BY 1;
    
  • entity에서 사용할 데이터베이스 시퀀스를 매핑하기 위해 @SequenceGenerator 를 사용해서 시퀀스 생성기를 등록한다.

    @Entity
    @SequenceGenerator(
    	name="USER_SEQ_GENERATOR",
      sequenceName="USER_SEQ",
      initialValue=1, allocationSize=1
    )
    public class User {
    ...
    
    • name : 식별자 생성기 이름
    • sequenceName : 데이터베이스에 등록되어 있는 시퀀스 이름
    • initialValue : 시퀀스 생성시 처음 시작하는 수(DDL 자동 생성시에만 사용됨)
    • allocationSize : 시퀀스 한번 호출에 증가하는 수 (최적화에 사용됨)
  • 키 생성 전략과 시퀀스 생성기를 지정한다.

    @GeneratedValue(strategy=GenerationType.SEQUENCE,
                   	generator="USER_SEQ_GENERATOR")
    
  • persist() 를 호출할 때 먼저 데이터베이스 시퀀스를 사용해서 식별자를 조회한다.

    • 조회한 식별자를 엔티티에 할당한 후 엔티티를 영속성 컨텍스트에 저장한다.
    • 이후 트랜잭션 커밋으로 flush가 일어나면 엔티티를 데이터베이스에 저장한다.

SEQUENCE 전략 최적화

  • SEQUENCE 전략은 데이터베이스 시퀀스를 통해 식별자를 조회하는 추가 작업이 필요하여 다음과 같이 데이터베이스와 2번 통신한다.

    • 식별자를 구하기 위한 데이터베이스 시퀀스 조회
    • 조회한 시퀀스를 기본 키 값으로 사용해 데이터베이스에 저장
  • JPA는 시퀀스에 접근하는 횟수를 줄이기 위해 allocationSize 를 사용한다.

  • 설정한 allocationSize 값만큼 한번에 시퀀스 값을 증가시키고 나서 그만큼 메모리에 시퀀스 값을 할당한다.

    • 예를 들어 allocationSize 값이 50이면 시퀀스를 한번에 50 증가시키고, 1~50 까지는 데이터베이스에 직접 접근하지 않고 메모리에서 식별자를 할당한다.

    • 시퀀스 값을 선점하므로 여러 JVM이 동시에 동작해도 기본 키 값 충돌이 일어나지 않는 장점이 있다.

  • INSERT 성능이 중요하지 않으면 allocationSize 를 1로 설정하면 된다.

TABLE

  • 키 생성 전용 테이블을 하나 만들고 이름과 기본 키 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략이다.

    • 시퀀스 대신에 테이블을 사용한다는 것만 제외하면 SEQUENCE 전략과 내부 동작 방식이 같다.
  • 테이블을 사용하므로 모든 데이터베이스에서 사용할 수 있다.

    create TABLE MY_SEQUENCES (
    	sequence_name varchar(255) not null primary key,
      next_val bigint
    )
    
    • sequence_name : 시퀀스 이름
    • next_val : 시퀀스 값
  • 테이블 키 생성기를 등록해야 한다.

    @Entity
    @TableGenerator(
    	name="USER_SEQ_GENERATOR",
      table="MY_SEQUENCES",
      pkColumnValue="USER_SEQ", allocationSize=1
    )
    
    • name : 식별자 생성기 이름
    • table : 키 생성 테이블명
    • pkColumnName : 시퀀스 컬럼명
    • valueColumnName : 시퀀스 값 컬럼명
  • 키 생성 전략과 테이블 키 생성기를 지정한다.

    @GeneratedValue(strategy=GenerationType.TABLE,
                   	generator="USER_SEQ_GENERATOR")
    

TABLE 전략 최적화

  • TABLE 전략은 기본 키 값을 조회하면서 SELECT 쿼리를 사용하고 다음 값으로 증가시키기 위해 UPDATE 쿼리를 사용한다.
    • SEQUENCE 전략과 비교해서 데이터베이스와 한번 더 통신하는 단점이 있다.
  • 데이터베이스 통신 횟수를 줄여 최적화 하기 위해 allocationSize 를 사용하고, 최적화 방법은 SEQUENCE 전략과 동일하다.

AUTO

  • 선택한 Database Dialect에 따라 IDENTITY , SEQUENCE , TABLE 전략 중 하나를 자동으로 선택한다.
  • 데이터베이스를 변경해도 코드를 수정할 필요가 없다는 장점이 있다.
@GeneratedValue(strategy=GenerationType.AUTO)
또는
@GeneratedValue