꾸준함. 2025. 2. 21. 23:37

@Entity

  • JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 어노테이션을 필수로 붙여야 함
  • @Entity가 붙은 클래스는 JPA가 관리하는 것으로 엔티티라 부름
  • @Entity 적용 시 주의사항은 다음과 같음
    • 기본 생성자는 필수
    • final 클래스, enum, interface, inner 클래스에는 사용할 수 없음
    • 저장할 필드에 final을 사용하면 안 됨

 

속성 기능 기본값
name JPA에서 사용할 엔티티 이름을 지정
보통 기본값인 클래스 이름을 사용

다른 패키지에 이름이 같은 엔티티 클래스가 있다면 name을 지정해서 충돌하지 않도록 해야 함
설정하지 않을 경우 클래스 이름을 그대로 사용

 

@Table

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

 

속성 기능 기본값
name 매핑할 테이블명 엔티티 이름을 사용
catalog catalog 기능이 있는 DB에서 catalog 매핑  
schema schema 기능이 있는 DB에서 schema 매핑  
uniqueConstraints
(DDL)
DDL 생성 시 유니크 제약조건을 만듦
2개 이상의 복합 유니크 제약조건도 만들 수 있음

dll-auto를 통해 스키마 자동 생성 기능을 사용해서 DDL을 만들 때만 사용됨
 

 

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

  • JPA는 DB 스키마를 자동으로 생성하는 기능을 지원함
    • 클래스의 매핑 정보를 토대로 어떤 테이블에 어떤 컬럼을 사용하는지 알 수 있음
    • JPA는 해당 매핑 정보와 데이터베이스 방언을 사용해 DB 스키마를 생성

 

  • persistence.xml에 다음 속성을 추가하면 애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성함
    • <property name="hibernate.hbm2ddl.auto" value="create" />

 

  • 참고로 hibernate.show_sql 속성을 true로 설정하면 콘솔에 실행되는 테이블 생성 DDL (Data Definition Language)을 출력할 수 있음
    • <property name="hibernate.show_sql" value="true" />

 

요즘은 위 설정을 application.yml에 작성하며 예시는 다음과 같습니다.

 

 

 

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

Member 엔티티가 다음과 같이 정의되었을 때 스키마 자동 생성 기능에 의해 DB 벤더에 맞게 DDL이 실행됩니다.

  • @Column의 length와 nullable 속성, @Table의 uniqueConstraints 속성 같은 기능들은 단지 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않음
  • 해당 기능을 사용하면 애플리케이션 개발자가 엔티티만 보고도 손쉽게 다양한 제약 조건을 파악할 수 있는 장점이 있음

 

 

하지만 스키마 자동 생성 기능이 만든 DDL은 운영 환경에서 사용할 만큼 완벽하지는 않기 때문에 개발 환경에서 사용하거나 매핑을 어떻게 해야 하는지 참고하는 정도로만 사용하는 것을 권장합니다.

 

hibernate.hbm2ddl.auto 속성은 다음과 같습니다.

 

옵션 설명
create 기존 테이블을 삭제하고 새로 생성
DROP + CREATE
create-drop create 속성에 추가로 애플리케이션을 종료할 때 생성한 DDL 제거
DROP + CREATE + DROP
update 데이터베이스 테이블과 엔티티 매핑정보를 비교해서 변경 사항만 수정
validate 데이터베이스 테이블과 엔티티 매핑정보를 비교해서 차이가 있으면 경고를 남기고 애플리케이션을 실행하지 않음

운영 환경에서 권장하는 속성이며 DDL을 수정하지 않음
none 자동 생성 기능을 사용하지 않기 위한 옵션

 

기본 키 매핑

JPA가 제공하는 데이터베이스 기본 키  생성 전략은 다음과 같습니다.

  • 직접 할당: 기본 키를 애플리케이션에서 직접 할당
  • 자동 생성: 대리 키 사용 방식
    • IDENTITY: 기본 키 생성을 데이터베이스에 위임
    • SEQUENCE: 데이터베이스 시퀀스를 사용해서 기본 키를 할당
    • TABLE: 키 생성 테이블을 사용

 

