1. `@Id`: 기본키 필드 표시
JPA에서 `@Id`는 엔티티의 기본 키를 지정하는 키워드이다.
테이블과 엔티티를 매핑 할때 식별자로 사용할 필드 위에 `@Id`어노테이션을 붙여 테이블의 Primary Key와 연결 시켜줘야한다.
컬럼 명을 지정하지 않으면 camelCase로 작성된 필드명을 snake_case로 바뀐 테이블 컬럼을 찾아서 매핑시켜준다.
ex) memberId -> member_id , orderItemId -> order_item_id
`@Column` 어노테이션을 활용하여 테이블의 pk 컬럼을 따로 지정할 수도 있다.
@Entity
public class Member {
@Id
@Column(name = "member_id") // 컬럼명 따로 지정
private Long id;
}
이렇게 `@Id`로 식별자필드와 테이블의 PK를 매핑만 시켜놓으면
식별자로 사용될 값을 일일히 수동으로 넣어줘야 하는 불편함이 있는데, `@GeneratedValue` 를 사용하면 이를 해결할 수 있다.
`@GeneratedValue` 어노테이션을 사용하면 식별자 값을 자동 생성 시켜줄 수 있다.
(그냥 일련번호같은 숫자를 자동으로 증가시켜줄수있다는 뜻이다. MySql의 AUTO_INCREMENT, Oracle의 시퀀스와 같은 개념이다. 당연함 저 데이터베이스를 활용할때 JPA가 기본키 생성을 하는거라 개념이 같을수밖에없음!)
`@GeneratedValue`에는 3가지 전략이 있고, JPA에게 전략 선택을 위임하는 옵션인 AUTO 옵션을 포함해서
총 4가지 옵션이 존재한다.
2. 식별자 전략 (@Id, @GeneratedValue)
`@GeneratedValue`는 기본 키를 어떻게 자동 생성할지 설정하는 어노테이션이다.
아래 표는 4가지 옵션에 관한 간략한 설명이다.
| 전략 | 설명 | DB 종속성 | 특징 | DB |
| AUTO | JPA가 알아서 결정 (기본값) | O | DB에 따라 IDENTITY나 SEQUENCE 등 자동 선택 *주의 :하이버네이트를 무조건 믿어선 안된다! |
자동선택 |
| IDENTITY | DB가 자동 생성 (MySQL의 AUTO_INCREMENT 같은 것) |
O | insert할 때 ID 없음 → DB가 넣어줌 | MySQL에서 가장 흔함 |
| SEQUENCE | DB 시퀀스 오브젝트 사용 (Oracle, PostgreSQL 등) |
O | 시퀀스에서 값을 미리 가져와서 insert | PostgreSQL, Oracle에서 사용 |
| TABLE | 별도 키 생성용 테이블에서 ID 관리 | X | DB 독립적이지만 성능 떨어짐 | 모든 DB 가능! |
@GeneratedValue : ID( 기본키 ) 자동 생성
식별라고 일컫는게 일련번호같은 것을 만들어주는것같다. 순자를 순차적으로 늘리는것인데
JPA가 값을 자동생성하게 만들겠다는 뜻이다. `strategy = ...`는 어떤방식으로 생성할지 지정해주는것 (위의 4가지 방식중 하나)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
2-1. IDENTITY 키워드 (MySQL에서 사용)
"DB가 자동으로 ID 값을 만들어줘!"
사전 프로젝트 게시판 구현할때 MySql을 사용했기때문에 게시판의 번호를 자동증가하기위해 `IDENTITY`를 사용해보았다.
- `INSERT INTO member (name) VALUES ('홍길동')`
- → DB가 `id` 값을 자동으로 채워줌 (MySQL에서 흔히 쓰는`AUTO_INCREMENT`)
- JPA는 이 값을 insert 이후에 DB로부터 받아옴
- 단점: `em.persist()` 즉시 insert됨 → 배치 INSERT 최적화 어려움
-- DB에 이런 테이블이 있다면
CREATE TABLE member (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255)
);
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
2-1-1. IDENTITY 동작 흐름 방식
Member member = new Member("홍길동");
em.persist(member);
내부 동작
- `em.persist(member)` 호출하면,
- JPA는 즉시 INSERT SQL을 실행함
- (디비에 진짜 찐찐 들어간다는뜻 영속성이아니라)
- 사유 : DB가 ID를 만들어줘야 하기 때문!!!!
- 그래서 Hibernate는 INSERT를 바로 실행해야만 member.getId() 값을 알 수 있음
- DB가 ID 값을 생성해줌 (예: 1, 2, 3...)
- 그 ID 값이 member.id에 자동으로 채워짐
특징
- ID가 생성되기 전에는 insert할 수 없음
- 그래서 persist() = 곧바로 insert 실행됨
- 배치 INSERT 최적화 불가
-- 바로 실행됨
INSERT INTO member (username) VALUES ('홍길동');
-- 그리고 DB가 자동으로 id=1 부여
📍 배치 최적화란~?
배치(Batch) 란?
한 번에 여러 건의 작업을 묶어서 처리하는 것
예를 들어, 회원 100명을 한 명씩 처리하는 대신
→ 10명씩 묶어서 처리하는것이다. 훨~씬 빠름
배치 INSERT 예시
for (int i = 0; i < 1000; i++) {
em.persist(new Member("user" + i));
if (i % 50 == 0) {
em.flush(); // DB에 반영
em.clear(); // 메모리 비우기
}
}
→ 이렇게 하면 1000명을 한 번에 다 처리하지 않고, 50명씩 나눠서 처리할 수 있다.
배치처리는 회사에서도 정말많이 사용해서 jpa에서도 최적화된 배치처리를 알면 좋을것이라고 생각했다.
배치 insert를 하려면 ID를 미리 알아야 한다.
즉, insert 전에 ID가 있어야 → 쿼리를 모아서 한 번에 날릴 수 있다.
전략에 따른 차이
| 전략배치 | insert 가능여부 | 이유 |
| IDENTITY | 불가능 | DB가 insert 후에 ID를 생성 → 한 건씩 insert해야 함 |
| SEQUENCE + allocationSize ≥ 1 | 가능 | 미리 ID 확보 가능 → 쿼리를 모아서 한 번에 insert 가능 |
| 항목 | 설명 |
| 배치 최적화란? | 여러 insert 쿼리를 모아서 한 번에 처리하는 것 |
| 왜 중요? | 성능 향상, DB round trip 감소 |
| 가능한 조건 | ID를 미리 확보할 수 있어야 함 (SEQUENCE + allocationSize 필요) |
| 불가능한 경우 | IDENTITY 전략 (ID를 DB가 생성하므로 insert 시점에 몰라서 배치 불가) |
2-2. SEQUENCE 키워드 (PostgreSQL, Oracle에서 사용)
"미리 시퀀스에서 ID 번호 받아올게~~"
- JPA가 먼저 `SELECT nextval('member_seq')`로 ID 값을 미리 가져옴
- 그 다음 `INSERT` 실행
- ID 값을 insert 전에 미리 안다는 게 큰 차이점
- 배치 시 insert 최적화
완전 걍 오라클문법인듯? 나는 회사에서 오라클을 사용하기때문에 시퀀스를 잘 사용하는데
먼저 값을 가져와서 필요한 곳에 넣어준다
jpa에서도 마찬가지로 db에서 시쿼스를 미리 생성해줘야 사용할 수있다.
CREATE SEQUENCE member_seq START WITH 1 INCREMENT BY 1;
@SequenceGenerator(
name = "member_seq_gen",
sequenceName = "member_seq", // DB에 생성한 시퀀스 이름
allocationSize = 50 // 기본값은 50 ( DB 시퀀스를 몇 개씩 미리 할당받을지 정하는 옵션)
)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "member_seq_gen")
private Long id;
2-2-1. SEQUENCE 동작 흐름 방식
- em.persist(member); 이 시점엔 쿼리 아직 안 날아감
- JPA는 "영속 상태"만 등록하고
- flush() 또는 트랜잭션 commit 시점까지 대기했다가 INSERT 실행됨
Member member = new Member("홍길동");
em.persist(member);
내부 동작
- em.persist(member) 호출 시,
- 먼저 시퀀스에서 ID를 미리 가져옴
SELECT nextval('member_seq');
3. 그 ID를 엔티티에 할당 (member.id = 100)
4. 이후에 INSERT 실행
INSERT INTO member (id, username) VALUES (100, '홍길동');
특징
- ID를 먼저 확보하므로,
- JPA는 쓰기 지연(쓰기 모아서 처리) 가능 → 배치 insert에 유리
- 성능 최적화에 더 적합
📍 allocationSize란?
가정
- allocationSize = 50
- 현재 DB 시퀀스 값: 1
동작:
- JPA가 시퀀스를 한번 조회해서 1~50번 ID 범위를 확보함
- 그 다음 insert는 DB 시퀀스를 다시 조회하지 않고,
메모리에서 2, 3, 4, ..., 50까지 ID를 직접 할당 - 50개를 다 쓰고 나면 다시 시퀀스 조회 → nextval = 51 → 51~100 확보
미리 범위 확보하는 이유
🔸 이유: 성능 최적화 => 이래서 배치할때 좋은것임!
- 매번 DB에 SELECT nextval() 하려면 느리니까
- 한번에 여러 개 미리 받아와서 메모리에 캐싱하는 방식
주의할 점
| 항목 | 설명 |
| ID가 건너뛸 수 있음 | 서버 재시작 등으로 미리 받은 ID를 안 쓰고 버릴 수도 있음 (예: 1~50 받아놨는데 3번까지만 쓰고 종료되면, 4~50은 날아감) |
| 성능은 확실히 좋아짐 | 배치 insert 등에서 매우 유리 |
| 필요 시 allocationSize=1로 조정 | ID 정밀하게 관리하고 싶으면 (대신 성능은 떨어짐) |
'공부일기.. > JPA' 카테고리의 다른 글
| [JPA] Spring Data JPA 페이징 (3/3) - 성능 최적화와 테스트 (0) | 2025.12.13 |
|---|---|
| [JPA] Spring Data JPA 페이징(2/3) - 실무 패턴과 동적 쿼리 (0) | 2025.12.11 |
| [JPA] Spring Data JPA 페이징 (1/3) - 기본 개념과 핵심 객체 (0) | 2025.12.08 |
| [jpa] BaseEntity와 JPA Auditing - 동작 원리 , 중복필드 제거 (예시코드) (0) | 2025.10.18 |
| [JPA] JPA의 필수개념_1 (JPA정의, Projection) (0) | 2025.06.26 |