모든 실습 코드는 필자의 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)
}
}
@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 언어로 작성해도 성공적으로 작성하는 것을 확인할 수 있었다.