Принудительное встраивание встроенного класса 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 : определив оболочку, мы можем ограничить область действия наших свойств расширения и при этом заставить их вести себя как обычные старые, не платя никаких накладных расходов на обертку.
Т.е. для нашегоCharacteristic
s, мы можем определить
@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>
интерфейс - Поместите соответствующие методы непосредственно в этот интерфейс или в качестве методов расширения.
- Используйте стратегию генератора , чтобы убедиться, что все соответствующие таблицы реализуют этот интерфейс.