DevLog ๐ถ
[JPA] @GeneratedType - ํค ์์ฑ ์ ๋ต ์์๋ณด๊ธฐ ๋ณธ๋ฌธ
๐ฑ JPA ๊ธฐ๋ณธ ํค ์์ฑ ์ ๋ต
JPA์์ ์ ๊ณตํ๋ ๊ธฐ๋ณธ ํค ์์ฑ ์ ๋ต์ ํฌ๊ฒ 5๊ฐ์ง๋ก ๋๋์ด์ง๋ค.
- TABLE, SEQUENCE, IDENTITY, UUID, AUTO
๊ฐ ์ ๋ต์ ๋ํด์ ํ๋์ฉ ์์๋ณด๋๋ก ํ์.
๐ฑ TABLE
๐ก Indicates that the persistence provider must assign primary keys for the entity using an underlying database table to ensure uniqueness.
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ์ ์ฌ์ฉํ์ฌ ์ํฐํฐ์ ๊ธฐ๋ณธ ํค๋ฅผ ํ ๋นํด์ผ ํ๋ค.
๐ฌ ์ํฐํฐ ์ค์
@Entity
class User(
@Id @GeneratedValue(strategy = GenerationType.TABLE)
val id: Long = 0L
)
- ๊ฐ์ฅ ๊ฐ๋จํ๊ฒ ์ค์ ํ๊ธฐ ์ํด์ User ์ํฐํฐ์ ์์ด๋์ ๋ํ ํ๋๋ง ์ค์ ํด๋์๋ค.
๐ฌ h2 database

h2์์๋ ๋จผ์ user ํ ์ด๋ธ๊ณผ ์ํ์ค๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํ hibernate_sequences ํ ์ด๋ธ์ ๋ง๋ ๋ค.
์ด๋ ๋ด๋ถ์ ์ผ๋ก ์ํ์ค ๊ฐ ์์ฒด๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํ next_val๊ณผ ์ํ์ค ์ด๋ฆ ํ๋์ธ sequence_name์ ๋ง๋ค๋ฉฐ, ์์ฑํ๋ ํ ์ด๋ธ ๊ฐ๊ฐ์ ๋ํด ์ํ์ค๋ฅผ ๊ด๋ฆฌํ๊ฒ ๋๋ค.
์ดํ, ๊ฐ์ฅ ์ฒ์ ์ํ์ค ๊ฐ์ ์ด๊ธฐํํ๊ธฐ ์ํด์ insert๋ฅผ ์งํํ๋ฉฐ, 0์ผ๋ก ์ด๊ธฐํ๋ฅผ ํด๋์๋ค.
๐ฌ mysql

mysql ์ญ์ ๋ง์ฐฌ๊ฐ์ง๋ก user์ ์ํ์ค๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํ ํ ์ด๋ธ์ ์ถ๊ฐํ๋ค.
innoDB ์คํ ๋ฆฌ์ง ์์ง์ ์ฌ์ฉํ๋ค๋ ์ ๋นผ๊ณ ๋ ๋์ผํ๋ค.
๐ฌ postgresql

postgresql์์๋ ์์ ํ ๋์ผํ๊ฒ ๋์ํ๊ธฐ ๋๋ฌธ์ ์๋ตํ๊ฒ ๋ค.
๐ฌ insert ์ ๋์ ๊ณผ์

๊ธฐ๋ณธ์ ์ผ๋ก select๋ฅผ ํตํด์ ํ์ฌ ํ ์ด๋ธ์ ์ ์ฅ๋์ด ์๋ ์ํ์ค ๊ฐ์ ๊ฐ์ ธ์จ๋ค.
์ด๋, select for update ๊ตฌ๋ฌธ์ ํ์ฉํ์ฌ ๋ฝ์ ๊ฑธ๊ณ ์๊ธฐ ๋๋ฌธ์ ์ธ๋ถ์์ ํด๋น ํ ์ด๋ธ์ ๊ฐ์ ์ฝ์ ํ ์๊ฐ ์์ด ๋ฐ์ดํฐ์ ์ผ๊ด์ฑ์ ๋ง์กฑ๋๋ค. ์ดํ, ๊ฐ์ ธ์จ ๊ฐ์ ๋ฐํ์ผ๋ก ์ํ์ค ๊ฐ์ update ํด์ฃผ๊ณ , ์ต์ข ์ ์ผ๋ก user ์ํฐํฐ์ ๋ํด insert๋ฅผ ์งํํ๋ค.
์์ ์ํฉ์์๋ ๋ํดํธ๋ก 0์ด ์ ์ฅ๋์ด ์๊ณ , update ์ 1์ ์ถ๊ฐํ์ฌ 0 + 1 = 1์ ๊ฐ์ ๊ฐ์ง๊ณ , insert ์ ์
๋ฐ์ดํธ๋ ๊ฐ์ ํตํด์ id๋ฅผ ํ ๋นํ ์ํฉ์ด๋ผ๊ณ ๋ณผ ์ ์๋ค.
๐ฌ @TableGenerator
@Entity
class User(
@Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "user_seq_generator")
@TableGenerator(name = "user_seq_generator", table="user_sequence",
pkColumnName = "name", pkColumnValue = "user_col", allocationSize = 50)
val id: Long = 0L
)

