[Java] JPA - 엔티티 매핑

daheenamic·4일 전
0

JAVA

목록 보기
34/34

JPA 엔티티 매핑

JPA에서 중요한 두 축

JPA를 이해하려면 두 가지를 꼭 잡아야 한다.
1. 매커니즘 측면: 영속성 컨텍스트와 JPA 내부 동작 방식
2. 설계 측면: 객체와 관계형 데이터베이스를 어떻게 매핑할 것인가


엔티티 매핑

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

@Entity - 클래스와 테이블 매핑

@Entity가 붙은 클래스는 JPA가 관리하는 엔티티(Entity)가 된다.
JPA로 테이블과 매핑하려면 이 어노테이션이 필수이다.

규칙

  • 기본 생성자(파라미터 없는 public/protected) 필수
  • final 클래스, enum, interface, inner class 사용 불가
  • DB 저장용 필드에는 final 사용 불가
@Entity
public class Member { ... }

속성

  • name: JPA에서 사용할 엔티티 이름 (기본값 = 클래스명)
    특별한 이유 없으면 기본값을 그대로 사용하는 게좋다.

@Table - 테이블 이름 지정

@Entity
@Table(name = "MBR")
public class Member { ... }
  • name: 매핑할 테이블 이름 지정
  • catalog, schema: 데이터베이스 catalog/schema 매핑
  • uniqueConstraints: DDL 생성 시 유니크 제약조건 지정

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

JPA는 애플리케이션 실행 시점에 DDL을 자동 생성할 수 있다.

  • 테이블 중심 설계에서 객체 중심 설계로 전환이 가능
  • 데이터베이스 방언(dialect)에 따라 SQL이 달라짐
    예) Oracle - VARCHAR2 / MySQL - VARCHAR

💡주의: 이 기능은 개발환경에서만 사용!
운영DB에서는 직접 DDL을 관리해야 한다.

hibernate.hbm2ddl.auto 옵션

  • create: 기존 테이블 삭제 후 다시 생성 (DROP + CREATE)
  • create-drop: 애플리케이션 종료 시 DROP
  • update: 변경된 부분만 반영 (컬럼 추가만 가능)
  • validate: 매핑 검증만 수행, 불일치 시 오류
  • none: 아무것도 하지 않음 (관례상 none이라 쓰지만 실제 옵션은 없음)

⚠️ 환경별 추천 설정

  • 개발 초기: create, update
  • 테스트 서버: update, validate
  • 운영/스테이징: validate, none

DDL 생성 기능

DDL 생성 시 다음과 같은 제약 조건을 추가할 수 있다.

@Column(nullable = false, length = 10)
@Table(uniqueConstraints = {
    @UniqueConstraint(name = "NAME_AGE_UNIQUE", columnNames = {"NAME", "AGE"})
})

DDL 생성 옵션은 “테이블 생성 시점”에만 영향을 주며,
애플리케이션 실행 중에는 아무 영향이 없다.


필드와 컬럼 매핑 예시

다음과 같은 요구사항이 있다고 가정하자.
1. 회원은 일반회원/관리자로 구분
2. 회원가입일과 수정일 기록
3. 회원 설명(description)은 길이 제한 없음

@Entity
public class Member {
    @Id
    private Long id;

    @Column(name = "name")
    private String username;

    private Integer age;

    @Enumerated(EnumType.STRING)
    private RoleType roleType;

    @Temporal(TemporalType.TIMESTAMP)
    private Date createdDate;

    @Temporal(TemporalType.TIMESTAMP)
    private Date lastModifiedDate;

    @Lob
    private String description;
}
public enum RoleType {
    USER, ADMIN
}

어노테이션 설명

  • @Column: 컬럼 매핑
  • @Temporal: 날짜 타입 매핑
  • @Enumerated: enum 타입 매핑
  • @Lob: BLOB/CLOB 매핑
  • @Transient: DB 매핑 제외 (임시 필드)

@Column - 속성

속성설명기본값
name컬럼 이름필드명
insertable/updatable등록/수정 가능 여부TRUE
nullableNULL 허용 여부 (DDL 시 not null 생성)TRUE
unique단일 컬럼에 유니크 제약 (잘 안씀, 이름 자동 생성됨)FALSE
columnDefinition컬럼 정의 직접 지정방언에 따라 다름
length문자열 길이 제한 (String만 적용)255
precision / scaleBigDecimal용 숫자 정밀도19 / 2

@Enumerated — Enum 매핑

자바의 enum 타입을 DB에 저장할 때 사용

옵션설명
EnumType.ORDINAL순서(숫자) 저장 → ❌ 사용 금지
EnumType.STRINGenum 이름 문자열 저장 → ✅ 추천