자동 생성 전략이 위와 같이 다양한 이유는 DB 벤더마다 지원하는 방식이 다르기 때문입니다.

  • ex) Oracle DB는 시퀀스를 제공하지만 MySQL은 시퀀스를 제공하지 않는 대신 기본 키 값을 자동으로 채워주는 AUTO_INCREMENT 기능을 제공
  • 따라서 SEQUENCE나 IDENTITY 전략은 사용하는 데이터베이스에 의존
  • TABLE 전략은 키 생성용 테이블을 하나 만들어두고 마치 시퀀스처럼 사용하는 방법이기 때문에 모든 데이터베이스에서 사용 가능

 

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

 

1. 기본 키 직접 할당 전략

기본 키를 직접 할당하기 위해서는 다음 코드와 같이 @Id로 매핑하면 됩니다.

 

 

 

@Id 적용 가능 자바 타입은 다음과 같습니다.

  • 자바 기본형
  • 자바 Wrapper 형
  • String
  • java.util.Date
  • java.sql.Date
  • java.math.BigDecimal
  • java.math.BigInteger

 

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

 

 

2. IDENTITY 전략

  • IDENTITY는 기본 키 생성을 데이터베이스에 위임하는 전략
  • 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용
    • ex) MySQL의 AUTO_INCREMENT 기능은 데이터베이스가 기본 키를 자동으로 생성

 

  • IDENTITY 전략은 데이터베이스에 값을 저장하고 나서야 기본 키 값을 구할 수 있을 때 사용
  • 개발자가 엔티티에 직접 식별자를 할당하면 @Id 어노테이션만 있으면 되지만 식별자가 생성되는 경우에는 @GeneratedValue 어노테이션을 사용하고 식별자 생성 전략을 선택해야 함
    • IDENTITY 전략을 사용하려면 @GeneratedValue의 strategy 속성 값을 GenerationType.IDENTITY로 지정
    • 앞서 언급했다시피 IDENTITY 전략은 저장하고 나서야 기본 키 값을 구할 수 있으므로 JPA는 기본 키 값을 얻어오기 위해 데이터베이스를 추가로 조회함
    • JDBC3에 추가된 Statement.getGeneratedKeys()를 사용하면 데이터를 저장하면서 동시에 생성된 기본 키 값도 얻어올 수 있으므로 하이버네이트는 이 메서드를 사용해서 데이터베이스와 한 번만 통신
    • 엔티티가 영속 상태가 되려면 식별자가 반드시 필요한데 IDENTITY 식별자 생성 전략은 엔티티를 DB에 저장해야  식별자를 구할 수 있으므로 em.persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달되므로 해당 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않음

 

3. SEQUENCE 전략

  • DB 시퀀스는 유일한 값을 순서대로 생성하는 특별한 DB 오브젝트이며 SEQUENCE 전략은 해당 시퀀스를 사용해서 기본 키를 생성함
  • 해당 전략은 시퀀스를 지원하는 Oracle, PostgreSQL, DB2, H2 데이터베이스에서 사용할 수 있음

 

시퀀스를 사용하기 위해 다음과 같이 시퀀스를 생성해야 합니다.

 

 

 

그리고 사용할 데이터베이스 시퀀스를 매핑해야 합니다.

  • 아래 예제에서는 @SequenceGenerator를 사용해서 BOARD_SEQ_GENERATOR라는 시퀀스 생성기를 등록함
  • 그리고 sequenceName 속성의 이름으로 BOARD_SEQ를 지정했는데 JPA는 해당 시퀀스 생성기를 실제 데이터베이스의 BOARD_SEQ 시퀀스와 매핑

 

다음으로 키 생성 전략을 GenerationType.SEQUENCE로 설정하고 generator = "BOARD_SEQ_GENERATOR"로 위에 등록한 시퀀스 생성기를 선택했으며 이제부터 id 식별자 값은 BOARD_SEQ_GENERATOR 시퀀스 생성기가 할당합니다.

 

 