ํ ์ด๋ธ ์ ๋ต์ ๊ฒฝ์ฐ @TableGenerator ์ด๋ ธํ ์ด์ ๊ณผ ํจ๊ป ์ฌ์ฉํ๋ฉด ์ํ์ค ํ ์ด๋ธ์ ๋ํ ์ ๋ณด๋ฅผ ์ปค์คํ ํ ์ ์๋ค.
- name: ์์ฑํ๋ ์ ๋๋ ์ดํฐ์ ์ด๋ฆ (@GeneratedValue์ ์ธ์๋ก ์ ๋๋ ์ดํฐ๋ฅผ ์ฐธ์กฐํ ๋ ์ฐธ์กฐํ๋ ์ด๋ฆ์ด๋ค.)
- table: ์์ฑํ๋ ์ ๋๋ ์ดํฐ ํ
์ด๋ธ์ ์ด๋ฆ
- pkColumnName: ์ ๋๋ ์ดํฐ ํ
์ด๋ธ์์ ์ง์ ํ ์ํ์ค ์ด๋ฆ ํ๋๋ช
- pkColumnValue: ์ํ์ค ์ด๋ฆ ํ๋์ ๋ํด์ ํด๋น ํ
์ด๋ธ์ ์ด๋ค ์ด๋ฆ์ผ๋ก ๊ด๋ฆฌํ ์ง ์ง์
- allocationSize: ์ฌ์ ์ ์ ์ํ ์ํ์ค ์ฌ์ด์ฆ. DB์์๋ ํ ๋นํ ์ฌ์ด์ฆ๋งํผ ๋จผ์ ์๋ฅผ ์ฆ๊ฐ์์ผ๋๊ณ , ํธ๋์ญ์
์ด ๋๋ ๋๋ง๋ค ์ฌ์ด์ฆ๋งํผ ์ถ๊ฐํ๋ค. (๊ธฐ๋ณธ๊ฐ์ด 50์ด๊ธฐ ๋๋ฌธ์ ํ์ํ๋ค๋ฉด 1๋ก ์ค์ !)
์์ ์ ๋ณด ์ธ์๋ ๋ค์ํ ์ค์ ๊ฐ๋ค์ ์ธํ ํ ์ ์๋ค.
@Test
@Transactional
fun test() {
userRepository.save(User())
}

์ค์ ๋ก, allocationSize์ ๋ํด ํ ์คํธ๋ฅผ ํ๊ธฐ ์ํด ์์ ๊ฐ์ด ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ ์ฅํด๋ณด์.
์ฐ์ , ์์ ๋ดค๋ ๊ฒ์ฒ๋ผ ์ํ์ค ์ ๋ณด๋ฅผ select ํ ํด๋น ๊ฐ์ update ํ ๊ฒ์ ๋ณผ ์ ์๋ค.

๊ทธ๋ฆฌ๊ณ sequence ์ ๋ณด๋ฅผ ํ์ธํด๋ณด๋ฉด, user ํ๋๋ง ์ ์ฅํ์์๋ ํ ๋นํ ํฌ๊ธฐ(50)๋งํผ ๊ฐ์ด ์ถ๊ฐ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
๋ง์ฝ, ๋ค์ ํ ๋ฒ ํ ์คํธ ์ฝ๋๋ฅผ ์คํํ๊ฒ ๋๋ค๋ฉด ๋ค์ sequence๋ ์ด๋ป๊ฒ ๋ ๊น? (ddl๋ update๋ก ์ค์ )

์ด๋๋ db์ next_val์ด 100์ผ๋ก ์ ์ฅ์ด ๋์ด ์๋ค. (๊ธฐ์กด next_val ๊ฐ 50 + allocationSize 50 = 100)
@Test
@Transactional
fun test() {
// ํ ๋ฒ ๋ ์คํํ์ ๋์ ๊ฒฐ๊ณผ
val user = userRepository.save(User())
assertThat(user.id).isEqualTo(2)
}
ํ์ง๋ง, user์ pk ๊ฐ์ 2๋ก ๋์ค๋ ๊ฒ์ ํ์ธํ ์ ์๋ค. ์ด๋ ๋ด๋ถ์ ์ผ๋ก ๋ฐ์์จ next_val์ ๊ฐ๊ณผ allocationSize ๊ฐ์ ๋ฐํ์ผ๋ก pk๋ฅผ ๊ณ์ฐํ ๊ฐ์ด๋ค. ๋ ์ฝ๋ 1๊ฐ๋ฅผ ์ฝ์ ํ ๋, ์ฒ์ select ์ ์ป์ด์จ (next_val - allocation_size) + 1์ผ๋ก ํ ๋น๋๋ค.
๐ฑ SEQUENCE
๐ก Indicates that the persistence provider must assign primary keys for the entity using a database sequence.
๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ ๊ณตํ๋ ์ํ์ค๋ฅผ ์ฌ์ฉํ์ฌ ์ํฐํฐ์ ๊ธฐ๋ณธ ํค๋ฅผ ํ ๋นํด์ผ ํ๋ค.
์ด๋, ์ํ์ค๋ฅผ ์ง์ํ์ง ์๋ DB๋ผ๋ฉด ํ ์ด๋ธ ์ ๋ต์ ์ฌ์ฉํ๋ค.
๐ฌ ์ํฐํฐ ์ค์
@Entity
class User(
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE)
val id: Long = 0L
)
๐ฌ h2 database

