본문 바로가기

백엔드 개발/JPA

[하이버네이트 유저 가이드 파헤치기] JPA 2.1 AttributeConverters 실습

반응형
모든 실습 코드는 필자의 github의 리포지토리에서 볼 수 있다.
실습 환경은 spring boot(2.2.6) + kotlin이다.
자세한 사항은 build.gradle.kts 참고바란다.

 

공식 문서에 나와있는대로 해보기

우선 공식 문서의 예제를 그대로 재현해서 attributeConverter가 제대로 동작하는지 테스트해보겠다.

 

example1. 엔티티 Event 정의

@Entity
class Event(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(insertable = false, updatable = false)
    val id: Long? = null,

    @Convert(converter = PeriodStringConverter::class)
    @Column(columnDefinition = "")
    var span: Period,
)

 

 

example2. PeriodStringConverter 정의

@Converter
class PeriodStringConverter : AttributeConverter<Period, String> {
    override fun convertToDatabaseColumn(attribute: Period?): String {
        return attribute.toString()
    }

    override fun convertToEntityAttribute(dbData: String?): Period {
        return Period.parse(dbData)
    }
}

 

 

example3. 영속화 test 코드 작성

@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@DataJpaTest
class AttributeConvertorTest(val em: EntityManager) {
    @Test
    fun testSave() {
        val event = Event(
            span = Period.between(LocalDate.now(), LocalDate.now().plusDays(16))
        )
        em.persist(event)
        em.flush()
    }
}

 

 

영속화 테스트 실행 결과

 

로그를 보면 hibernate가 실행한 SQL 문과 파라미터를 볼 수 있는데.

 span 칼럼에 VARCHAR 타입으로 값이 들어가서 의도한대로 엔티티가 저장됐다.

 

이제 조회하는 코드를 테스트해보자.

필자는 @DataJpaTest 어노테이션으로 테스트를 하고 있는데.

이 경우 테스트를 실행할 때 마다 rollback 하므로 실제 db에 저장되어 있는 값을 조회하기 위해서는 별도의 설정을 해줘야한다.

application.properties 파일에 spring.datasource.data=classpath:data-h2-test.sql 설정을 추가해서 resources 폴더 하위에 data-h2-test.sql가 테스트 실행 전에 실행되도록 해주자.

 

data-h2-test.sql에는 아래 SQL문을 넣어주자

insert into event (span, id) values ('P6D', 1)
insert into event (span, id) values ('P6D', 2)

 

 

 

example4. 조회 test 코드 작성

@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@DataJpaTest
class AttributeConvertorTest(val em: EntityManager) {
    @Test
    fun testSelect() {
        val event1 = em.find(Event::class.java, 1L)
        val event2 = em.find(Event::class.java, 2L)
    }
}

 

조회 테스트 실행 결과

 

조회또한 성공적으로 실행된다.

 

반응형

 

AttributeConverter 선언해서 사용해보기

attributeConverter가 동작한다는 것을 확인했으니 새로운 attributeConverter를 정의해서 테스트해보겠다.

우선 Event 엔티티에 새로운 속성을 추가해보겠다.

 

example.5 Event엔티티에 새로운 속성 추가

@Entity
class Event(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(insertable = false, updatable = false)
    val id: Long? = null,

    @Convert(converter = PeriodStringConverter::class)
    @Column(columnDefinition = "")
    var span: Period,

    @Convert(converter = LocalDateLongConverter::class)
    @Column(columnDefinition = "")
    var tmpDate: LocalDate = LocalDate.now()
)

 

LocalDate 타입인 속성을 추가했다.

LocalDate 타입을 Long 타입으로 변환해서 DB에는 BIGINT로 저장되도록하는 attributeConverter를 정의해보겠다.

 

example.6 LocalDateLongConverter 정의

@Converter
class LocalDateLongConverter : AttributeConverter<LocalDate, Long> {
    override fun convertToDatabaseColumn(attribute: LocalDate?): Long {
        return attribute?.run {
            val split = attribute.toString().split("-")
            (split[0] + split[1] + split[2]).toLong()
        } ?: 0
    }

    override fun convertToEntityAttribute(dbData: Long?): LocalDate {
        return dbData?.run {
            val year = (dbData / 10000).toInt()
            val month = ((dbData % 10000) / 100).toInt()
            val dayOfWeek = (dbData % 100).toInt()
            LocalDate.of(year, month, dayOfWeek)
        } ?: LocalDate.now()
    }

}

 

example7. 영속화 test 코드 작성

@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@DataJpaTest
class AttributeConvertorTest(val em: EntityManager) {
    @Test
    fun testSave() {
        val event = Event(
            span = Period.between(LocalDate.now(), LocalDate.now().plusDays(16)),
            tmpDate = LocalDate.now().plusMonths(2)
        )
        em.persist(event)
        em.flush()
    }
 }

 

 

영속화 테스트 실행 결과

 

로그를 보면 insert 문에 tmp_date라는 칼럼이 추가되어 있고, tmp_date 칼럼에 BIGINT 타입의 값이 들어간 것을 알 수 있다.

테스트코드에서 현재 날짜(2021.10.11)에서 2달 이후의 날짜(2021.12.11)를 tmpDate 값으로 넣었는데.

20211211값이 들어간 것을 확인할 수 있다.

 

 

example8. 조회 test 코드 작성

@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@DataJpaTest
class AttributeConvertorTest(val em: EntityManager) {
    @Test
    @Order(2)
    fun testSelect() {
        val event1 = em.find(Event::class.java, 1L)
        val event2 = em.find(Event::class.java, 2L)
        Assertions.assertEquals(event1.tmpDate, LocalDate.of(2021, 10, 4))
        Assertions.assertEquals(event2.tmpDate, LocalDate.of(2021, 11, 24))
    }
}

 

이제 저장된 내용을 조회하는 코드를 테스트해보겠다.

앞서 application.properties 파일에 spring.datasource.data=classpath:data-h2-test.sql 설정을 추가해서 resources 폴더 하위에 data-h2-test.sql가 테스트 실행 전에 실행되도록 했었는데.

data-h2-test.sql의 insert 문을 tmp_date 항목을 넣을 수 있도록 아래와 같이 수정해줘야한다.

 

insert into event (tmp_date, span, id) values (20211004, 'P6D', 1)
insert into event (tmp_date, span, id) values (20211124, 'P6D', 2)

 

조회 테스트 실행 결과

 

테스트 통과했으며, 로그를 통해서 성공적으로 DB에서 값을 불러온 것을 알 수 있다.

 

 

이번 실습으로 하이버네이트 공식 문서에 기재된 AttributeConverters가 정상적으로 동작한다는 것을 알 수 있었다.
그리고 공식문서에는 JAVA로되어있었지만, Kotlin 언어로 작성해도 성공적으로 작성하는 것을 확인할 수 있었다.

 

반응형