@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에 문자의 크기를 지정할 수 있다.
@Table
의uniqueConstraints
속성으로 유니크 제약 조건을 추가할 수 있다.- 이런 기능들은 단지 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 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다.
- 그런데 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
- CLOB:
@Transient
- 이 필드는 데이터베이스에 저장하지 않고 조회하지 도 않는다.
- 객체에 임시로 어떤 값을 보관하고 싶을 때 사용한다.
@Access
- 필드 접근:
AccessType.FIELD
로 지정한다. 필드에 직접 접근한다. 필드 접근 권한이 private이어도 접근할 수 있다. - 프로퍼티 접근:
AccessTYpe.PROPERTY
로 지정한다. 접근자(getter)를 사용한다. @Access
를 설정하지 않으면@Id
의 위치를 기준으로 접근 방식이 설정된다.- 아래는
@Id
가 프로퍼티에 있으므로@Access(AccessType.PROPERTY)
로 설정한 것과 같다. 따라서 생략 가능하다. - 필드 접근 방식과 프로퍼티 접근 방식을 함께 사용할 수도 있다.
@Id
가 필드에 있으므로 기본은 필드 접근 방식을 사용하고,getFullName()
만 프로퍼티 접근 방식을 사용한다.