h2์ ๊ฒฝ์ฐ sequence๋ฅผ ์ง์ํ๊ธฐ ๋๋ฌธ์ โcreate sequenceโ๋ผ๋ ๊ฒ์ ํตํด์ sequence ๊ด๋ฆฌํ๋ค.
์ด๋, ๋ํดํธ๋ก ์ํ์ค ๊ฐ์ 1๋ถํฐ ์์ํ์ฌ 50์ฉ ์ฆ๊ฐํ๋ค. ์ฆ, ํ๋์ ํธ๋์ญ์
์ด ๋๋๋ฉด 50๊ฐ ์ดํ๋ฅผ ์ฝ์
ํ๋๋ผ๋ 50์ฉ ์ํ์ค ๊ฐ์ด ์ฆ๊ฐํ๊ธฐ ๋๋ฌธ์ ์ค๊ฐ์ค๊ฐ ๋น์ด์๋ PK ๊ฐ์ ๊ฐ์ง ์ ์๋ค๋ ๊ฒ์ด ํน์ง์ด๋ค.
๐ฌ mysql

โญ๏ธ ๊ทธ๋ฌ๋, mysql์์๋ sequence๋ฅผ ์ง์ํ์ง ์๊ธฐ ๋๋ฌธ์ ํ
์ด๋ธ ์ ๋ต์ฒ๋ผ user_seq๋ผ๋ ํ
์ด๋ธ์ด ์์ฑ๋๋ค.
์ด๋, ์ํ์ค ํ
์ด๋ธ์ ๋ํ ๋ค์ด๋ฐ์ ์ํฐํฐ๋ช
+ _seq์ ๊ฐ์ ํํ๋ก ๋์ํ๋ค.
๐ฌ postgresql

h2์ ๋ง์ฐฌ๊ฐ์ง๋ก postgresql ์ญ์ sequence๋ฅผ ์ง์ํ๊ธฐ ๋๋ฌธ์ create sequence๋ฅผ ํตํด์ ์ํ์ค๋ฅผ ์์ฑํ๋ค.
๐ฌ insert ์ ๋์ ๊ณผ์

๋จผ์ , ์ํ์ค๋ก๋ถํฐ ๋ค์ ๊ฐ์ ๋ฐ์์ค๋ select ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ๋ค. ("next value for" = 1์ด๋ผ๋ ๊ฐ์ ๋ฐ์์จ๋ค.)
์ด์ ์ TABLE ์ ๋ต๊ณผ ๋ค๋ฅด๊ฒ '๊ทธ ๋ค์ insert ์ ์ฌ์ฉํ ์ํ์ค ๊ฐ'์ ์ ์ฅํ๊ธฐ ๋๋ฌธ์ ์ํ์ค์ ๋ํ ๋ณ๋์ update๋ฅผ ์ฟผ๋ฆฌ๋ก ๊ด๋ฆฌํ๋ ๊ฒ์ด ์๋ ๋ด๋ถ์ ์ผ๋ก ๊ด๋ฆฌ๋๋ค๊ณ ์ถ์ธก๋๋ค. (๋ฌผ๋ก MySQL์ด๋ผ๋ฉด ํ ์ด๋ธ ์ ๋ต์ด๊ธฐ ๋๋ฌธ์ update ์ฟผ๋ฆฌ ๋ฐ์)
๋ํ, ์ถํ ๋์ฌ IDENTITY ์ ๋ต๊ณผ ๋ค๋ฅด๊ฒ persist ์ insert๋ฅผ ํ์ง ์์๋ ๋ผ์ ํธ๋์ญ์ ์ปค๋ฐ ์ insert๊ฐ ์ด๋ฃจ์ด์ง๋ค. (์ฐ๊ธฐ ์ง์ฐ ๊ฐ๋ฅ)
๐ฌ @SequenceGenerator
@Entity
class User(
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq_generator")
@SequenceGenerator(name = "user_seq_generator", sequenceName = "user_sequence",
initialValue = 10, allocationSize = 50)
val id: Long = 0L
)
@SequenceGenerator๋ฅผ ํ์ฉํ๋ฉด ์ํ์ค์ ๋ํ ์ ๋ณด๋ฅผ ์ค์ ํด์ค ์ ์๋ค.
- name: ์์ฑํ๋ ์ ๋๋ ์ดํฐ์ ์ด๋ฆ (@GeneratedValue์ ์ธ์๋ก ์ ๋๋ ์ดํฐ๋ฅผ ์ฐธ์กฐํ ๋ ์ฐธ์กฐํ๋ ์ด๋ฆ์ด๋ค.)
- sequenceName: ์์ฑํ๋ ์ํ์ค์ ์ด๋ฆ
- initialValue: ์ํ์ค์ ์ด๊ธฐ๊ฐ
- allocationSize: ์ฌ์ ์ ์ ์ํ ์ํ์ค ์ฌ์ด์ฆ. DB์์๋ ํ ๋นํ ์ฌ์ด์ฆ๋งํผ ๋จผ์ ์๋ฅผ ์ฆ๊ฐ์์ผ๋๊ณ , ํธ๋์ญ์
์ด ๋๋ ๋๋ง๋ค ์ฌ์ด์ฆ๋งํผ ์ถ๊ฐํ๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก ์์ ์ ๋ณด ์ธ์๋ ๋ค์ํ ์ค์ ๊ฐ๋ค์ ์ธํ
ํ ์ ์๋ค.

