Принудительное встраивание встроенного класса Kotlin?

@LukasEder, этот вопрос больше связан с Котлином, вы, вероятно, можете пропустить его в пользу продолжения, более связанного с jOOQ, в https://github.com/jOOQ/jOOQ/issues/14972 :)

Таким образом, часть устаревшей базы данных имеет довольно сложную конструкцию, но мы не можем и не хотим менять ее в рамках перехода от JPA к jOOQ и хотим оставить это на потом.

Для примера (одного из, к сожалению, множества): у нас есть s. Не так уж и важно, что они делают, просто они напечатаны.

Поскольку они выглядят очень идентично и поскольку в какой-то момент нам может потребоваться упростить базу данных, нам не хочется писать для них несколько запросов.

Решение, на котором мы остановились, состоит в том, чтобы определить сопоставление, которое предоставит нам правильную таблицу определенного типа.

тоесть что-то вроде

      @Bean(CHARACTERISTIC_TABLE)
fun characteristicTable(): Map<CharacteristicType, Table<*>> = mapOf(
    CharacteristicType.INTEGER to INTEGER_CHARACTERISTIC,
    CharacteristicType.FLOAT to FLOAT_CHARACTERISTIC,
    CharacteristicType.TEXT to TEXT_CHARACTERISTIC,
    CharacteristicType.NOMINAL to NOMINAL_CHARACTERISTIC,
    CharacteristicType.ORDINAL to ORDINAL_CHARACTERISTIC,
    CharacteristicType.BINARY_PDF to BINARY_CHARACTERISTIC,
    CharacteristicType.BINARY_IMAGE to BINARY_CHARACTERISTIC,
    CharacteristicType.TABULAR to GROUP_CHARACTERISTIC
)

это мы можем внедрить в наши репозитории и получить из него объект на основеCharacteristicType.

Очевидно, он ничего не знает о своих полях, поэтому нам приходится самостоятельно определять столбцы, которые они разделяют. Например

      private val Table<*>.ID get() = this.checkField<Int>("id")
private val Table<*>.NAME get() = this.checkField<String>("name")

где

      inline fun <reified T : Any> Table<*>.checkField(fieldName: String): Field<T> =
    checkNotNull(this.field(fieldName, T::class.javaObjectType)) {
        "Table [$this] lacks the '$fieldName' column."
    }

Работает, но проблема, конечно, в том, что теперь ВСЕ таблицы имеют, например,.NAMEстолбцы, даже если они на самом деле не имеютnameстолбец, потому что все они соответствуют типу .

Введите классы значений Kotlin : определив оболочку, мы можем ограничить область действия наших свойств расширения и при этом заставить их вести себя как обычные старые, не платя никаких накладных расходов на обертку.

Т.е. для нашегоCharacteristics, мы можем определить

      @JvmInline
value class CharacteristicTable<T : Record>(val value: Table<T>) : Table<T> by value

оберните наши таблицы на карте

      @Bean(CHARACTERISTIC_TABLE)
fun characteristicTable(): Map<CharacteristicType, CharacteristicTable<*>> = mapOf(
    CharacteristicType.INTEGER to CharacteristicTable(INTEGER_CHARACTERISTIC),
    CharacteristicType.FLOAT to CharacteristicTable(FLOAT_CHARACTERISTIC),
    CharacteristicType.TEXT to CharacteristicTable(TEXT_CHARACTERISTIC),
    CharacteristicType.NOMINAL to CharacteristicTable(NOMINAL_CHARACTERISTIC),
    CharacteristicType.ORDINAL to CharacteristicTable(ORDINAL_CHARACTERISTIC),
    CharacteristicType.BINARY_PDF to CharacteristicTable(BINARY_CHARACTERISTIC),
    CharacteristicType.BINARY_IMAGE to CharacteristicTable(BINARY_CHARACTERISTIC),
    CharacteristicType.TABULAR to CharacteristicTable(GROUP_CHARACTERISTIC)
)

а затем правильно определите область наших свойств расширения в

      private val CharacteristicTable<*>.ID get() = this.checkField<Int>("id")
