как преобразовать kotline Exposed ResultSet в Entity?

Я использую kotlin Exposed в стиле DAO и имею код, как показано ниже. здесь узлы - это таблица открытых, а узел - это объект, который создается из таблицы узлов и имеет длинный идентификатор.

    transaction {
        TransactionManager.current().exec(
            "select * from nodes " + 
            "where date_part('microseconds', updated_at - last_deployed) > 1000 " + 
            "order by last_change_status asc " + 
            "limit " + max + " offset " + offset
        ) { 
            rs -> while (rs.next()) {
                // Question: how I can convert rs to Node object correctly???
                nodes.add(Node.wrap(EntityID(rs.getLong("id"), Nodes), ResultRow.create(rs, Nodes.columns)))
            }
        }

поскольку Kotlin, похоже, не может правильно обрабатывать присвоение отметок времени в микросекундах (кажется, что оно округлено с точностью до миллисекунд), я должен использовать необработанный SQL, чтобы игнорировать разницу в миллисекунды.

Я могу правильно получить ResultSet, но мне нужно преобразовать ResultSet в целевую Entity, чтобы использовать результат в другой части кода.

nodes.add(Node.wrap(EntityID(rs.getLong("id"), Nodes), ResultRow.create(rs, Nodes.columns)))

передает компиляцию, но когда я действительно обращаюсь к свойству (например, podName) объекта Node, возникает следующее исключение. обратите внимание, что если я использую обычный способ выбора объекта, например nodes = Node.find{ Nodes.lastDeployed less Nodes.updateAt }.limit(max, offset).asSequence().toList(), ошибка исчезла.

Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:50)
    at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)
Caused by: java.lang.IllegalStateException: No transaction in context.
    at org.jetbrains.exposed.sql.transactions.TransactionManager$Companion.current(TransactionApi.kt:98)
    at org.jetbrains.exposed.sql.Query.getTransaction(Query.kt:16)
    at org.jetbrains.exposed.sql.Query.iterator(Query.kt:214)
    at kotlin.collections.CollectionsKt___CollectionsKt.firstOrNull(_Collections.kt:231)
    at org.jetbrains.exposed.dao.Entity.getReadValues(Entity.kt:25)
    at org.jetbrains.exposed.dao.Entity.lookup(Entity.kt:107)
    at org.jetbrains.exposed.dao.Entity.getValue(Entity.kt:88)
    at com.mydomain.models.Node.getPodName(Node.kt)

Объект узла, созданный Node.wrap(EntityID(rs.getLong("id"), Nodes), ResultRow.create(rs, Nodes.columns)), кажется, находится в несогласованном состоянии и рассматривается как несвежий (я думаю, это причина, по которой Exposed снова пытается получить доступ к базе данных) ...

у кого-нибудь есть такой же опыт (преобразовать необработанный результат SQL в список Exposed Entity)?

С уважением,


person takehiro iyatomi    schedule 08.07.2020    source источник
comment
Вы пробовали wrapRows? Пример показан здесь: github. ru / JetBrains / Exposed / wiki /   -  person JCamacho    schedule 13.08.2020


Ответы (1)


У меня была такая же проблема, и я решил ее благодаря подсказке JCamacho. Ключ состоит в том, чтобы использовать wrapRow вместе с ResultRow.create для сопоставления простого sql с сущностью.

Вот код, который я использую сейчас (учитывая, что у меня есть IntEntity Task и IntIdTable TaskEntity:

("SELECT ${TaskEntity.allFields} " +
        "FROM ${TaskEntity.tableName} " +
        "WHERE ..."
).execAndMap {
    Task.wrapRow(ResultRow.create(it, TaskEntity.fields))
}

со следующим методом, взятым из https://github.com/JetBrains/Exposed/wiki/FAQ#q-is-it-possible-to-use-native-sql--sql-as-a-string < / а>

fun <T : Any> String.execAndMap(transform: (ResultSet) -> T): List<T> {
    val result = arrayListOf<T>()
    TransactionManager.current().exec(this) { rs ->
       while (rs.next()) {
          result += transform(rs)
       }
    }
    return result
}

И следующее расширение для получения полей выбора (вы также можете использовать *, но тогда вам нужно убедиться, что поля определены в том же порядке, что и в вашей базе данных)

val IntIdTable.allFields: String get() {
    return fields.map { "$tableName.`${(it as Column<*>).name}`" }.joinToString(", ")
}
person Phoca    schedule 07.03.2021