ํค ํ ๋น ์ ๋ต์ table ๋์ ๋๊ฐ์ด (next_val - allocation_size) + 1์ ๊ฐ์ด ์ค์ ๋๋ค.
๐ฑ IDENTITY
๐ก Indicates that the persistence provider must assign primary keys for the entity using a database identity column.
- ๋ฐ์ดํฐ๋ฒ ์ด์ค์ auto-increment ์ด์ ์ฌ์ฉํ์ฌ ์ํฐํฐ์ ๊ธฐ๋ณธ ํค๋ฅผ ํ ๋นํด์ผ ํ๋ค.
๋ ์ฝ๋๊ฐ ์๋กญ๊ฒ ์ถ๊ฐ๋ ๋๋ง๋ค ์๋์ผ๋ก ๊ณ ์ ๊ฐ์ ์์ฑํ๊ณ ์ฆ๊ฐ์ํจ๋ค.
๐ฌ ์ํฐํฐ ์ค์
@Entity
class User(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0L
)
๐ฌ h2 database

๋ณ๋์ ์ํ์ค ํ ์ด๋ธ์ ์์ฑํ์ง ์๊ณ , pk๋ก ์ฌ์ฉ๋ ์ปฌ๋ผ์ as identity๋ผ๋ ์ด๋ฆ์ด ๋ค์ด๊ฐ๋ค.
๐ฌ mysql

h2์ ๊ฑฐ์ ๋น์ทํ์ง๋ง auto_increment๊ฐ pk ์ปฌ๋ผ์ ๋ถ๊ฒ ๋๋ค.
cf. ์ฐธ๊ณ ๋ก innoDB์์๋ auto_increment๋ฅผ ์ํ ์๋ ์ฆ๊ฐ ๋ฝ์ด๋ผ๋ ๊ฒ์ ํตํด์ ํด๋น ๊ฐ์ ๊ด๋ฆฌํ๋ค.
๐ฌ postgresql

postgresql์ ๊ฒฝ์ฐ bigserial์ด๋ผ๋ ๊ฐ์ ํตํด์ ๊ด๋ฆฌํ๋ค.
์ด๋ postgresql์์ ์ฌ์ฉํ๋ ์๋ ์ฆ๊ฐ ํ์
์ผ๋ก, ์ปฌ๋ผ์ด ์ฝ์
๋ ๋๋ง๋ค 1์ฉ ์ฆ๊ฐ๋ ๊ฐ์ด ์ ์ฅ๋๋ค.
๐ฌ insert ์ ๋์ ๊ณผ์

์์ ๋ณธ ํ ์ด๋ธ, ์ํ์ค ์ ๋ต๊ณผ ๋ค๋ฅด๊ฒ select ์ฟผ๋ฆฌ๊ฐ ์์ด ๋ฐ๋ก insert ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ๋ค.
์ด๋, ()์ ๊ฐ์ด ๋น ๊ฐ์ด ๋ค์ด๊ฐ๋ ๊ฒ์ ๋ณผ ์ ์๋๋ฐ, ์ด๋ ์ฝ์ ํ๋ ์์ ์๋ id ๊ฐ์ ์ ์ ์๊ธฐ ๋๋ฌธ์ ๋น๊ฐ (java๋ผ๋ฉด null)์ด ๋ค์ด๊ฐ๋ ๊ฒ์ด๋ค. ์ฆ, ์ฝ์ ์์ ์ id ๊ฐ์ ์ ์ด์ ๋ชจ๋ฅด๋๊น ์ฝ์ ์ ์ select ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆด ์ ์๋ ๊ฒ์ด๋ค.
IDENTITY ์ ๋ต ์ฌ์ฉ ์ ํธ๋์ญ์
์ปค๋ฐ ์์ ์ด ์๋, persist ์์ ์ ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ์ฌ, ์ฐ๊ธฐ ์ง์ฐ์ด ๋ถ๊ฐ๋ฅํ๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก JPA์ ์์์ฑ ์ปจํ ์คํธ์์ ๊ฐ์ฒด๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํด์๋ @Id ์์ฑ์ด ๊ผญ ํ์ํ๋ฐ auto_increment์ ๊ฒฝ์ฐ DB์ ์ง์ ๋ ์ฝ๋๋ฅผ insert๋ฅผ ํ ๋ค์ ID ๊ฐ์ ์ ์ ์๊ธฐ ๋๋ฌธ์ persist() ์์ ์ insert๋ฅผ ์งํํ์ฌ ์๋ณ์๋ฅผ ๋ฏธ๋ฆฌ ์์๋๋ค. ์ดํ, ์์๋ณธ ๊ฐ์ ์์์ฑ ์ปจํ ์คํธ์ 1์ฐจ ์บ์ ๋ด๋ถ์ ์ ์ฅํ๋ค.
๐ฌ IDENTITY with generator
๋ ์์ฑ์ ๊ฐ์ด ์ฐ๋ฉด ์ด๋ป๊ฒ ๋์ํ ์ง ๊ถ๊ธํด์ ์คํํด๋ณด์๋ค.
@Entity
class User(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "user_seq_generator")
@SequenceGenerator(name = "user_seq_generator", sequenceName = "user_sequence",
initialValue = 10, allocationSize = 50)
val id: Long = 0L
)

