Chapter 4: 엔티티 매핑

1. @Entity

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

    • @Entity 가 붙은 class는 JPA가 관리한다

  • @Entity 속성

    • name

      • JPA에서 사용할 엔티티 이름을 지정

      • 보통 기본값인 클래스 이름을 사용

        • 설정하지 않으면 클래스 이름을 그대로 사용한다 (default value)

      • 만약 다른 패키지에 이름이 같은 엔티티 클래스가 있다면, 이름을 지정해서 충돌하지 않게 해야함!

  • @Entity 적용 시 주의사항

    • 기본 생성자는 필수 (파라미터가 없는 public or protected 생성자)

    • final class, enum, interface, inner class에는 사용할 수 없다

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

2. @Table

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

  • @Table 속성

    • name

      • 매핑할 테이블 이름 (default는 매핑한 엔티티 이름)

    • catalog

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

    • schema

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

    • uniqueConstraints (DDL)

      • DDL 생성 시에 유니크 제약조건을 만든다

      • 2개 이상의 복합 유니크 제약조건도 만들 수 있다

      • 이 기능은 스키마 자동 생성 기능을 사용해서 DDL을 만들 때만 사용된다

3. 다양한 매핑 사용

  • @Enumerated

    • 자바의 enum을 사용하려면 @Enumerated annotation으로 매핑해야 한다

  • @Temporal

    • 자바의 날짜 타입 매핑

  • @Lob

    • 길이 제한이 없는 필드에 대해 데이터베이스의 VARCHAR 타입 대신에 CLOB 타입으로 지정한다

    • @Lob 을 사용하면 CLOB, BLOB 타입을 매핑할 수 있다

4. 데이터베이스 스키마 자동 생성

  • JPA는 데이터베이스 스키마자동으로 생성하는 기능을 지원한다

  • Class의 매핑 정보를 보면 어떤 테이블에 어떤 컬럼을 사용하는지 알 수 있다

    • JPA는 이 매핑정보와 데이터베이스 dialect를 사용해서 데이터베이스 스키마를 생성한다

  • application.ymlspring.jpa.hibernate.ddl-auto 에 아래의 속성을 주어 자동 생성 옵션을 설정할 수 있다

    • create

      • 기존 테이블을 삭제하고 새로 생성한다

      • DROP + CREATE

    • create-drop

      • create 속성 + 애플리케이션 종료 시 생성한 DDL 제거

      • DROP + CREATE + DROP

    • update

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

    • validate

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

      • 이 설정은 DDL을 수정하지 않는다

    • none

      • 자동 생성 기능을 사용하지 않으려면 유효하지 않은 옵션값을 주면 된다

      • none은 유효하지 않은 옵션값이다

  • 자동 생성되는 DDL은 지정한 데이터베이스 dialect에 따라 달라진다

  • 스키마 자동 생성 기능을 사용하면 애플리케이션 실행 시점에 데이터베이스 테이블이 자동으로 생성되므로 개발자가 테이블을 직접 생성하는 수고를 덜 수 있다

    • but, 스키마 자동 생성 기능이 만든 DDL은 운영 환경에서 사용할 만큼 완벽하지는 않으므로 개발 환경에서 사용하거나 매핑을 어떻게 해야 하는지 참고하는 정도로만 사용하는 것이 좋다

HBM2DDL 주의사항

  • 운영 서버에서 create, create-drop, update처럼 DDL을 수정하는 옵션은 절대 사용하면 안 된다

    • 개발 서버나 개발 단계에서만 사용해야 한다

    • 이 옵션들은 운영중인 데이터베이스의 테이블이나 컬럼을 삭제할 수 있다

  • 개발 환경에 따른 추천 전략

    • 개발 초기 단계

      • create or update

    • 테스트 서버

      • update or validate

    • 스테이징 & 운영 서버

      • validate or none

이름 매핑 전략 변경하기

  • 자바는 카멜(Camel) 표기법, 데이터베이스는 언더스코어(_)를 주로 사용한다

  • hibernate.ejb.naming_strategy 속성을 사용해서 이름 매핑 전략을 변경할 수 있다

5. DDL 생성 기능

  • @Column 매핑정보의 nullable 속성 값을 false로 지정하면 자동 생성되는 DDL에 not null 제약조건을 추가할 수 있다

    • 그리고 length 속성 값을 사용하면 자동 생성되는 DDL에 문자의 크기를 지정할 수 있다

  • @Columnlengthnullable 속성과 같은 기능들은 DDL을 자동으로 생성할 때만 사용되고, JPA의 실행 로직에는 영향을 주지 않는다

    • 따라서 스키마 자동 생성 기능을 사용하지 않고 직접 DDL을 만든다면 사용할 이유가 없다

    • 그래도 이 기능을 사용하면 애플리케이션 개발자가 엔티티만 보고도 손쉽게 다양한 제약 조건을 파악할 수 있는 장점이 있다

