본문 바로가기
spring

[JPA] 영속성 관리, 엔티티 매핑

by 개발자 쿠키 2024. 8. 30.

영속성 컨텍스트

영속화된 모든 엔티티들을 추적, 관리하는 공간으로 JPA의 핵심 기능 중 하나이며, 본질적으로 캐싱역할을 수행하는 공간 → Git 맥락에서는 커밋을 수행하기 전에 작업 내역이 위치한 스테이징 영역

앱과 DB사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할을 한다. 영속성 컨텍스트 덕분에 1차 캐시, 동일성 보장, 트랜잭션을 지원하는 쓰기 지연, 변경 감지, 지연 로딩 기능을 사용할 수 있다.

영속성 컨텍스트에 저장한 엔티티는 flush 시점에 DB에 반영되는데 일반적으로 transaction을 commit할 때 영속성 컨텍스트가 flush된다. 영속성 컨텍스트가 관리하는 엔티티를 영속 상태의 엔티티라 한다.

준영속 상태의 엔티티

더는 영속성 컨텍스트의 관리를 받지 못하므로 영속성 컨텍스트가 제공하는 1차 캐시, 동일성 보장, 트랜잭션을 지원하는 쓰기 지연, 변경 감지, 지연 로딩 같은 기능들을 사용할 수 없다.

 

영속성 컨텍스트 생성

  • 엔티티 매니저 인스턴스가 생성되면 영속성 컨텍스트가 자동으로 생성됨
    • 엔티티 매니저와 영속성 컨텍스트는 1:1 매칭, 각 매니저는 개별 영속성 컨텍스트를 가지고 있음
  • 스프링에서는 엔티티 매니저를 직접 생성하지 않고, 컨테이너를 통해 제공되기 때문에 Factory, Manager 자체가 자동으로 관리됨

EntityManager

EntityManager Factory에서 생성한다. 자바를 직접 다루는 J2SE환경에서는 엔티티 매니저를 만들면 그 내부에 영속성 컨텍스트도 함께 만들어진다. 이 영속성 컨텍스트는 엔티티 매니저를 통해서 접근할 수 있다.

 

JPA를 사용하는데 가장 중요한 일은 엔티티와 테이블을 정확히 매핑하는 것

 

JPA는 크게 4가지로 분류 가능

  • 객체 테이블 매핑 : @Entity, @Table
  • 기본 키 매핑 : @Id
  • 필드와 컬럼 매핑 : @Column
  • 연관관계 매핑 : @ManyToOne, @JoinColumn

 

@Entity

JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 어노테이션을 필수로 붙여야 한다. @Entity가 붙은 클래스는 JPA가 관리하는 것으로, 엔티티라 부른다.

 

주의사항

  • 기본 생성자는 필수다
  • final, enum, interface, inner 클래스에는 사용할 수 없다.
  • 저장할 필드에 final을 사용하면 안된다.

JPA가 엔티티 객체를 생성할 때 기본 생성자를 사용하므로 이 생성자는 반드시 있어야한다.

 

@Table

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

@Lob

CLOB, BLOB 타입을 매핑할 수 있다.

hibernate.hbm2ddl.auto 속성

create : 기존 테이블을 삭제하고 새로 생성 (drop + create)

create-drop : create 속성에 추가로 어플리케이션을 종료할 때 생성한 DDL을 제거 (drop + create + drop)

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

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

none : 자동 생성 기능을 사용하지 않으려면 hibernate.hbm2ddl.auto 속성 자체를 삭제하거나 유효하지 않은 옵션 값을 주면 된다.

 

※주의사항※

운영 서버에서 create, create-drop, update 처럼 ddl을 수정하는 옵션은 절대 사용하면 안된다. 오직 개발 서버나 개발 단계에서만 사용해야 한다.


추천전략

개발 초기 단계 : create, update

초기화 상태로 자동화된 테스트를 진행하는 개발자 환경과 CI 서버는 create 또는 create-drop

테스트 서버는 update or validate

스테이징과 운영 서버는 validate, none


데이터베이스 기본 키 생성 전략

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

자동 생성 전략이 이렇게 다양한 이유는 데이터베이스 벤더마다 지원하는 방식이 다르기 때문이다. 예를 드렁 오라클 데이터베이스는 시퀀스를 제공하지만 MYSQL은 시퀀스를 제공하지 않는다. 대신에 MYSQL은 기본 키 값을 자동으로 채워주는 AUTO_INCREMENT 기능을 제공한다. 따라서 SEQUENCE나 IDENTITY 전략은 사용하는 데이터베이스에 의존한다. TABLE 전략은 키 생성용 테이블을 하나 만들어두고 마치 시퀀스처럼 사용하는 방법이다. 이 전략은 테이블을 활용하므로 모든 데이터베이스에서 사용할 수 있다.

기본 키 직접 할당 : @Id

자동 생성 전략 : @Id @GeneratedValue

키 생성 전략 사용하려면 persistence.xml에 hibernate.id_new_generator_mapping=true 속성을 반드시 추가해야한다.


@Id 적용 가능 자바 타입

  • 자바 기본형
  • 자바 래퍼형
  • String
  • java.util.Date
  • java.sql.Date
  • java.math.BigDecimal
  • java.math.bBingInteger


IDENTITY 전략

IDENTITY는 기본 키 생성을 데이터베이스에 위임하는 전략이다. 주로 MYSQL, PostgreSQL, SQL Server, DB2에서 사용한다.


SEQUENCE 전략

@Entity
@SequencGenerator (
		name = "BOARD_SEQ_GENERATOR",
		sequenceName = "BOARD_SEQ",
		initialValue = 1, allocationSize = 1)

public class Board {	
		@Id
		@GeneratedValue(Strategy = GenerationType.SEQUENCE,
										generator = "BOARD_SEQ_GENERATOR")
		private Long id;
}

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

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


@SequenceGenerator

속성 기능 기본값

name 식별자 생성기 이름 필수
sequenceName 데이터베이스에 등록되어 있는 시퀀스 이름 hibernate_sequence
initalValue DDL 생성 시에만 사용됨. 시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정한다. 1
allocationSize 시퀀스 한번 호출에 증가하는 수(성능 최적화에 사용됨) 50
catalog, schema 데이터베이스 catalog, schema 이름  

데이터베이스 시퀀스 값이 하나씩 증가 하도록 설정되어 있으면 이 값을 반드시 1로 설정해야한다.


TABLE 전략

@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

속성 기능 기본값

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


AUTO 전략

GenerationType.AUTO는 선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다. 예를 들어 오라클을 선택하면 SEQUENCE를, MYSQL을 선택하면 IDENTITY를 사용한다.

@Entity
public class Board {
		@Id
		@GeneratedValue(Strategy = GenerationType.AUTO)
		private Long id;
}

AUTO 전략의 장점은 데이터베이스를 변경해도 코드를 수정할 필요가 없다는 것이다. 특히, 키 생성 전략이 아직 확정되지 않은 개발 초기 단계나 프로토타입 개발 시 편리하게 사용할 수 있다.

AUTO를 사용할 때 SEQUENCE나 TABLE 전략이 선택되면 시퀀스나 키 생성용 테이블을 미리 만들어 두어야한다. 만약 스키마 자동 생성 기능을 사용한다면 하이버네이트가 기본값을 사용해서 적절한 시퀀스나 키 생성용 테이블을 만들어줄 것이다.


기본 키 매핑 정리

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

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

 

참고 자료

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