반응형
예제소스는 깃허브에 있습니다.
ResultSet 을 직접 사용할 일은 거의 없을 것이다. ORM 이 잘 처리해 주고 있기 때문..
하지만 요새는 JPA 말고 코틀린 진영에서 lightweight but powerful 한 ORM 프레임워크도 종종 사용이 되는 것 같은데
exposed 라는 것이다.
https://github.com/JetBrains/Exposed
spring starter 도 있어서 @Transactional 어노테이션과 혼용해서 사용도 가능하고, 무척 괜찮아 보인다.
exposed는 트랜잭션을 AOP 기반 어노테이션 방식이 아닌 functional 하게 관리할 수 있고, native query 도 쉽게 잘 쓸 수 있다.
여기서 ResultSet 을 얻어올 수 있는데 iterable을 돌리는 방식보다 재귀 & 리플렉션을 사용해서 List로 변환할 수 있을 것 같아 짜봤다.
fun <T> recursiveExtract(resultSet: ResultSet, clazz: Class<T>): List<T> {
// tailrec sub 함수를 사용하기 때문에 스택 낭비가 없다.
tailrec fun recursiveExtract_(resultSet: ResultSet, list: LinkedList<T>): List<T> {
return if (resultSet.next()) {
list.add(resultSet.mapTo(clazz))
recursiveExtract_(resultSet, list)
} else {
list
}
}
return recursiveExtract_(resultSet, LinkedList())
}
fun <T> ResultSet.mapTo(clazz: Class<T>): T {
val constructor = clazz.getDeclaredConstructor(*clazz.declaredFields.map { it.type }.toTypedArray())
val dataList = clazz.declaredFields.map {
val nameField = clazz.getDeclaredField(it.name)
nameField.isAccessible = true
this.getObject(it.name, nameField.type)
}
return constructor.newInstance(*dataList.toTypedArray())
}
코틀린이니 refied 를 써서 Class<T> 의 전달을 없앨 수도 있다.
inline fun <reified T> ResultSet.toList() = recursiveExtract(this, T::class.java)
테스트를 돌려보면
data class CityDto(val id: Int, val name: String)
class EntityTest {
@Test
fun test1() {
Database.connect(dataSource)
transaction {
// print sql to std-out
addLogger(
StdOutSqlLogger,
// Slf4jSqlDebugLogger
)
SchemaUtils.create(Cities)
// insert new city. SQL: INSERT INTO Cities (name) VALUES ('St. Petersburg')
val stPeteId = Cities.insert {
it[name] = "St. Petersburg"
} get Cities.id
val cityDtoList_with_no_refied = exec("SELECT * FROM Cities") { rs -> recursiveExtract(rs, CityDto::class.java) } ?: listOf()
// reified 가 인식되려면 변수에 타입을 명시적으로 지정해야 한다.
val cityDtoList_withrefied: List<CityDto> = exec("SELECT * FROM Cities") { rs -> rs.toList() } ?: listOf()
println(cityDtoList_with_no_refied)
println(cityDtoList_withrefied)
}
}
}
쿼리 로깅과 CityDto 의 결과가 잘 나왔다.
native 쿼리 말고도 exposed 에는 쉽고 다양하게 dsl query 도 만들 수 있고,
JPA 에서는 하기 어려운 batch insert, batch update 등의 기능도 사용할 수 있다고 하니
다음 신규 프로젝트가 있다면, 충분히 고려해 볼 만한 기술인 것 같다.
반응형
'코틀린' 카테고리의 다른 글
코루틴을 사용한 동시성, 병렬처리 - 2 (0) | 2022.01.14 |
---|---|
코루틴을 사용한 동시성, 병렬처리 - 1 (2) | 2022.01.14 |
QueryDsl 코틀린으로 안전하게 쓰기 (0) | 2022.01.11 |
Kotlin Exposed (orm) 사용해보기 (0) | 2022.01.09 |
코틀린으로 Functional 하게 테스트 코드 짜기 (0) | 2021.08.13 |
댓글