6. 기본 키 매핑

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

    • 직접 할당

      • 기본 키를 애플리케이션에서 직접 할당

    • 자동 생성 (대리 키 사용 방식)

      • IDENTITY

        • 기본 키 생성을 데이터베이스에 위임

      • SEQUENCE

        • 데이터베이스 시퀀스를 사용해서 기본 키를 할당

      • TABLE

        • 키 생성 테이블을 사용

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

6-1. 기본 키 직접 할당 전략

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

  • @Id 적용 가능 자바 타입

    • 자바 기본형

    • 자바 Wrapper 형

    • String

    • java.util.Date

    • java.sql.Date

    • java.math.BigDecimal

    • java.math.BigInteger

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

6-2. IDENTITY 전략

  • 기본 키 생성을 데이터베이스에 위임하는 전략이다

    • 데이터베이스에 값을 저장하고 나서야 기본 키 값을 구할 수 있을 때 사용한다

    • 이 전략을 사용하면 JPA는 기본 키 값을 얻어오기 위해 데이터베이스를 추가로 조회 한다

  • 사용 방법

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
  • ex)

    • MySQL의 AUTO_INCREMENT 기능은 데이터베이스가 기본 키를 자동으로 생성해 준다

  • 최적화

    • IDENTITY 전략은 데이터를 데이터베이스에 INSERT한 후에 키 값을 조회할 수 있다

      • 따라서 엔티티에 식별자 값을 할당하려면, JPA는 추가로 데이터베이스를 조회해야 한다

    • JDBC3에 추가된 Statement.getGeneratedKeys() 를 사용하면 데이터를 저장하면서 동시에 생성된 기본 키 값도 얻어올 수 있다

      • 하이버네이트는 이 method를 사용해서 데이터베이스와 한 번만 통신한다

  • 주의

    • 엔티티가 영속 상태가 되려면 식별자가 반드시 필요하다

    • 그런데 IDENTITY 식별자 생성 전략은 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있으므로, em.persist() 를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달된다

      • 따라서 이 전략은 트랜잭션 을 지원하는 쓰기 지연 이 동작하지 않는다

6-3. SEQUENCE 전략

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

    • SEQUENCE 전략은 이 시퀀스를 사용해서 기본 키를 생성한다

  • SEQUENCE 전략은 시퀀스를 지원하는 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용할 수 있다

  • SEQUENCE 전략은 em.persist() 를 호출할 때 먼저 데이터베이스 시퀀스를 사용해서 식별자조회 한다

    • 그리고 조회한 식별자를 엔티티할당한 후에 엔티티를 영속성 컨텍스트에 저장 한다

    • 이후 트랜잭션을 커밋 해서 플러시 가 일어나면, 엔티티를 데이터베이스에 저장한다

      • 6-2의 INDENTITY 전략은 먼저 엔티티를 데이터베이스에 저장한 후에 식별자를 조회해서 엔티티의 식별자에 할당한다

  • @SequenceGenerator 속성

    • 주의!

      • SequenceGenerator.allocationSize 의 기본값이 50인 것에 주의해야 한다

      • JPA가 기본으로 생성하는 데이터베이스 시퀀스는 아래와 같으므로, 시퀀스를 호출할 때마다 값이 50씩 증가한다

        create sequence [sequenceName] start with 1 increment by 50
      • 데이터베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값을 반드시 1로 설정해야 한다

  • SEQUENCE 전략과 최적화

    • SEQUENCE 전략은 데이터베이스 시퀀스를 통해 식별자를 조회하는 추가 작업이 필요하다

      • 따라서 데이터베이스와 2번 통신한다

        1. 식별자를 구하려고 데이터베이스 시퀀스를 조회한다

        SELECT BOARD_SEQ.NEXTVAL FROM DUAL
        1. 조회한 시퀀스를 기본 키 값으로 사용해 데이터베이스에 저장한다

        INSERT INTO BOARD ...
    • JPA는 시퀀스에 접근하는 횟수를 줄이기 위해 @SequenceGenerator.allocationSize 를 사용한다

      • 해당 속성에 설정한 값만큼 한 번에 시퀀스를 증가시키고 나서, 그만큼 메모리에 시퀀스 값을 할당한다

        • ex)

          • allocationSize 값이 50이면 시퀀스를 한 번에 50 증가시킨 다음에 1~50까지는 메모리에서 식별자를 할당한다

          • 그리고 51이 되면 시퀀스 값을 100으로 증가시킨 다음 51~100까지 메모리에서 식별자를 할당한다

      • 장점

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

      • 단점

        • 데이터베이스에 직접 접근해서 데이터를 등록할 때, 시퀀스 값이 한 번에 많이 증가한다는 점을 염두해두어야 한다

          • 이런 상황이 부담스럽고, INSERT 성능이 중요하지 않으면 allocationSize 의 값을 1로 설정하면 된다