ํ์ธ ๊ฒฐ๊ณผ, ์ํ์ค ์ต์ ์ด ๋ ์ฐ์ ์ผ๋ก ์ ์ฉ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
์ฐธ๊ณ ๋ก, ํ ์ด๋ธ ์ ๋๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ๋๋ผ๋ ๋ง์ฐฌ๊ฐ์ง๋ก ์ฐ์ ์ ์ฉ๋๋ค.
๐ฑ AUTO
๐ก Indicates that the persistence provider should pick an appropriate strategy for the particular database.
The AUTO generation strategy may expect a database resource to exist, or it may attempt to create one.
A vendor may provide documentation on how to create such resources in the event that it does not support schema generation or cannot create the schema resource at runtime.
ํน์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ๋ฅธ ์ ์ ํ ์ ๋ต์ ์ ํํ๋ฉฐ, ๋ฒค๋ ์ฌ์ ๋ฐ๋ผ์ ์๋์ผ๋ก ๊ฒฐ์ ๋๋ค.
๐ฌ ์ํฐํฐ ์ค์
@Entity
class User(
@Id @GeneratedValue(strategy = GenerationType.AUTO)
val id: Long = 0L
)
๐ฌ h2 database

h2์ ๊ฒฝ์ฐ ์ํ์ค๋ฅผ ์ง์ํ๊ธฐ ๋๋ฌธ์ ์ํ์ค ์ ๋ต์ ์๋์ผ๋ก ์ฑํํ๋ค.
๐ฌ mysql

์ํ์ค ์ ๋ต์ ์ง์ํ์ง ์๋ mysql์ ๊ฒฝ์ฐ ํ ์ด๋ธ ์ ๋ต์ ์ฌ์ฉํ๋ค.
๐ฌ postgresql

h2์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ํ์ค ์ ๋ต์ ์ฌ์ฉํ๋ค.
๐ฌ SEQUENCE vs AUTO
์ฌ๊ธฐ๊น์ง๋ง ๋ดค๋ค๋ฉด ์ํ์ค ์ ๋ต์ด๋ auto๋ ๋ณ๋ค๋ฅธ ๊ฒ ์๋ค๊ณ ์๊ฐ์ด ๋ค ๊ฒ์ด๋ค.
ํ์ง๋ง, pk๊ฐ uuid์ธ โ์ํฐํฐ๋ฅผ ์ ์ฅํ ๋โ ์ํ์ค ์ ๋ต์ ๊ฒฝ์ฐ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ณ , auto์ ๊ฒฝ์ฐ ์ ์์ ์ผ๋ก ๋์ํ๋ค.
์ด๋, ๋จ์ํ ํ ์ด๋ธ ์์ฑ ์์ฒด๋ ์ ์์ ์ผ๋ก ๋์ํ๋ค.
sequence
@Entity
class User(
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE)
val id: UUID = UUID.randomUUID()
)

h2๋ก ํ ์คํธ๋ฅผ ์งํํ๊ธฐ ๋๋ฌธ์ ์ํ์ค ํ ์ด๋ธ์ ์์ฑํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
์ด๋, user ํ ์ด๋ธ์ id๊ฐ uuid๋ก ์ค์ ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
userRepository.save(User())

๊ธฐ๋ณธ์ ์ผ๋ก User ๊ฐ์ฒด์ id์ ๊ธฐ๋ณธ๊ฐ์ ์ ์ํ์๊ธฐ ๋๋ฌธ์ select ์ฟผ๋ฆฌ๋ฅผ ํตํด์ ์ด๋ฏธ ๊ฐ์ฒด๊ฐ ์กด์ฌํ๋์ง ํ์ธํ ๋ค์, ์กด์ฌํ์ง ์์ผ๋ฉด user_seq์ ์ํ์ค ๊ฐ์ผ๋ก๋ถํฐ ๋ค์ ๊ฐ์ ๊ฐ์ ธ์จ๋ค.
Unknown integral data type for ids: java.util.UUID
ํ์ง๋ง, ์ดํ UUID ํ์
์ผ๋ก๋ save๊ฐ ๋์ง ์๋๋ค๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
์ด๋, ์ด๊ธฐ sequence ์ ์๋ฅผ ๋ณด๋ฉด 1๋ถํฐ ์์ํ์ฌ 50์ฉ ์ฆ๊ฐํ๋ ๋ํดํธ ์ค์ ์ ๊ฐ์ง๊ณ ์์ง๋ง, pk ๊ฐ์ด uuid๋ก ๋์ด ์๋ค ๋ณด๋ uuid์ ๋ํด ์ฆ๊ฐ ์ฐ์ฐ์ ํ ์ ์์ด ๋ฐ์ดํฐ ํ์
์ ๋ํ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
auto
@Entity
class User(
@Id @GeneratedValue(strategy = GenerationType.AUTO)
val id: UUID = UUID.randomUUID()
)