시퀀스 사용 코드는 IDENTITY 전략고 같지만 내부 동작 방식은 다릅니다.

  • SEQUENCE 전략: em.persist()를 호출할 때 먼저 DB 시퀀스를 사용해서 식별자를 조회하고 조회한 식별자를 엔티티에 할당한 뒤 엔티티를 영속성 컨텍스에 저장, 이후 트랜잭션을 커밋해서 flush가 일어나면 엔티티를 데이터베이스에 저장
    • 해당 전략은 데이터베이스 시퀀스를 통해 식별자를 조회하는 추가 작업이 필요하므로 데이터베이스와 두 번 통신
    • JPA는 시퀀스에 접근하는 횟수를 줄이기 위해 @SequenceGenerator.allocationSize 속성을 사용
    • allocationSize 속성에 설정한 값만큼 한 번에 시퀀스 값을 증가시키고 나서 그만큼 메모리에 시퀀스 값을 할당

 

  • IDENTITY 전략: 먼저 엔티티를 데이터베이스에 저장한 뒤 식별자를 조회해서 엔티티의 식별자에 할당함

 

 

 

@SequenceGenerator 속성은 다음과 같습니다.

 

속성 기능 기본값
name 식벼자 생성기 이름 필수
sequenceName DB에 등록되어 있는 시퀀스명 hibernate_sequence
initialValue DDL 생성 시에만 사용

시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정
1
allocationSize 시퀀스 한 번 호출에 증가하는 수
(성능 최적화용)
50
catalog, schema DB catalog, schema 이름  

 

4. TABLE 전략

  • 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내 내는 전략
  • 해당 전략은 테이블을 사용하므로 모든 데이터베이스에 적용할 수 있음

 

TABLE 전략을 사용하려면 먼저 키 생성 용도로 사용할 테이블을 만들어줘야 합니다.

  • sequence_name 컬럼을 시퀀스명으로 사용하고 
  • next_val 컬럼을 시퀀스 값으로 사용
  • 해당 전략은 값을 조회하면서 SELECT 쿼리를 사용하고 다음 값으로 증가시키기 위해 UPDATE 쿼리를 사용하기 때문에 SEQUENCE 전략과 비교해서 데이터베이스와 한 번 더 통신한다는 단점 존재

 

 

이후 @TableGenerator를 사용해서 테이블 키 생성기를 등록하는데 여기서는 BOARD_SEQ_GENERATOR라는 이름의 테이블 키 생성기를 등록하고 방금 생성한 MY_SEQUENCES 테이블을 키 생성용 테이블로 매핑했습니다.

  • 다음으로 TABLE 전략을 사용하기 위해 GenerationType.TABLE을 선택
  • 그리고 @GeneratedValue.generator에 방금 생성한 테이블 키 생성기를 지정하였고 이제부터 id 식별자 값은 BOARD_SEQ_GENERATOR 테이블 키 생성기가 할당

 

 

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

  • MY_SEQUENCES 테이블을 보면 @TableGenerator.pkColumnValue에서 지정한 "BOARD_SEQ"가 컬럼명으로 추가된 것을 확인할 수 있으며 이제 키 생성기를 사용할 때마다 next_val 컬럼 값이 증가함
  • MY_SEQUENCES 테이블에 값이 없으면 JPA가 값을 INSERT하면서 초기화하므로 값을 미리 넣어둘 필요 없음

 

 

@TableGenerator 속성은 다음과 같습니다.

 

속성 기능 기본값
name 식벼자 생성기 이름 필수
table 키 생성 테이블명 hibernate_sequences
pkColumnName 시퀀스 컬럼명 sequence_name
valueColumnName 시퀀스 값 컬럼명 next_val
pkColumnValue 키로 사용할 값 이름 엔티티 이름
initialValue 초기 값
마지막으로 생성된 값이 기준
0
allocationSize 시퀀스 한 번 호출에 증가하는 수
(성능 최적화용)
50
catalog, schema DB catalog, schema 이름  
uniqueConstraints
(DDL)
유니크 제약 조건을 지정 가능  

 

