티스토리 뷰
Id를 직접 할 당할 경우 : @Id
Id를 자동 생성 할 경우 @GeneratedValue (쉬운 예로 db의 Auto로 시퀀스 값을 올려주는 기능)
IDENTITY : 데이터베이스에 위임, MYSQL (나는 잘 모르겠고 db야 너가 알아서 해주라 라는 느낌)
> 기본 키 생성을 데이터베이스에 위임
> 주로 MySql, PostgreSQL, SQL server, DB2에서 사용 (예: MySQL의 AUTO_INCREMENT)
> JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL 실행
> AUTO_INCREMENT는 데이터베이스에 INSERT SQL을 실행한 이후에 ID 값을 알 수 있음
> DENTITY 전략은 em.persist() 시점에 즉시 INSERT SQL 실행 하고 DB에서 식별자를 조회
= IDENTITY전략은 db에 도착했을 때에 id가 null일 경우에 값이 자동으로 부여된다. 즉 id값을 알 수 있는 시점은 db에 저장이 되고 나서이다. jpa의 영속성 컨텍스트를 생각해봤을 때, 하나의 요청인 한개의 로직상태가 커밋 되기 전에는 id값을 알 수가 없다.
그래서 특이하게 작동하게 된다. 기존 영속성 컨텍스트와 부관하게, @GeneratedValue를 IDENTITY로 사용한 경우
Member member = new Member();
member.setRoleType(RoleType.USER);
System.out.println("============");
entityManager.persist(member);
System.out.println("memeber.id = " + member.getId());
System.out.println("============");
ts.commit();
실행로그
============
Hibernate:
/* insert helloJpa.Member
*/ insert
into
Member
(id, age, createdDate, description, lastModifiedDate, roleType, testLocalDate, testLocalDateTime, name)
values
(null, ?, ?, ?, ?, ?, ?, ?, ?)
memeber.id = 1
============
commit 단계가 아닌 persist 단계에서 쿼리를 날려버린다.
또한 jpa 내부에서는 인서트 쿼리를 날린 후 자동으로 return을 해주기에 바로 member.id를 찾을 수 있다.
SEQUENCE : 데이터베이스 시퀀스 오브젝트 사용, ORACLE
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
ㄴ @SequenceGenerator 필요
> 데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트 (예: 오라클 시퀀스)
> 오라클 , PostgreSQL, DB2, H2 데이터베이스에서 사용
+ SEQUENCE 전략 - 매핑
@Entity
@SequenceGenerator(
name = “MEMBER_SEQ_GENERATOR",
sequenceName = “MEMBER_SEQ", //매핑할 데이터베이스 시퀀스 이름
initialValue = 1, allocationSize = 1)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
private Long id;
이렇게 전략을 주면
create sequence member_seq start with 1 increment by 50
로 만들게 된다.
-속성
ㄴ name : 식별자 생성기 이름 (기본값: 필수)
ㄴ sequenceName : 데이터베이스에 등록되어 있는 시퀀스 이름 (기본값 : hibernate_sequence)
ㄴ initialValue : DDL 생성 시에만 사용됨, 시퀀스 DDL을 생성할 때 처음 1 시작하는 수를 지정한다 (기본값 1)
ㄴ allocationSize : 시퀀스 한 번 호출에 증가하는 수 (성능 최적화에 사용됨, 데이터베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값을 반드시 1로 설정해야 한다.) (기본값 : 50)
ㄴ catalog, schema : 데이터베이스 catalog, schema 이름
시퀀스 전략은 id값을 찾을 때 조금 다른게 작동한다.
============
Hibernate:
call next value for member_seq_generator
Hibernate:
call next value for member_seq_generator
memeber.id = 1
============
Hibernate:
/* insert helloJpa.Member
*/ insert
into
Member
(age, createdDate, description, lastModifiedDate, roleType, testLocalDate, testLocalDateTime, name, id)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?)
@SequenceGenerator(name = "member_seq_generator", sequenceName = "member_seq")
public class Member {
@Id
//@GeneratedValue(strategy = GenerationType.IDENTITY)
//@GeneratedValue(strategy = GenerationType.TABLE, generator = "MEMBER_SEQ_GENERATOR")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "member_seq_generator")
private Long id;
저장이 되기 전에 먼저 시퀀스, 즉 id 값을 찾는다면 @SequenceGenerator에서 발생한 시퀀스 값이 먼저 인서트 쿼리에 추가가 되이있기 때문에 그 값을 찾아준다.
sequence 전략에는 특이점이 있다. sequence 자동 생성값이 한번에 50이 디폴트인 것이다. 요청시 한번에 50을 늘려주고 메모리에서 1,2,3... 순으로 사용하다가 50이 되는 순간 다시 50을 늘려준다. 그렇게 사용해서 비효율적인 요청이 줄어든다.
@SequenceGenerator(name = "member_seq_generator", sequenceName = "member_seq",
initialValue = 1, allocationSize = 50)//50이 기본값
public class Member {
Member member1 = new Member();
member1.setUsername("a");
Member member2 = new Member();
member2.setUsername("b");
Member member3 = new Member();
member3.setUsername("c");
System.out.println("==========");
entityManager.persist(member1); // db seq 1 | id 1
entityManager.persist(member2); // db seq 51 | id 2
entityManager.persist(member3); // db seq 51 | id 3
System.out.println("member1 = " + member1.getId());
System.out.println("member2 = " + member2.getId());
System.out.println("member3 = " + member3.getId());
System.out.println("==========");
ts.commit();
로그
==========
Hibernate:
call next value for member_seq // 첫 1번 생성
Hibernate:
call next value for member_seq // 50 생성
member1 = 1
member2 = 2 //여기서 부터 50까지는 seq를 올리는 요청이 없다.
member3 = 3
==========
Hibernate:
/* insert helloJpa.Member
*/ insert
into
Member
(age, createdDate, description, lastModifiedDate, roleType, testLocalDate, testLocalDateTime, name, id)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate:
/* insert helloJpa.Member
*/ insert
into
Member
(age, createdDate, description, lastModifiedDate, roleType, testLocalDate, testLocalDateTime, name, id)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate:
/* insert helloJpa.Member
*/ insert
into
Member
(age, createdDate, description, lastModifiedDate, roleType, testLocalDate, testLocalDateTime, name, id)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?)
allocationSize는 TableGenerator 전략도 동일하다.
TABLE : 키 생성용 테이블 사용, 모든 DB에서 사용
ㄴ @TableGenerator 필요
> 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략
> 장점 : 모든 데이터베이스에 적용가능
> 단점 : 성능
@Entity
@TableGenerator(name = "MEMBER_SEQ_GENERATOR", table = "MY_SEQUENCES",
pkColumnValue = "MEMBER_SEQ" , allocationSize = 1)
//@SequenceGenerator(name = "member_seq_generator", sequenceName = "member_seq")
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "MEMBER_SEQ_GENERATOR")
//@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "member_seq_generator")
private Long id;
다른 시퀀스 전략을 따라한다는 것은 로그를 보면 알 수 있다.
Hibernate:
create table Member (
id bigint not null,
age integer,
createdDate timestamp,
description clob,
lastModifiedDate timestamp,
roleType varchar(255),
testLocalDate date,
testLocalDateTime timestamp,
name varchar(255),
primary key (id)
)
Hibernate:
create table MY_SEQUENCES (
sequence_name varchar(255) not null,
next_val bigint,
primary key (sequence_name)
)
Hibernate:
insert into MY_SEQUENCES(sequence_name, next_val) values ('MEMBER_SEQ',0)
Hibernate:
select
tbl.next_val
from
MY_SEQUENCES tbl
where
tbl.sequence_name=? for update
Hibernate:
update
MY_SEQUENCES
set
next_val=?
where
next_val=?
and sequence_name=?
Hibernate:
/* insert helloJpa.Member
*/ insert
into
Member
(age, createdDate, description, lastModifiedDate, roleType, testLocalDate, testLocalDateTime, name, id)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?)
시퀀스 테이블을 만들 뒤에 그 값을 넣고 올리고 있다.
* 운영에서는 부담되는 전략이다. db가 제공하는 걸 사용하는 것이 좋음
>속성
name
|
식별자 생성기 이름
|
필수
|
table
|
키생성 테이블명
|
hibernate_sequences
|
pkColumnName
|
시퀀스 컬럼명
|
sequence_name
|
valueColumnNa
|
시퀀스 값 컬럼명
|
next_val
|
me pkColumnValue
|
키로 사용할 값 이름
|
엔티티 이름
|
initialValue
|
초기 값, 마지막으로 생성된 값이 기준이다.
|
0
|
allocationSize
|
시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨)
|
50
|
catalog, schema
|
데이터베이스 catalog, schema 이름
|
|
uniqueConstraint s(DDL)
|
유니크 제약 조건을 지정할 수 있다.
|
- AUTO : 방언에 따라 자동 지정, 기본값
권장하는 식별자 전략자
- 기본 키 제약 조건 : null 아님, 유일, 변하면 안된다.
- 미래까지 이 조건을 만족하는 자연키(전화번호, 주민번호 등)는 찾기 어렵다. 대리키(대체키)(랜덤값)를 사용하자.
- 예를 들어 주민등록번호도 기본키로 적절하지 않다.
- 권장 : Long형 + 대체키 + 키 생정전략 사용
'dev > JPA 기본' 카테고리의 다른 글
다중성 - 다대일과 일대다, (1) | 2024.02.02 |
---|---|
양방향 매핑시 가장 많이 하는 실수 (0) | 2024.02.01 |
양방향 연관관계와 연관관계의 주인 (1) | 2024.02.01 |
단방향 연관관계 (1) | 2024.02.01 |
필드와 컬럼 매핑 및 속성들 (1) | 2024.01.31 |
- Total
- Today
- Yesterday
- 로그인
- 향해플러스
- hypertexttransferprotocol
- HTTP
- SpringBoot
- 스프링부트
- reject
- rejectValue
- 항해99
- 백엔드 개발자 역량
- Java
- thymleaf
- Intercepter
- 항해플러스
- exception
- ArgumentResolver
- jpa api
- 백엔드 개발자 공부
- JPA
- 리터럴
- react실행
- 향해99
- 인터셉터
- React
- BindingResult
- 스프링공부
- 예외처리
- 컨트
- filter
- 향해플러스백엔드
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |