ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Jpa Validate 에러 (with. Oracle DB, h2)
    트러블 슈팅 2023. 5. 30. 16:13

    프로젝트를 진행하며, Local, Dev, Prod 환경에서는 Oracle Database를 사용하고, Test 환경에서는 H2 데이터 베이스를 사용했다.

     

    ✔ Entity

    @Entity
    @Table(name = "users")
    @SequenceGenerator(name = "USERS_SEQ_GENERATOR", sequenceName = "USERS_SEQ", initialValue = 1, allocationSize = 1)
    class User(
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USERS_SEQ_GENERATOR")
        val id: Long = 0L,
    
        @Column(nullable = false, updatable = false)
        val username: String,
                               ---
    )

     

    ✔ Schema

    CREATE TABLE users
    (
        id            NUMERIC(19, 0) NOT NULL,
        username      VARCHAR2(50)   NOT NULL,
        email         VARCHAR2(255)  NULL,
                           ---
    )

     

    ✔ application.yml

    spring:
      jpa:
        hibernate:
          ddl-auto: validate

    ◼ Oracle과 H2의 Schema를 위와 같이 작성하였고, test 환경에서 jpa.ddl-auto: validate 옵션을 설정하였다.

     

    이후, 테스트 환경에서 contextLoads()를 실행하니 다음과 같은 에러가 발생하였다.

    Schema-validation: wrong column type encountered in column [id] in table [users]; found [numeric (Types#NUMERIC)], but expecting [bigint (Types#BIGINT)]

     

    원인

    💥 해당 에러는 데이터 베이스 스키마와 JPA 엔티티 클래스의 타입이 일치하지 않아 발생한다. 즉, 데이터 베이스에서 id 필드에 대해 numeric 타입으로 발견되었지만, JPA는 Entity의 Long 타입을 보고 bigint로 예상하고 있어 에러가 발생하는 것이다.

     

    @Converter(autoApply = true)
    class LongAttributeConverter : AttributeConverter<Long?, BigDecimal?> {
        override fun convertToDatabaseColumn(entityValue: Long?): BigDecimal? {
            return entityValue?.let { BigDecimal.valueOf(it) }
        }
    
        override fun convertToEntityAttribute(databaseValue: BigDecimal?): Long? {
            return databaseValue?.toLong()
        }
    }

    ◼ AttributeConverter를 사용해서 DB 컬럼 자체의 타입을 변경하여 해결하고 했지만, @Id 어노테이션과 Converter를 함께 사용할 수 없어 해당 방법으로는 해결할 수 없다.

     

    해결 방안

    @Entity
    @Table(name = "users")
    @SequenceGenerator(name = "USERS_SEQ_GENERATOR", sequenceName = "USERS_SEQ", initialValue = 1, allocationSize = 1)
    class User(
        @Id
        @Column(columnDefinition = "NUMERIC(19, 0)")
        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USERS_SEQ_GENERATOR")
        val id: Long = 0L,
    
        @Column(nullable = false, updatable = false)
        val username: String,
                               ---
    )

    ◼ 위와 같이 @Column(columnDefinition = "")을 사용해 id 컬럼의 정의를 명확히 명시하여, JPA의 예상 타입을 numeric으로 설정한다.  

    추가적인 에러

    @Column(nullable = false, updatable = false)
    val birthday: LocalDate,

    ◼ 이번에는 LocalDate 타입에서 다음과 같은 에러가 발생한다.

     

    Schema-validation: wrong column type encountered in column [birthday] in table [users]; found [timestamp (Types#TIMESTAMP)], but expecting [date (Types#DATE)]

    ◼ 해당 에러 역시 date 타입을 기대하지만, TIMESTAMP로 에러가 발생한다.

     

    birthday 컬럼은 @Id를 사용하고 있지 않아, Converter로 해결할 수 있다. 다음과 같이 AttributeConverter를 만들어서 사용하자

    @Converter(autoApply = true)
    class LocalDateAttributeConverter : AttributeConverter<LocalDate?, Timestamp?> {
        override fun convertToDatabaseColumn(entityValue: LocalDate?): Timestamp? {
            return entityValue?.let { Timestamp.from(Instant.from(it)) }
        }
    
        override fun convertToEntityAttribute(databaseValue: Timestamp?): LocalDate? {
            return databaseValue?.let { LocalDate.parse(it.toInstant().toString()) }
        }
    }

     

     

    댓글

Designed by Tistory.