5. AUTO 전략

  • GenerationType.AUTO는 선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택함
    • ex) Oracle을 선택하면 SEQUENCE를, MySQL을 선택하면 IDENTITY를 사용
    • AUTO 전략의 장점은 데이터베이스를 변경해도 코드를 수정할 필요가 없다는 것이며 특히 키 생성 전략이 아직 확정되지 않은 개발 초기 단계나 프로토타입 개발 시 편리하게 사용할 수 있음

 

GeneratedValue.strategy의 기본값은 AUTO이기 때문에 다음과 같이 사용해도 결과는 같습니다.

 

 

주의: AUTO를 사용할 때 SEQUENCE나  TABLE 전략이 선택되면 시퀀스나 키 생성용 테이블을 미리 만들어 두어야 합니다.

  • 스키마 자동 생성 기능을 사용한다면 hibernate가 기본값을 사용해서 적절한 시퀀스나 키 생성용 테이블을 만들어줄 것

 

6. 기본 키 매핑 정리

영속성 컨텍스트는 엔티티를 식별자 값으로 구분하므로 엔티티를 영속 상태로 만들려면 식별자 값이 반드시 있어야 하며 em.persist()를 호출한 직후 발생하는 일을 식별자 할당 전략별로 정리하면 다음과 같습니다.

  • 직접 할당: em.persist()를 호출하기 전 애플리케이션에서 직접 식별자 값을 할당해야 하며 만약 식별자 값이 없으면 예외가 발생함
  • SEQUENCE: 데이터베이스 시퀀스에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장
  • TABLE: 데이터베이스 시퀀스 생성용 테이블에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장
  • IDENTITY: 데이터베이스에 엔티티를 저장해서 식별자 값을 획득한 후 영속성 컨텍스트에 저장

 

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

JPA가 제공하는 필드와 컬럼 매핑용 어노테이션들은 다음과 같습니다.

 

분류 매핑 어노테이션 설명
필드와 컬럼 매핑 @Column 컬럼을 매핑
@Enumerated 자바의 enum 타입을 매핑
@Temporal 날짜 타입을 매핑
@Lob BLOB, CLOB 타입을 매핑
@Transient 특정 필드를 DB에 매핑하지 않음
기타 @Access JPA가 엔티티에 접근하는 방식을 지정

 

1. @Column

  • 객체 필드를 테이블 컬럼에 매핑하는 어노테이션
  • 속성 중에 name, nullable이 주로 사용되고 나머지는 잘 사용되지 않는 편
  • insertable, updatable 속성은 DB에 저장되어 있는 정보를 읽기만 하고 실수로 변경하는 것을 방지하고 싶을 때 사용

 

속성  기능 기본값
name 필드와 매핑할 테이블의 컬럼 이름 객체의 필드 이름
insertable
(거의 사용하지 않음)
엔티티 저장 시 해당 필드도 같이 저장됨

false로 설정하면 해당 필드는 DB에 저장하지 않음
true
updatable
(거의 사용하지 않음)
엔티티 수정 시 해당 필드도 같이 수정됨

false로 설정하면 해당 필드는 DB에 수정하지 않음
true
table
(거의 사용하지 않음)
하나의 엔티티를 두 개 이상의 테이블에 매핑할 때 사용 현재 클래스가 매핑된 테이블
nullable
(DDL)
null값의 허용 여부 설정

false로 설정하면 DDL 생성 시 not null 제약 조건 붙음
true
unique
(DDL)
@Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약 조건을 걸 때 사용

두 컬럼 이상을 사용해서 유니크 제약 조건을 사용하려면 클래스 레벨에서 @Table.uniqueConstraints를 사용
 