private val CharacteristicTable<*>.NAME get() = this.checkField<String>("name")
private val CharacteristicTable<*>.MACHINE_CODE get() = this.checkField<String>("machine_code")
[...]

продолжая использовать их в качествеTable<*>они заключаются в наши запросы:

      val characteristic: CharacteristicTable<*> =  [...]
val ctx: DSLContext = [...]

[...]

val intId = ctx
    .insertInto(characteristic) //our wrapped table, used to be the actual Table<*>
    .columns(*insertedColumns)
    .values(*insertedValues)
    .onConflict(characteristic.MACHINE_CODE)
    .doUpdate()
    .setAllToExcluded()
    .returning(characteristic.ID)
    .fetchSingle { it[characteristic.ID] }

[...]

... или я так думал.

Проблема, если мы это сделаем, заключается в том, что приведенный выше запрос выдаст

      java.lang.ClassCastException: class my.app.db.config.QualifiedMaps$CharacteristicTable cannot be cast to class org.jooq.QueryPartInternal (my.app.db.config.QualifiedMaps$CharacteristicTable and org.jooq.QueryPartInternal are in unnamed module of loader 'app')
    at org.jooq_3.18.0.POSTGRES.debug(Unknown Source)
    at org.jooq.impl.AbstractContext.visit(AbstractContext.java:292)
    at org.jooq.impl.InsertQueryImpl.lambda$toSQLInsert$11(InsertQueryImpl.java:700)
    at org.jooq.impl.AbstractContext.toggle(AbstractContext.java:379)
    at org.jooq.impl.AbstractContext.declareTables(AbstractContext.java:623)
    at org.jooq.impl.InsertQueryImpl.toSQLInsert(InsertQueryImpl.java:700)
    at org.jooq.impl.InsertQueryImpl.lambda$accept0$1(InsertQueryImpl.java:389)
    at org.jooq.impl.AbstractContext.toggle(AbstractContext.java:393)
    at org.jooq.impl.AbstractContext.data(AbstractContext.java:404)
    at org.jooq.impl.InsertQueryImpl.accept0(InsertQueryImpl.java:389)
    at org.jooq.impl.AbstractDMLQuery.accept(AbstractDMLQuery.java:670)
    at org.jooq.impl.DefaultRenderContext.visit0(DefaultRenderContext.java:726)
    at org.jooq.impl.AbstractContext.visit(AbstractContext.java:350)
    at org.jooq.impl.AbstractQuery.getSQL0(AbstractQuery.java:491)
    at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:300)
    at org.jooq.impl.AbstractDMLQueryAsResultQuery.fetch(AbstractDMLQueryAsResultQuery.java:140)
    at org.jooq.impl.ResultQueryTrait.fetchLazy(ResultQueryTrait.java:281)
    at org.jooq.impl.ResultQueryTrait.fetchLazyNonAutoClosing(ResultQueryTrait.java:290)
    at org.jooq.impl.ResultQueryTrait.fetchSingle(ResultQueryTrait.java:605)
    at org.jooq.impl.ResultQueryTrait.fetchSingle(ResultQueryTrait.java:610)

Почему же класс Kotlin не встроен?

Из документации мы знаем:

Как правило, встроенные классы упаковываются всякий раз, когда они используются как другой тип.

Ну вот и наш ответ (вроде как).

Мы, конечно, можем распаковать его вручную, но это противоречит цели использования класса значений...

Можем ли мы сделать что-нибудь с классом, чтобы заставить Kotlin всегда встраивать его?

1 ответ

Это не отвечает на ваш актуальный вопрос о встраиваемых типах, но я думаю, что вашу проблему гораздо лучше решить по-другому, как я также говорил о проблеме № 14972, которую вы создали и связали.

Вместо того, чтобы оборачивать сгенерированную таблицу jOOQ, просто позвольте сгенерированной таблице jOOQ расширить ваш тип:

Вы могли бы:

  • УкажитеCharacteristicsTable<R : Record> : Table<R>интерфейс
  • Поместите соответствующие методы непосредственно в этот интерфейс или в качестве методов расширения.
  • Используйте стратегию генератора , чтобы убедиться, что все соответствующие таблицы реализуют этот интерфейс.
Другие вопросы по тегам