@Entity

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

kotlin을 사용할 때는 maven의 jpa 플러그인을 사용 한다면 파라미터가 없는 생성자를 만들 필요가 없다. (https://kotlinlang.org/docs/no-arg-plugin.html#jpa-support)

kotlin을 사용할 때는 기본적으로 final 클래스로 만들어지는데, 아래와 같이 플러그인을 이용해서 특정 어노테이션이 붙어 있는 경우에는 open 클래스로 만들도록 수정할 수 있다. final 클래스로 만들지 않으면 컴파일 타임에 에러가 발생하지 않고, lazy 로딩이 아닌 전부 eager 로딩이 되니 주의 해야된다.

@Table

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

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

  • JPA는 데이터베이스 스키마를 자동으로 생성하는 기능을 지원한다.
    • 클래스의 매핑 정보를 보면 어떤 테이블에 어떤 컬럼을 사용하는지 알 수 있다.
    • JPA는 이 매핑정보와 데이터베이스 방언을 사용해서 데이터베이스를 생성한다.
  • 하이버네이트 기준 hibernate.hdb2ddl.auto, spring data jpa 기준 spring.jpa.hibernate.ddl-auto 프로퍼티로 설정
  • 운영 서버에서 create, create-drop, update처럼 DDL을 수정하는 옵션을 절대 사용하면 안된다.
  • 이름 매핑 전략 변경하기
    • hibernate.ejb.naming_strategy 속성을 사용하면 이름 매핑 전략을 변경할 수 있다. (기본값: ImprovedNamingStrategy)
    • spring data jpa 기준 spring.jpa.hibernate.naming.implicit-strategy 속성으로 매핑 전략을 바꿀 수 있고 기본값은 SpringImplicitNamingStrategy

DDL 생성 기능

  • @Column
    • nullable 속성 값을 false로 지정하면 자동 생성되는 DDL에 not null 제약조건을 추가할 수 있다.
    • length 속성 값을 사용하면 자동으로 생성되는 DDL에 문자의 크기를 지정할 수 있다.
  • @TableuniqueConstraints 속성으로 유니크 제약 조건을 추가할 수 있다.
  • 이런 기능들은 단지 DDL을 자동으로 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다.

기본 키 매핑

  • JPA가 제공하는 데이터베이스 기본 키 생성 전략
    • 직접 할당: 기본 키를 애플리케이션에서 직접 할당한다.
    • 자동 생성: 대리 키 사용 방식(Id@GeneratedValue 추가)
      • IDENTITY: 기본 키 생성을 데이터베이스에 위임한다.
      • SEQUENCE: 데이터베이스 시퀀스를 사용해서 기본 키를 할당한다.
      • TABLE: 키 생성 테이블을 사용한다.
  • 자동 생성 전략이 이렇게 다양한 이유는 데이터베이스 벤더마다 지원하는 방식이 다르기 때문이다.
    • 오라클 데이터베이스는 시퀀스를 제공하지만 MySQL은 시퀀스를 제공하지 않는다.
    • MySQL은 기본 키 값을 자동으로 채워주는 AUTO_INCREMENT 기능을 제공한다.
    • TABLE 전략은 키 생성용 테이블을 하나 만들어두고 마치 시퀀스처럼 사용하는 방법이라 모든 데이터베이스에서 사용할 수 있다.

기본 키 직접 할당 전략

  • 기본 키를 직접 할당하려면 다음 코드와 같이 @Id로 매핑하면 된다.
  • @Id 적용 가능 자바 타입
    • 자바 기본형
    • 자바 래퍼형
    • String
    • java.util.Date
    • java.sql.Date
    • java.math.BigDecimal
    • java.math.BigInteger
  • 기본 키 직접 할당 전략이 식별자 값 없이 저장하면 JPA 최 상위 예외인 PersistenceException 예외가 발생하는데, 내부에 하이버네이트 예외인 IdentifierGenerationException 예외를 포함하고 있다.

IDENTITY 전략

  • MySQL의 AUTO_INCREMENT 기능은 데이터베이스가 기본 키를 자동으로 생성해준다.
  • 엔티티가 영속 상태가 되려면 식별자가 반드시 필요하다.
    • 그런데 IDENTITY 식별자 생성 전략은 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있으므로 em.persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달된다.
    • 따라서 IDENTITY 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다.

SEQUENCE 전략

  • 시퀀스를 미리 생성해야 된다.
    • @SequenceGenerator를 사용해서 시퀀스 생성기를 등록할 수 있다.
  • SEQUENCE 전략은 em.persist()를 호출할 때,
    • 먼저 데이터베이스 시퀀스를 사용해서 식별자를 조회한다.
    • 그리고 조회한 식별자를 엔티티에 할당한 후에 엔티티를 영속성 컨텍스트에 저장한다.
    • 이후 트랜잭션을 커밋해서 플러시가 일어나면 엔티티를 데이터베이스에 저장한다.
  • SequenceGenerator.allocationSize가 50이면 시퀀스를 호출할 때마다 값이 50씩 증가한다.
    • JPA는 시퀀스를 접근하는 회수를 줄이기위해 한 번에 시퀀스 값을 50만큼 증가시키고 1~50까지는 메모리에서 식별자를 할당한다.
    • 그리고 51이 되면 시퀀스 값을 100으로 증가시킨 다음 51~100까지 메모리에서 식별자를 할당한다.
  • @SequenceGenerator는 다음과 같이 @GeneratorValue 옆에 사용해도 된다.

TABLE 전략

  • 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 스퀀스를 흉내내는 전략이다.
  • TABLE 전략을 사용하려면 먼저 키 생성 용도로 사용할 테이블을 만들어야 한다.
    • @TeableGenerator를 사용해 테이블 키 생성기를 등록한다.

AUTO 전략

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

권장하는 식별자 선택 전략

  • 데이터베이스 기본 키는 3가지 조건을 모두 만족해야 한다.
    • null값은 허용하지 않는다.
    • 유일해야 한다.
    • 변해선 안 된다.
  • 테이블의 기본 키를 선택하는 전략을 2가지가 있다.
    • 자연 키: 비즈니스에 의미가 있는 키
      • 주민등록번호, 이메일, 전화번호
    • 대리 키: 비즈니스와 관련 없는 임의로 만들어진 키, 대체 키로도 불린다.
      • 오라클 시퀀스, auto_increment, 키생성 테이블 사용
  • 자연 키보다는 대리 키를 권장한다.
    • 현실과 비즈니스 규칙은 생각보다 쉽게 변한다.
    • 주민등록번호조차도 여러 가지 이유로 변경될 수 있다.
    • 비즈니스 환경은 언젠가 변한다.
  • JPA는 모든 엔티티에 일관된 방식으로 대리 키 사용을 권장한다.

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

@Column

  • nullable 속성의 경우 기본값에 예외가 있다.
    • @Column이 생략되었을 경우 int 같은 자바 기본 타입에서는 nullable=false로 제약조건이 추가된다.
    • @Column이 생략되지 않은 경우 int 같은 자바 기본 타입에도 nullable=true로 설정된다.
      • 따라서, 자바 기본 타입에 @Column을 사용하면 nullable = false로 지정하는 것이 안전하다.

@Enumerated

  • EnumType.ORDINAL
    • 장점: 데이터베이스에 저장되는 데이터 크기가 작다.
    • 단점: 이미 저장된 enum의 순서는 변경할 수 없다.
  • EnumType.STRING (권장)
    • 장점: 저장된 enum의 순서가 바뀌거나 enum이 추가되어도 안전하다.
    • 단점: 데이터베이스에 저장되는 데이터 크기가 ORDINAL에 비해서 크다.

@Temporal

  • MySQL 같이 방언에따라 timestamp 대신에 datetime을 예약어로 사용하는 데이터베이스도 있다.
    • datetime: MySQL
    • timestamp: H2, 오라클, PostgreSQL

@Lob

  • 지정할 수 있는 속성이 없다.
  • 매핑하는 필드 타입이 문자면 CLOB으로 매핑하고 나머지는 BLOB으로 매핑한다.
    • CLOB: String, char[], java.sql.CLOB
    • BLOB: byte[], java.sql.BLOB

@Transient

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

@Access

  • 필드 접근: AccessType.FIELD로 지정한다. 필드에 직접 접근한다. 필드 접근 권한이 private이어도 접근할 수 있다.
  • 프로퍼티 접근: AccessTYpe.PROPERTY로 지정한다. 접근자(getter)를 사용한다.
  • @Access를 설정하지 않으면 @Id의 위치를 기준으로 접근 방식이 설정된다.
  • 아래는 @Id가 프로퍼티에 있으므로 @Access(AccessType.PROPERTY)로 설정한 것과 같다. 따라서 생략 가능하다.
  • 필드 접근 방식과 프로퍼티 접근 방식을 함께 사용할 수도 있다.
    • @Id가 필드에 있으므로 기본은 필드 접근 방식을 사용하고, getFullName() 만 프로퍼티 접근 방식을 사용한다.