columnDefinition
(DDL)
데이터베이스 컬럼 정보를 직접 줄 수 있음 필드의 자바 타입과 방언 정볼르 사용해서 적절한 컬럼 타입을 생성
length
(DDL)
문자 길이 제약조건, String 타입에만 사용 255
precision, scale
(DDL)
BigDecimal 타입에서 사용

precision은 소수점을 포함한 전체 자릿수를, scale은 소수의 자릿수
precision = 19
scale = 2

 

2. @Enumerated

  • 자바의 enum 타입을 매핑할 때 사용
  • EnumType.String은 저장된 enum의 순서가 바뀌거나 enum이 추가되어도 안전하기 때문에 현업에서는 대부분 EnumType.String을 사용
    • EnumType.ORDINAL은 이미 저장된 enum의 순서를 변경할 수 없는 치명적인 단점 존재

 

속성 기능 기본값
value EnumType.ORDINAL: enum 순서를 DB에 저장
EnumType.STRING: enum 이름을 DB에 저장
EnumType.ORDINAL

 

3. @Temporal

  • 날짜 타입 (java.util.Date, java.util.Calendar)을 매핑할 때 사용
  • @Temporal을 생략하면 자바의 Date와 가장 유사한 timestamp로 정의됨
    • MySQL과 같이 timestamp 대신 datetime을 예약어로 사용하는 데이터베이스도 있지만 데이터베이스 방언 덕분에 애플리케이션 코드는 변경하지 않아도 됨

 

속성 기능 기본값
value TemporalType.DATE: 날짜, 데이터베이스 date 타입과 매핑
TemporalType.TIME: 시간, 데이터베이스 time 타입과 매핑
TemporalType.TIMESTAMP: 날짜오 ㅏ시간, 데이터베이스 timestamp 타입과 매핑
TemporalType은 필수로 지정해야 함

 

4. @Lob

  • 데이터베이스 BLOB, CLOB 타입과 매핑함
  • 엔티티의 필드가 매우 큰 데이터 (대개 텍스트 또는 바이너리 데이터)를 저장할 때 사용하는 어노테이션
    • 데이터를 다룰 때는 성능상 영향을 줄 수 있으므로, 데이터 로딩 시점에 대한 제어(FetchType.LAZY 같은)를 고려하여 사용하는 것이 좋음

 

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

 

5. @Transient

  • @Transient가 적용된 필드는 매핑하지 않기 때문에 데이터베이스에 저장하지 않고 조회하지도 않음
  • 객체에 임시로 어떤 값을 보관하고 싶을 때 사용

 

6. @Access

  • JPA의 @Access 어노테이션은 엔티티의 영속성 상태를 데이터베이스와 매핑할 때, JPA 공급자가 필드에 직접 접근할지(getter/setter 사용 없이) 아니면 프로퍼티 접근 방식을 사용할지 지정하는 데 사용
    • AccessType.FIELD: 필드에 직접 접근하고 필드가 private이어도 리플렉션을 통해 직접 값을 읽거나 설정하기 때문에, getter/setter 메서드를 호출하지 않음
    • AccessType.PROPERTY: 엔티티의 getter와 setter 메서드를 통해 데이터에 접근

 

  • @Access 어노테이션은 클래스 레벨과 필드 혹은 프로퍼티 레벨에 모두 적용할 수 있음
    • 클래스 레벨에 지정하면 해당 클래스 내의 모든 매핑에 대해 지정한 접근 방식이 적용됨
    • 필드 또는 프로퍼티별로 세부적인 접근 방식을 지정할 수도 있어, 클래스 전체의 기본 접근 방식과 다른 특정 필드만 구분하여 설정할 수 있음

 

  • 기본 접근 방식은 엔티티 내에서 @Id 어노테이션이 위치한 곳에 따라 결정됨
    • ex) @Id가 필드에 있으면 기본적으로 필드 접근 방식이 채택되고, getter 메서드에 있으면 프로퍼티 접근 방식이 적용됨

 

참고

자바 ORM 표준 JPA 프로그래밍 - 김영한 저

반응형