나다.
이 글이 뭐하는 글이냐면
오랜만에 글을 쓴다.
"요세 무슨 일이 이렇게도 많은지" 라는 핑계로 잠깐 놀다가 왔다.
이번 글에서는 영속성 컨텍스트에서 특정 컨텐츠를 다루기 위해서
필수적으로 있어야만 했던 Id(식별자)와 그 값의 사용 전략들에 대해 알아볼거다.
아이디를 부여하는 방법
모든 일에는 순서가 있다고 했던가.
먼저 아이디를 사용하고 싶다면 아이디를 만들어야 사용할 수 있을 것이다.
우리는 간혹 테스트코드를 만들거나 간단히 어떤 요소를 테스트하고 싶을때 임시적으로
Long id = 1L;
item.setId(++id);
와 같이 사용하고는 한다.
물론 아이디를 부여함에는 변함이 없으며 자동으로 1씩 증가하기 때문에 모든 요소에
중복되지 않는 고유적인 아이디를 부여함은 변하지 않는다.
하지만 위의 방법은 그저 테스트를 위한 하나의 일회성 수단 밖에 되지않는다.
예를 들어,
"김 실장, 우리 서버에 지금 버그가 존X게 많아.
문 닫고 점검 후에 리부트. 진행시켜."
라는 주문에 서버를 한번 내렸다 올렸다고 가정하자.
우리가 위에 선언했던 id라는 변수는 다시 1L로 초기화되며
입력되는 값은 다시 1부터 순차적으로 증가하게 된다.
그렇다면 이런 상황이 발생했을 때 먼저 입력되어 데이터베이스에 존재하던 1번 데이터는?
차후 입력 될 리부트 후의 1번 데이터는?
결국 변수가 다시 초기화되는 시점에서 충돌이 일어날 수 밖에 없는 구조가 된다.
이런식으로 쓰면 X된다는 뜻이다.
그래서 우리 어케함?
방법은 간단하다.
나의 지능을 믿지말고 기술을 믿고 배우면 된다.
미개한 우리의 편의를 위해 개발을 위한 개발을 하신 우리의 석박들께서
각종 어노테이션과 편의기능을 통해 우리를 구제하셨다.
??? : ㅋㅋ 근데 어노테이션 몇 개 붙히면 끝나는거 그렇게 대단한 기술임?
이 어노테이션 몇 개 붙혀 끝내기 위해 몇 명의 고지능자들이 카페인에 절여지며
고민했을지 생각하고 감사한 마음으로 쓰기바란다.
먼저 영속성 컨텍스트에서 각 개체(엔티티)를 관리하기 위해 Id를 부여할 때는
크게 두가지 어노테이션을 필요로한다
첫 번째는 @Id이며
두 번째는 @GeneratedValue 가 되겠다.
먼저 JPA는 각 엔티티 클래스를 통해 각 테이블의 구조를 투영한다.
즉, 엔티티 클래스를 제작함과 동시에 테이블의 구조를 객체에서 명시함으로써
DB는 명시된 설계에 따라 테이블을 구성하고 작동한다.
여기서 먼저 @Id를 통해 각 테이블의 PK값을 지정할 수 있다.
@Entity
@Getter @Setter
public class Member {
@Id
private Long id;
private String name;
}
위의 엔티티 클래스의 예를 들어보자.
먼저 Long id에 @Id라는 어노테이션을 붙혔다.
그렇다는 것은 이 객체를 통해 생성될 테이블 MEMBER에서
PK의 역할은 id라는 컬럼이 담당하게 되는 것이다.
Id로 사용할 필드를 지정하는 것은 존X 쉽다.
그렇다면 이 Id를 어떤 식으로 부여하고 사용할 것인가에 대한 고찰이 가장 중요하다.
@GeneratedValue와 생성전략
여러분은 자동화를 좋아하는가?
난 존X 좋아함.
이 @GeneratedValue를 사용하면 상단의 예제처럼
Id값을 부여하기 위해 Long을 1L로 초기화하는 등의 Don't go show를 할 필요가 없어진다.
다음의 어노테이션은 총 4가지의 전략을 사용 할 수 있는데 전략은 다음과 같다.
@GeneratedValue(strategy = GenerationType.IDENTITY)
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@GeneratedValue(strategy = GenerationType.TABLE)
@GeneratedValue(strategy = GenerationType.AUTO) // default
다음 4개의 전략을 한 개씩 알아보자.
GenerationType.IDENTITY
먼저 IDENTITY 방식 전략에 대해 알아보겠다.
- PK의 생성을 데이터베이스에 위임.
- em.persist(뿅뿅); 의 인식과 동시에 쿼리 실행.
- 부여된 Id(식별자) DB에서 조회.
대표적으로 MySQL의 AUTO_INCREMENT가 있다.
@Entity
@Getter @Setter
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
이렇게 쓰시면 됨.
JPA는 기본적으로 쿼리를 모아쏘기 한다고 저번 글에서 언급한 바 있다.
수행해야 할 목록을 지연 저장소에 모아뒀다 트랜젝션 커밋이 이뤄지는 순간
모든 쿼리를 DB로 보내 수행하는 구조인데
위의 경우 트랜젝션 커밋 전까지 엔티티를 통한 다른 어떤 행동도 할 수 없는 제약을 받게 된다는
단점이 존재한다.
요약하면
다음과 같은 상황이 일어나는 것이다.
그렇기 때문에 IDENTITY 전략을 사용하면
em.persist(뿅뿅); 과 동시에 지연없이 바로 DB로 쿼리를 보내
저장 후 식별자 값을 사용 가능하게 한다.
GenerationType.SEQUENCE
오라클 데이터베이스를 사용해봤다면 시퀀스라는 것을 알고 있을 확률이 높다.
시퀀스란 유일한 값을 생성하는 데이터베이스 오브젝트로
MySQL의 AUTO_INCREMENT와 같은 역할을 수행한다.
먼저 SEQUENCE 전략을 사용하기 위해서는 하나의 어노테이션이 더 필요한데
@SequenceGenerator 다.
속성 | 설명 | default |
name | 식별자 생성기 이름 | 필수임. |
sequenceName | 데이터베이스에 있는 시퀀스 이름 | hibernate_sequence |
initialValue | 시퀀스 시작점을 지정 | 1 |
allocationSize | 시퀀스의 증가값 1로 세팅하셈. | 50 |
catalog, schema | 데이터베이스의 각 항목 이름 |
김영한 선생님의 JPA 기본편(존X 좋음. 사셈.)
@Entity
@Getter @Setter
@SequenceGenerator(
name = "뿅뿅제네레다",
sequenceName = "개뿅뿅",
initialValue = 1,
allocationSize = 1 //상황에 따라 좀 변하겠지만 걍 1 하셈.
)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "뿅뿅제네레다")
private Long id;
private String name;
}
다음과 같이 사용하면 된다.
쉽게 @SequenceGenerator라는 중계기를 한번 거친다고 생각하면 편하게 이해가 될듯 싶다.
GenerationType.TABLE
쉽게 말해 "짭퀀스"라 정의할 수 있겠다.
테이블 전략은 키 생성 전용 테이블을 하나 만들어
시퀀스의 동작을 모방하여 사용한다.
모든 데이터베이스(ORACLE, MySQL, Maria 등)에 적용이 가능하지만
개병X같은 성능을 가지고 있으니 주의하자.
@Entity
@Getter @Setter
@TableGenerator(
name = "뿅뿅제네레다",
table = "뿅뿅같은_테이블",
pkColumnValue = "뿅뿅_시퀀스",
allocationSize = 1 //상황에 따라 좀 변하겠지만 걍 1 하셈.
)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.TABLE,
generator = "뿅뿅제네레다")
private Long id;
private String name;
}
다음과 같이 사용하면 된다.
표는 적어둘테지만 읽어보는 것 만으로도 이미 개병X같은 냄새를 감지했을테니
엥간하면 Auto나 다른거 쓰자.
속성 | 설명 | default |
name | 식별자 생성기 이름 | 필수임. |
table | 키 생성하는 테이블 이름 | hibernate_sequences |
pkColumnName | 시퀀스 컬럼 이름 | sequence_name |
valueColumnName | 시퀀스 값 컬럼 이름 | next_val |
pkColumnValue | 키로 사용할 값 이름 | 엔티티 이름 |
initialValue | 시작 값, 마지막 생성된 값이 기준임. | 0 |
allocationSize | 한 번 마다 증가하는 수 | 50 |
catalog, schema | 데이터베이스 내 각 항목 이름 | |
uniqueConstratints(DDL) | 유니크 제약조건 지정가능 |
김영한 선생님의 JPA 기본편(일단 사셈.)
다음과 같은 속성이 있다.
인터넷에 떠도는 표들을 보면 valueColumnNa라고 적혀있는 곳이 많은데
아무리 봐도 구려서 인텔리제이로 찍어봤더니 valueColumnName이 맞다.
valueColumnNa로 적고 한참 찾아헤메는 몽키같은 짓은 하지 않도록 하자.
GenerationType.AUTO // default값
각 데이터베이스의 문법에 맞게 본인이 알아서 처리한다.
그냥 엥간하면 이거 쓰셈.
조금 더 세밀한 조정이 필요하면 데이터베이스에 따라
위에 IDENTITY나 SEQUENCE 둘 중 하나 땡겨다 쓰시고.
@Entity
@Getter @Setter
public class Member {
@Id
@GeneratedValue
private Long id;
private String name;
}
아무것도 안적어도 되기때문에 가독성이 15G다.
이상으로 Id의 부여와 각 부여방법 별 전략에 대해 알아봤다.
혹시나 글에 틀린점이 있다면 그건 내가 허접이라 그런것이니 대충 흘려들어주시기 바란다.
그럼 컴파일 오류 없는 개발하시길 :)
'JPA' 카테고리의 다른 글
[JPA] no entitymanager with actual transaction available for current thread (5) | 2023.01.04 |
---|---|
[JPA] 연관관계 맵핑(다대일 / N:1) (2) | 2022.12.28 |
[JPA] 영속성 컨텍스트와 Flush (4) | 2022.12.22 |
[JPA] 영속성 컨텍스트란 무엇인가. (0) | 2022.12.22 |
[JPA] JPA란 무엇인가. (1) | 2022.12.20 |