본문 바로가기
Spring/Spring Data

JPA PK 를 SequentialUUID 로 사용하기

by 김짜이상 2023. 12. 5.

JPA 에서 SequentialUUID 를 사용하려면 우선 Utils 에 SequentialUUID 생성 하는 함수를 생성한다.

const val SEQUENTIAL_UUID_SIZE = 32

fun creteSequentialUUID(): String {  
    val uuidParts = Generators.timeBasedGenerator().generate().toString().split("-")  
    return StringBuffer(SEQUENTIAL_UUID_SIZE).apply {  
        append(uuidParts[2])  
        append(uuidParts[1])  
        append(uuidParts[0])  
        append(uuidParts[3])  
        append(uuidParts[4])  
    }.toString()  
}

환경 정보

  • Kotlin 1.7.22
  • Spring Boot 3.0.2
  • PostgreSQL 16 - Local with Podman

1. Entity 에서 직접 ID 에 주입 하는 방식

  • Entity Class
import com.example.kotlinplayground.common.utils.creteSequentialUUID  
import jakarta.persistence.Column  
import jakarta.persistence.Entity  
import jakarta.persistence.Id  
import jakarta.persistence.Table  
import java.time.LocalDateTime  

@Entity  
@Table(name = "table_uuid")  
class TableUUID(  

    @Id  
    @Column(columnDefinition = "varchar(32)")  
    var id: String = creteSequentialUUID(),  

    val name: String,  
    val email: String,  
    val password: String,  

    val createdAt: LocalDateTime = LocalDateTime.now(),  
) {  

    override fun equals(other: Any?): Boolean {  

        if (this === other) return true  
        if (other !is TableUUID) return false  
        if (id != other.id) return false  

        return true    }  

    override fun hashCode(): Int {  
        return id.hashCode()  
    }  
}
  • Repository Class
import com.example.kotlinplayground.domain.tableUUID.TableUUID  
import org.springframework.data.jpa.repository.JpaRepository  

interface TableUUIDRepository : JpaRepository<TableUUID, String>,
  • TableUUIDRepositoryTest - kotest 의 DescribeSpec 사용
@SpringBootTest  
@ActiveProfiles("local")  
@Transactional 
class TableUUIDRepositoryTest @Autowired constructor(  
    private val tableUUIDRepository: TableUUIDRepository  
) : DescribeSpec({

    describe("tableUUIDRepository") {  
        context("tableUUID 를 생성해서 저장하면") {  
            val tableUUID = tableUUIDRepository.save(createTableUUID())  
            it("count 1이 된다.") {  
                println("tableUUID.id = ${tableUUID.id}")  
                tableUUIDRepository.count() shouldBeGreaterThanOrEqual 1  
            }  
            it("id 의 길이는 32 이다") {  
                println("tableUUID.id = ${tableUUID.id}")  
                tableUUID.id.length shouldBe 32  
            }  
        }
    }

)}

2. Entity 에서 @GeneratedValue 처럼 Annotation 에서 주입 하는 방식

  • IdentifierGenerator 의 구현체를 추가
  • SequentialUUIDGenerator 이름의 class 로 생성
import com.example.kotlinplayground.common.utils.creteSequentialUUID  
import org.hibernate.engine.spi.SharedSessionContractImplementor  
import org.hibernate.id.IdentifierGenerator  
import java.io.Serializable  

class SequentialUUIDGenerator : IdentifierGenerator {  

    override fun generate(session: SharedSessionContractImplementor, entity: Any): Serializable {  
        return creteSequentialUUID()  
    }  
}
  • Entity Class
  • GeneratedValue Annotation 의 generator 이름을 정의 해줍니다. 이름은 임의로 해주셔도 됩니다.(ex. sequential-uuid)
  • GenericGenerator Annotation 에서 name 와 strategy 항목 기입
    • GeneratedValue 의 generator 값이랑 동일하게 기입
    • 추가 할 Generator 의 Class 가 위치하는 주소

Intellij 에서의 class 위치 복사하기

  • 복사하려는 class 우클릭 -> Copy Path / Reference
  • Copy Reference 클릭
import jakarta.persistence.*  
import org.hibernate.annotations.GenericGenerator  
import java.time.LocalDateTime  

@Entity  
@Table(name = "table_uuid_annotation")  
class TableUUIDAnnotation(  

    @Id  
    @GeneratedValue(generator = "sequential-uuid")  
    @GenericGenerator(name = "sequential-uuid",  
        strategy = "com.example.kotlinplayground.common.domain.SequentialUUIDGenerator"  
    )  
    @Column(columnDefinition = "varchar(36)")  
    val id: String = "",  

    val name: String,  
    val email: String,  
    val password: String,  

    val createdAt: LocalDateTime = LocalDateTime.now(),  

    ) {  

    override fun equals(other: Any?): Boolean {  

        if (this === other) return true  
        if (other !is TableUUIDAnnotation) return false  
        if (id != other.id) return false  

        return true    }  

    override fun hashCode(): Int {  
        return id.hashCode()  
    }  
}
  • TableUUIDAnnotationRepository Class
import com.example.kotlinplayground.domain.tableUUIDAnnotation.TableUUIDAnnotation  
import org.springframework.data.jpa.repository.JpaRepository  

interface TableUUIDAnnotationRepository : JpaRepository<TableUUIDAnnotation, String>,
  • TableUUIDAnnotationRepositoryTest - kotest 의 DescribeSpec 사용
@SpringBootTest  
@ActiveProfiles("local")  
@Transactional  
@Commit  
class TableUUIDAnnotationRepositoryTest @Autowired constructor(  
    private val tableUUIDAnnotationRepository: TableUUIDAnnotationRepository,  
) : DescribeSpec({  

    extension(SpringExtension)  

    describe("tableUUIDAnnotationRepository") {  
         context("tableUUIDAnnotationRepository 를 생성해서 저장하면") {  
    val tableUUIDAnnotation = tableUUIDAnnotationRepository.save(createTableUUIDAnnotation())  
    it("count 1이 된다.") {  
        println("tableUUID.id = ${tableUUIDAnnotation.id}")  
        tableUUIDAnnotationRepository.count() shouldBeGreaterThanOrEqual 1  
    }  
    it("id 의 길이는 32 이다") {  
        println("tableUUID.id = ${tableUUIDAnnotation.id}")  
        tableUUIDAnnotation.id.length shouldBe 32  
    }  
}
    }  
})

자세한 코드는 링크 에 있습니다!


출처

반응형

'Spring > Spring Data' 카테고리의 다른 글

Spring JPA 예약어 컬럼 이슈  (0) 2023.02.06
Spring 프로젝트 DataSource 없이 실행  (0) 2023.02.06