EnumType.ORDINAL 사용을 지양(금지)하는 이유는 ORDINAL은 순서가 바뀌면 데이터가 꼬인다.
예를들어 USER(0) → ADMIN(1) 이었는데, GUEST 추가 시 USER가 1로 밀림.
항상 EnumType.STRING 사용하기.

@Temporal — 날짜 타입 매핑

과거에는 Date, Calendar를 매핑할 때 사용했지만, 지금은 LocalDate / LocalDateTime을 그대로 쓰면 된다. 최신 Hibernate는 자동 매핑을 지원한다.

@Lob - 대용량 데이터 매핑

  • 문자 타입 → CLOB (Character Large Object)
  • 비문자 타입 → BLOB (Binary Large Object)
@Lob
private String description; // CLOB

속성 지정 불가, 타입에 따라 자동 결정

@Transient — 매핑 제외 필드

DB에는 저장되지 않고 메모리 내 임시 데이터 저장용 어노테이션이다.

@Transient
private int tempCount;

기본 키 매핑

  1. 직접 할당: @Id
  2. 자동 생성: @GeneratedValue
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

전략 종류

전략설명주요 DB
IDENTITYDB에 키 생성을 위임 (AUTO_INCREMENT)MySQL 등
SEQUENCEDB 시퀀스 객체 사용Oracle, PostgreSQL 등
TABLE별도 테이블 사용 (모든 DB 가능)모든 DB
AUTO방언에 따라 자동 선택-

IDENTITY 전략

  • DB가 자동으로 PK 생성 (예: MySQL AUTO_INCREMENT)
  • em.persist() 시점에 즉시 INSERT SQL 실행
    → 왜냐면, ID를 알아야 1차 캐시에 등록 가능하니까
  • 보통 JPA는 커밋 시점에 쿼리를 보내지만, 이 경우에는 예외적으로 즉시 INSERT 후 식별자 조회.

즉, 영속성 컨텍스트가 ID를 관리하기 위해 DB에 먼저 쿼리를 날림.


SEQUENCE 전략

Oracle, PostgreSQL, H2 등 시퀀스 객체를 사용하는 전략.

@Entity
@SequenceGenerator(
  name = "MEMBER_SEQ_GENERATOR",
  sequenceName = "MEMBER_SEQ",
  initialValue = 1, allocationSize = 1
)
public class Member {
  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQ_GENERATOR")
  private Long id;
}
  • sequenceNam: 실제 DB 시퀀스
  • allocationSize: 한 번에 미리 땡겨오는 개수 (기본값 50)

💡 성능 최적화를 위해 DB에서 시퀀스 50개를 미리 가져와 메모리에서 사용.
예: 첫 persist() 호출 시 1~50까지 캐싱 → 다음엔 51~100.


TABLE 전략

키 생성용 전용 테이블을 만들어 시퀀스를 흉내내는 방식.
모든 DB에서 동작하지만 성능이 떨어져서 실무에서는 잘 안 씀.


식별자 전략 권장사항

  • 기본키 제약조건: null 아님, 유일, 변하지 않아야 함
  • 자연키(주민번호 등)는 위험 → 대체키(비즈니스와 무관한 값) 사용
  • 권장 조합: Long 타입 + 대체키(UUID 등) + 자동 생성 전략

IDENTITY vs SEQUENCE 동작 차이 요약

구분동작 시점특징
IDENTITYem.persist() 시점에 DB INSERT즉시 쿼리 실행
SEQUENCE커밋 시점 INSERTcall next value for ... 로 ID 선획득 가능

💬 allocationSize=50 : 시퀀스를 한 번에 여러 개 가져와 메모리에서 소비 → 성능 향상
여러 서버 환경에서도 동시성 문제 없음.

[ SEQUENCE + allocationSize = 50 예시 ]

DB 시퀀스: MEMBER_SEQ
초기 상태: 0

1️⃣ JPA가 처음 persist() 호출 → 시퀀스 50개 미리 조회 (1~50)
2️⃣ 메모리에 1~50 캐싱
3️⃣ persist() 여러 번 호출해도 DB 시퀀스는 재조회 안함
4️⃣ 51번째 persist() 시점 → 다음 50개(51~100) 미리 조회

결과:
DB call 횟수 ↓
동시성 문제 없이 여러 서버에서도 ID 충돌 없이 동작

정리 요약

주제핵심 내용
@Entity, @Table클래스 ↔ 테이블 매핑
스키마 자동 생성개발용으로만 사용
@Column 등 필드 매핑컬럼 속성, 제약조건 지정 가능
@Enumerated반드시 STRING 사용
@Lob, @Transient대용량 / 임시 데이터 매핑
식별자 전략IDENTITY, SEQUENCE, TABLE, AUTO
권장 방식Long형 + 대체키 + Generation 전략

0개의 댓글