JPA를 이해하려면 두 가지를 꼭 잡아야 한다.
1. 매커니즘 측면: 영속성 컨텍스트와 JPA 내부 동작 방식
2. 설계 측면: 객체와 관계형 데이터베이스를 어떻게 매핑할 것인가
@Entity가 붙은 클래스는 JPA가 관리하는 엔티티(Entity)가 된다.
JPA로 테이블과 매핑하려면 이 어노테이션이 필수이다.
@Entity
public class Member { ... }
@Entity
@Table(name = "MBR")
public class Member { ... }
JPA는 애플리케이션 실행 시점에 DDL을 자동 생성할 수 있다.
💡주의: 이 기능은 개발환경에서만 사용!
운영DB에서는 직접 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
}
속성 | 설명 | 기본값 |
---|---|---|
name | 컬럼 이름 | 필드명 |
insertable/updatable | 등록/수정 가능 여부 | TRUE |
nullable | NULL 허용 여부 (DDL 시 not null 생성) | TRUE |
unique | 단일 컬럼에 유니크 제약 (잘 안씀, 이름 자동 생성됨) | FALSE |
columnDefinition | 컬럼 정의 직접 지정 | 방언에 따라 다름 |
length | 문자열 길이 제한 (String만 적용) | 255 |
precision / scale | BigDecimal용 숫자 정밀도 | 19 / 2 |
자바의 enum 타입을 DB에 저장할 때 사용
옵션 | 설명 |
---|---|
EnumType.ORDINAL | 순서(숫자) 저장 → ❌ 사용 금지 |
EnumType.STRING | enum 이름 문자열 저장 → ✅ 추천 |
EnumType.ORDINAL
사용을 지양(금지)하는 이유는 ORDINAL은 순서가 바뀌면 데이터가 꼬인다.
예를들어 USER(0) → ADMIN(1) 이었는데, GUEST 추가 시 USER가 1로 밀림.
항상 EnumType.STRING 사용하기.
과거에는 Date
, Calendar
를 매핑할 때 사용했지만, 지금은 LocalDate / LocalDateTime을 그대로 쓰면 된다. 최신 Hibernate는 자동 매핑을 지원한다.
@Lob
private String description; // CLOB
속성 지정 불가, 타입에 따라 자동 결정
DB에는 저장되지 않고 메모리 내 임시 데이터 저장용 어노테이션이다.
@Transient
private int tempCount;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
전략 | 설명 | 주요 DB |
---|---|---|
IDENTITY | DB에 키 생성을 위임 (AUTO_INCREMENT) | MySQL 등 |
SEQUENCE | DB 시퀀스 객체 사용 | Oracle, PostgreSQL 등 |
TABLE | 별도 테이블 사용 (모든 DB 가능) | 모든 DB |
AUTO | 방언에 따라 자동 선택 | - |
즉, 영속성 컨텍스트가 ID를 관리하기 위해 DB에 먼저 쿼리를 날림.
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.
키 생성용 전용 테이블을 만들어 시퀀스를 흉내내는 방식.
모든 DB에서 동작하지만 성능이 떨어져서 실무에서는 잘 안 씀.
구분 | 동작 시점 | 특징 |
---|---|---|
IDENTITY | em.persist() 시점에 DB INSERT | 즉시 쿼리 실행 |
SEQUENCE | 커밋 시점 INSERT | call 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 전략 |