์ํ์ค๋ฅผ ์ง์ํ๋ h2์์๋, ์ํ์ค๋ฅผ ์์ฑํ์ง ์๊ณ ๋จ์ํ pk๊ฐ UUID์ธ user ํ ์ด๋ธ์ ์์ฑํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
userRepository.save(User())

์ฐ์ ์์ด๋ ๊ฐ์ ๋ํด์ UUID.randomUUID()๋ฅผ ํ์ฉํ์ฌ ๊ธฐ๋ณธ๊ฐ์ ์ค์ ํ๊ฒ ๋๋ฉด, ์ด๋ฏธ ์กด์ฌํ๋ ๋ ์ฝ๋์ธ์ง ํ์ธํ๊ธฐ ์ํด ์ ์ฅ ์ ์ select ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ๊ณ ์ดํ insert ์ฟผ๋ฆฌ๊ฐ ๋ ๋ผ๊ฐ๋ค. ์ด๋, ๋ ์ฝ๋ ์ฝ์ ์ ํด๋น PK ๊ฐ์ ์ฆ๊ฐ์ํค๋ฉด์ ๊ด๋ฆฌํ ํ์๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ง ์๊ณ ์ ์ง์ ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
identity (๋ฒ์ธ)
@Entity
class User(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0L
)

์ฐธ๊ณ ๋ก, identity๋ก ์ค์ ํ์ ๋๋ save๊ฐ ์๋ ํ ์ด๋ธ์ ์์ฑํ๋ ์์ ๋ถํฐ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
uuid ์์ฒด๋ฅผ ์๋ ์ฆ๊ฐ๊ฐ์ผ๋ก ๊ด๋ฆฌํ ์ ์๊ธฐ ๋๋ฌธ์ ๋ฐ์ํ๋ ์ค๋ฅ์ด๋ค.
โญ๏ธ ๊ฒฐ๊ณผ์ ์ผ๋ก ์๋ณ์ ํ์ ์ด UUID๋ฉด UUID๋ฅผ ์๋ณ์๋ก ์ฌ์ฉํ๊ณ , ์ซ์๋ฉด์ ์ํ์ค๋ฅผ ์ง์ํ๋ฉด ์ํ์ค๋ก, ์๋๋ฉด ํ ์ด๋ธ๋ก ์ฌ์ฉํ๋ค๊ณ ์๊ฐํ๋ฉด ๋๋ค.
๐ฑ UUID
๐ก Indicates that the persistence provider must assign primary keys for the entity by generating an RFC 4122 Univerally Unique IDentifier.
- RFC 4122 Universally Unique Identifier๋ฅผ ํตํด์ ์ํฐํฐ์ ๊ธฐ๋ณธ ํค๋ฅผ ํ ๋นํ ์ ์๋ค.
PK ํ๋ ์์ฒด๋ฅผ UUID๋ก ์ค์ ํ๊ณ , AUTO ์ ๋ต์ ์ฌ์ฉํ๋ ๊ฒ๊ณผ ๋์ผํ๊ฒ ๋์ํ๋ค.
๐ฌ ์ํฐํฐ ์ค์
@Entity
class User(
@Id @GeneratedValue(strategy = GenerationType.UUID)
val id: Long = 0L
)
๐ฌ h2 database

id ๊ฐ์ ํ์ ์ผ๋ก uuid๊ฐ ์ค์ ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ด๋ ๋ณ๋์ ์ํ์ค๊ฐ ์๋ค๋ ์ง, ์๋ ์ฆ๊ฐ ์ปฌ๋ผ์ผ๋ก์ ๋์ํ์ง๋ ์๋๋ค.
๐ฌ mysql

mysql์ ๊ฒฝ์ฐ binary๋ผ๋ ํ์ ์ ํ์ฉํ์ฌ ํ๋๊ฐ ์์ฑ์ด ๋๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก ์๋ ์ฆ๊ฐ๋ก ๊ด๋ฆฌ๋์ง ์๊ณ , ๋ณ๋์ ์ํ์ค ํ ์ด๋ธ์ด ์์ฑ๋์ง ์๋๋ค.
๐ฌ postgresql