6-4. TABLE 전략

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

    • 이 전략은 테이블을 사용하므로, 모든 데이터베이스적용할 수 있다

  • ex)

    @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 를 사용해서 테이블 키 생성기를 등록한다

    • GenerationType.TABLE 을 사용하여 TABLE 전략을 사용함을 명시한다

    • @GeneratedValue.generator 에 테이블 키 생성기를 지정한다

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

  • @TableGenerator 속성

  • TABLE 전략과 최적화

    • TABLE 전략은 값을 조회하면서 SELECT 쿼리를 사용하고, 다음 값으로 증가시키기 위해 UPDATE 쿼리를 사용한다

      • SEQUENCE 전략과 비교해서 데이터베이스와 한 번 더 통신하는 단점이 있다

    • TABLE 전략을 최적화하려면, @TableGenerator.allocationSize 를 사용하면 된다

      • 최적화 방법은 위의 SEQUENCE 전략과 동일!

6-5. AUTO 전략

  • AUTO는 선택한 데이터베이스 dialect에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다

  • AUTO 전략의 장점은 데이터베이스를 변경해도 코드를 수정할 필요가 없다는 것이다

    • 특히 키 생성 전략이 아직 확정되지 않은 개발 초기 단계나 프로토타입 개발 시 편리하게 사용할 수 있다

  • AUTO를 사용할 때, SEQUENCE나 TABLE 전략이 선택되면 시퀀스나 키 생성용 테이블을 미리 만들어 두어야 한다

    • 만약 스키마 자동 생성 기능을 사용한다면, 하이버네이트가 기본값을 사용해서 적절한 시퀀스나 키 생성용 테이블을 만들어 준다

6-6. 권장하는 식별자 선택 전략

  • JPA는 모든 엔티티에 일관된 방식으로 대리 키 사용을 권장한다

    • 비즈니스 요구사항은 계속해서 변하는데, 테이블은 한 번 정의하면 변경하기 어렵다

    • 그런 면에서 외부 풍파에 쉽게 흔들리지 않는 대리 키가 일반적으로 좋은 선택!

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

7-1. @Column

  • 객체 필드를 테이블 컬럼에 매핑한다

  • 속성 중 name, nullable이 주로 사용되고 나머지는 잘 사용되지 않는다

  • 속성

    • nullable(DDL)

      • null 값의 허용 여부를 설정한다

      • false로 설정하면, DDL 생성 시에 not nul 제약 조건이 붙는다

7-2. @Enumerated

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

  • 속성

    • value

      • EnumType.ORDINAL (기본값)

        • enum 순서를 데이터베이스에 저장

        • 장점

          • 데이터베이스에 저장되는 데이터 크기가 같다

        • 단점

          • 이미 저장된 enum의 순서를 변경할 수 없다

      • EnumType.STRING

        • enum 이름을 데이터베이스에 저장

        • 장점

          • 저장된 enum의 순서가 바뀌거나 enum이 추가되어도 안전한다

        • 단점

          • 데이터베이스에 저장되는 데이터 크기가 ORDINAL에 비해서 크다

7-3. @Temporal

  • 날짜 타입 (java.util.Date, java.util.Calendar) 을 매핑할 때 사용한다

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

    • @Temporal 을 생략하면 자바의 Date와 가장 유사한 timestamp로 정의된다

    • but, timestmp 대신에 datetime을 예약어로 사용하는 데이터베이스도 있는데, 데이터베이스 dialect 덕분에 애플리케이션 코드는 변경하지 않아도 된다

  • 데이터베이스 방언에 따라 생성되는 DDL

    • datetime: MySQL

    • timestamp: H2, 오라클, PostgreSQL

7-4. @Lob

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

  • 속성

    • @Lob 에는 지정할 수 있는 속성이 없다

    • 대신에 매핑하는 필드 타입이

      • 문자CLOB으로 매핑하고,

      • 나머지BLOB으로 매핑한다

7-5. @Transient

  • 이 필드는 매핑하지 않는다

  • 데이터베이스에 저장하지 않고, 조회하지도 않는다

  • 객체에 임시로 어떤 값을 보관하고 싶을 때 사용한다

7-6. @Access

  • JPA가 엔티티 데이터에 접근하는 방식을 지정한다

  • 필드 접근

    • AccessType.FIELD로 지정한다

    • 필드에 직접 접근한다

    • 필드 접근 권한이 private이어도 접근할 수 있다

  • 프로퍼티 접근

    • AccessType.PROPERTY로 지정한다

    • 접근자(Getter)를 사용한다

  • @Access 를 설정하지 않으면, @Id 의 위치를 기준으로 접근 방식이 결정된다

    • @Id 가 필드에 있으면, @Access(AccessType.FIELD) 로 설정한 것 과 같다

    • @Id 가 프로퍼티에 있으면, @Access(AccessType.PROPERTY) 로 설정한 것과 같다

Last updated