h2์ ๋์ผํ๊ฒ uuid๋ผ๋ ํ๋๊ฐ์ผ๋ก ์์ฑ๋๋ค.
๐ฑ @IdGeneratorType
@TableGenerator, @SequenceGenerator ์ธ์๋ ๋ฒ์ฉ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๋ @GenericGenerator๋ผ๋ ์ด๋
ธํ
์ด์
์ด ์กด์ฌํ๋ค.
ํ์ง๋ง, ๊ณต์ ๋ฌธ์์์ @IdGeneratorType์ ๋์ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ๊ณ ํ์๊ธฐ ๋๋ฌธ์ ์ฌ์ค์ deprecated ๋์๋ค๊ณ ์๊ฐํ๋ค.
@IdGeneratorType์ ์ฌ์ฉํ๋ฉด ์ด๋
ธํ
์ด์
์ด ์๋ ์ธํฐํ์ด์ค๋ฅผ ํ์ฉํ์ฌ ์กฐ๊ธ ๋ ์ธ์ธํ๊ฒ ์ปค์คํ
์ ์งํํ ์ ์๋ค.
public class CustomSequenceGenerator implements IdentifierGenerator {
public CustomSequenceGenerator(
Sequence config,
Member annotatedMember,
CustomIdGeneratorCreationContext context) {
//...
}
@Override
public Object generate(
SharedSessionContractImplementor session,
Object object) {
//...
}
@IdGeneratorType( CustomSequenceGenerator.class )
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface Sequence {
String name();
int startWith() default 1;
int incrementBy() default 50;
Class<? extends Optimizer> optimizer() default Optimizer.class;
}
์ฌ์ค ์ง์ ์ฌ์ฉํ ์ผ์ ์์ ๊ฒ ๊ฐ์์ ๊ทธ๋ฅ Member ์์ @Sequence๋ผ๋ ์ปค์คํ ์ด๋ ธํ ์ด์ ์ด ์ ์ฉ๋์์ ๊ฒฝ์ฐ๋ฅผ ๋ณด์ฌ์ฃผ๋ ์์ ์ฝ๋๋ฅผ ๊ฐ์ ธ์๋ค.
public class TableGenerator implements PersistentIdentifierGenerator {
...
@Override
public Object generate(final SharedSessionContractImplementor session, final Object obj) {
final SqlStatementLogger statementLogger = session.getFactory().getServiceRegistry()
.getService( JdbcServices.class )
.getSqlStatementLogger();
final SessionEventListenerManager statsCollector = session.getEventListenerManager();
return optimizer.generate(
new AccessCallback() {
@Override
public IntegralDataTypeHolder getNextValue() {
return session.getTransactionCoordinator().createIsolationDelegate().delegateWork(
new AbstractReturningWork<>() {
@Override
public IntegralDataTypeHolder execute(Connection connection) throws SQLException {
return nextValue( connection, statementLogger, statsCollector );
}
},
true
);
}
@Override
public String getTenantIdentifier() {
return session.getTenantIdentifier();
}
}
);
}
}
์ค์ ๋ก @TableGenerator ์ญ์ IdentifierGenerator ์ธํฐํ์ด์ค๋ฅผ ํ์ฉํ์ฌ ๊ตฌํ๋์ด ์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๐ฑ ์์ ์๋ฌด๊ฒ๋ ๋ถ์ด์ง ์๋๋ค๋ฉด?
@Entity
class User(
@Id
val id: Long = 0L
)
@GenerateType์ ์์ ๋ถ์ด์ง ์์๋ ๋๋์ง ๊ถ๊ธํด์ ์คํํด๋ณด์๋ค.
๐ฌ h2 database

๊ธฐ๋ณธ์ ์ผ๋ก user table๋ง ์์ฑํ๊ณ , id์๋ auto_increment ๊ฐ์ด ๊ด๋ฆฌํ๋ ์กฐ๊ฑด์ด ๋ค์ด๊ฐ์ง ์๋๋ค.
์ด ์ํ๋ก ์ฝ์ ์ ํ๊ฒ ๋๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
userRepository.save(User())

id์ ๋ํ ๋ฐ์ธ๋ฉ์ด 0์ผ๋ก ๋ค์ด๊ฐ ๊ฒ์ ๋ณผ ์ ์๋ค.
userRepository.save(User())
userRepository.save(User())

๋ํ, save๋ฅผ 2๋ฒ ํธ์ถํ๊ฒ ๋๋ฉด์ ์์ ๊ฐ์ด ์ค๋ณต๋ ์๋ณ์๋ฅผ ์ฌ์ฉํ๋ค๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค. (PK๋ unique ํด์ผ ํ๋๊น)
์ด๋ PK ๊ฐ์ ๋ํด ๋ณ๋๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ง์ ํ์ง ์์๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ์๊ฐ ์ง์ id๋ฅผ ๋ฃ์ด์ค์ผ ๋๋ฉด์ ์๊ธด ๋ฌธ์ ์ด๋ค. ์ฐ์ ์ํฐํฐ ์์ฑ ์ ์์ด๋์ ๋ํ ๊ธฐ๋ณธ๊ฐ์ธ 0L์ ์ง์ ํ๋ฉด์ ๋ ๋ค์ '0L'์ด๋ผ๋ ๋์ผํ ํค๋ฅผ ๊ฐ์ง ์ํฐํฐ๊ฐ ์ฝ์ ๋๋ฉด์ ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฒ์ด๋ค.๊ทธ๋์ ๋ณตํฉํค ๊ฐ์ ํน์ ์ํฉ์ ์ ์ธํ๋ฉด ์ฌ๋งํ๋ฉด ์์ฑ ์ ๋ต์ ํตํด์ ๊ด๋ฆฌํ๋ ๊ฒ ์ข๋ค๋ ์๊ฐ์ด ๋ค์๋ค.
๐ฌ mysql

์๋ ๊ณผ์ ์ ์๊ณผ ๋๊ฐ๊ธฐ ๋๋ฌธ์ ๋ณ๋๋ก ์ค๋ช ํ์ง ์๊ณ , ํ ์ด๋ธ ํ์๋ง ๋ณด๊ฒ ๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก ๋จ์ํ ํ ์ด๋ธ์ ํ๋๊ฐ์ผ๋ก bigint๋ง ์กด์ฌํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
๐ฌ postgresql

h2์ ๋์ผํ๊ฒ ์์ฑ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
๐ฑ ๊ธฐ๋ณธ๊ฐ์ ํ ๋นํด์ฃผ์ง ์์๋ค๋ฉด?
์ฌ๊ธฐ์๋ ๊ธฐ๋ณธ๊ฐ์ ์ด๋ป๊ฒ ํ ๋นํ๋์ง์ ๋ฐ๋ผ์ ๊ฒฐ๊ณผ๊ฐ ๋ฌ๋ผ์ง๋ค. (generatedType์ ์ง์ ํ์ง ์์์ ๋์ ๊ฒฐ๊ณผ)
@Entity
class User(
@Id
val id: Long = 0L
)

ํํ ์ฌ์ฉํ๋ Long ํ์
id์ 0L ๊ฐ์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ด๊ธฐํํ๊ฒ ๋๋ค๋ฉด, ์ํฐํฐ ์ ์ฅ ์ ๋จ์ํ ํ๋์ insert ์ฟผ๋ฆฌ๋ง ๋ฐ์ํ๊ฒ ๋๋ค. (Int ํ์
๋ ๋ง์ฐฌ๊ฐ์ง)

ํ์ง๋ง, 1L ๊ฐ์ด ํน์ ํ ๊ฐ์ผ๋ก ์ค์ ํ๊ฒ ๋๋ฉด select ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ๊ณ ๊ทธ ๋ค์ insert ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ๋ค.
์ด๋ ์ฝ์ ์ ์ ์ด๋ฏธ ํด๋น ๋ ์ฝ๋๊ฐ ์กด์ฌํ๋์ง ํ์ธํ๊ณ (๊ด๋ฆฌ ์ ๋ต์ ์ค์ ํด์ฃผ์ง ์์์ผ๋๊น) insert๋ฅผ ๋ ๋ ค์ ์กด์ฌํ์ง ์๋ ๊ฒ์ ๋ํด์๋ง insert๋ฅผ ํ๊ธฐ ๋๋ฌธ์ด๋ค.
@Entity
class User(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0L
)

๊ฐ์ ์๋ฆฌ๋ก IDENTITY๋ฅผ ์ค์ ํ๊ฒ ๋๋ฉด ์ ์ด์ insert ์ ์์ด๋๊ฐ ์์ผ๋๊น ์ ์ด์ ๊ฒ์ฆํ ๊ฐ์ด ์์ผ๋ ๋จ์ํ insert๋ง ๋ฐ์ํ๋ค. (persist ์์ insert ์ฟผ๋ฆฌ ๋ฐ์)
@Entity
class User(
@Id
val id: String = ""
)

String์ ๊ฒฝ์ฐ ๋ฌด์กฐ๊ฑด select๊ฐ ๋ฐ์ํ๋ค. (์ ์ด์ String? ํ์
์ผ๋ก ์ค์ ํ ๊ธฐ๋ณธ ๊ฐ์ null๋ก ์ง์ ํ๋ฉด ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.)
์ด๋, save() ์์ ๋ฉ์๋๋ฅผ ๋ณด๋ฉด ํ์ ํ ์ ์๋ค.

save() ์ ๋ฉ์๋๋ฅผ ๋ณด๋ฉด ์ํฐํฐ๊ฐ ์๋ก์ฐ๋ฉด persist, ์๋๋ฉด merge๋ฅผ ํ๊ฒ ๋๋ ๊ฑธ ๋ณผ ์ ์๋ค.
์ฌ๊ธฐ์ isNew() ๋ฉ์๋๋ฅผ ํ๊ณ ๋ค์ด๊ฐ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.

primitive ํ์ ์ด ์๋๋ผ๋ฉด null๋ก ์ฒดํฌ, Number ํ์ด๋ผ๋ฉด 0L์ผ๋ก ์ฒดํฌํด์ ์ด๊ฒ ์๋ก์ด ๊ฐ์ธ์ง ํ ์คํธํ๊ฒ ๋๋ค.