Как требует JPA, @Entity
классы должны иметь конструктор по умолчанию (без аргументов) для создания экземпляров объектов при их извлечении из базы данных.
В Kotlin свойства очень удобно объявлять в основном конструкторе, как в следующем примере:
class Person(val name: String, val age: Int) { /* ... */ }
Но когда конструктор без аргументов объявляется как вторичный, он требует передачи значений для первичного конструктора, поэтому для них необходимы некоторые допустимые значения, как здесь:
@Entity
class Person(val name: String, val age: Int) {
private constructor(): this("", 0)
}
В случае, когда свойства имеют более сложный тип, чем просто String
и, Int
и они не допускают значения NULL, выглядит совершенно плохим предоставление значений для них, особенно когда в первичном конструкторе и init
блоках много кода и когда параметры активно используются - - когда они будут переназначены посредством отражения, большая часть кода будет выполнена снова.
Более того, val
-properties нельзя переназначить после выполнения конструктора, поэтому неизменяемость также теряется.
Возникает вопрос: как адаптировать код Kotlin для работы с JPA без дублирования кода, выбора «волшебных» начальных значений и потери неизменяемости?
PS Верно ли, что Hibernate помимо JPA может создавать объекты без конструктора по умолчанию?
источник
INFO -- org.hibernate.tuple.PojoInstantiator: HHH000182: No default (no-argument) constructor for class: Test (class must be instantiated by Interceptor)
- так что да, Hibernate может работать без конструктора по умолчанию.Ответы:
Начиная с Kotlin 1.0.6 ,
kotlin-noarg
плагин компилятора генерирует синтетические конструкторы по умолчанию для классов, которые были аннотированы выбранными аннотациями.Если вы используете gradle, применения
kotlin-jpa
плагина достаточно, чтобы сгенерировать конструкторы по умолчанию для классов, аннотированных@Entity
:Для Maven:
источник
data class foo(bar: String)
не меняется». Было бы неплохо увидеть более полный пример того, как это работает. Спасибоkotlin-noarg
и содержитkotlin-jpa
ссылки, подробно описывающие их цель. Blog.jetbrains.com/kotlin/2016/12/kotlin-1-0-6-is-here@Embeddable
атрибута, даже если он вам не нужен. Таким образом, его заберутkotlin-jpa
.просто укажите значения по умолчанию для всех аргументов, Kotlin создаст для вас конструктор по умолчанию.
см.
NOTE
рамку под следующим разделом:https://kotlinlang.org/docs/reference/classes.html#secondary-constructors
источник
@ D3xter имеет хороший ответ для одной модели, другая - это новая функция в Kotlin, которая называется
lateinit
:Вы могли бы использовать это, когда уверены, что что-то заполнит значения во время создания или очень скоро после (и перед первым использованием экземпляра).
Вы заметите, что я изменил
age
на,birthdate
потому что вы не можете использовать примитивные значения с,lateinit
и они также должны быть на данный моментvar
(ограничение может быть снято в будущем).Так что не идеальный ответ на неизменность, та же проблема, что и другой ответ в этом отношении. Решением для этого являются плагины к библиотекам, которые могут обрабатывать понимание конструктора Kotlin и сопоставление свойств с параметрами конструктора, вместо того, чтобы требовать конструктор по умолчанию. Модуль Kotlin для Джексона делает это, поэтому очевидно, что это возможно.
См. Также: https://stackoverflow.com/a/34624907/3679676 для изучения похожих вариантов.
источник
lateinit
когда у вас есть четко определенный жизненный цикл, гарантирующий инициализацию вскоре после создания, он предназначен для таких случаев. В то время как делегат больше предназначен для «когда-нибудь перед первым использованием». Хотя технически они имеют схожее поведение и защиту, они не идентичны.false
для Ints и Booleans соответственно. Не уверен, как это повлияет на код фреймворкаНачальные значения требуются, если вы хотите повторно использовать конструктор для разных полей, kotlin не допускает нулей. Поэтому всякий раз, когда вы планируете опустить поле, используйте эту форму в конструкторе:
var field: Type? = defaultValue
jpa не требует конструктора аргументов:
нет дублирования кода. Если вам нужно построить объект и установить только возраст, используйте эту форму:
магии нет (просто прочтите документацию)
источник
Невозможно сохранить таким образом неизменность. Vals ДОЛЖЕН быть инициализирован при создании экземпляра.
Один из способов сделать это без неизменности:
источник
Я довольно давно работаю с Kotlin + JPA, и у меня возникла собственная идея, как писать классы Entity.
Я лишь немного расширил вашу первоначальную идею. Как вы сказали, мы можем создать частный конструктор без аргументов и предоставить значения по умолчанию для примитивов , но когда мы пытаемся использовать другие классы, это становится немного беспорядочным. Моя идея - создать статический объект STUB для класса сущности, который вы сейчас пишете, например:
и когда у меня есть класс сущности, связанный с TestEntity, я могу легко использовать только что созданную заглушку. Например:
Конечно, это решение не идеально. Вам все еще нужно создать шаблонный код, который не требуется. Также есть один случай, который не может быть решен с помощью заглушки - отношения родитель-потомок в одном классе сущностей - например:
Этот код сгенерирует исключение NullPointerException из-за проблемы с куриным яйцом - нам нужен STUB для создания STUB. К сожалению, нам нужно сделать это поле допускающим значение NULL (или какое-либо подобное решение), чтобы код работал.
Также, на мой взгляд, наличие Id в качестве последнего поля (и допускающего значение NULL) вполне оптимально. Мы не должны назначать его вручную и позволять базе данных делать это за нас.
Я не говорю, что это идеальное решение, но я думаю, что оно использует удобочитаемость кода объекта и функции Kotlin (например, нулевую безопасность). Я просто надеюсь, что будущие выпуски JPA и / или Kotlin сделают наш код еще проще и приятнее.
источник
Как указано выше, вы должны использовать не
no-arg
плагин, предоставляемый Jetbrains.Если вы используете Eclispe, вам, возможно, придется отредактировать настройки компилятора Kotlin.
Окно> Настройки> Котлин> Компилятор
Активируйте
no-arg
плагин в разделе Compiler Plugins.См. Https://discuss.kotlinlang.org/t/kotlin-allopen-plugin-doesnt-work-with-sts/13277/10
источник
Я сам болван, но, похоже, вам нужно явно указать инициализатор и вернуться к нулевому значению, как это
источник
Подобно @pawelbial, я использовал объект-компаньон для создания экземпляра по умолчанию, однако вместо определения вторичного конструктора просто используйте аргументы конструктора по умолчанию, такие как @iolo. Это избавляет вас от необходимости определять несколько конструкторов и упрощает код (хотя и разрешено, определение сопутствующих объектов "STUB" не совсем упрощает его)
А затем для классов, относящихся к
TestEntity
Как упоминалось в @pawelbial, это не сработает, если
TestEntity
класс «имеет»TestEntity
класс, поскольку STUB не будет инициализирован при запуске конструктора.источник
Эти строки сборки Gradle мне помогли:
https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.jpa/1.1.50 .
По крайней мере, он встроен в IntelliJ. На данный момент он не работает в командной строке.
И у меня есть
и
var path: LtreeType не работает.
источник
Если вы добавили плагин gradle https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.jpa, но не работал, скорее всего, версия устарела . Я был на 1.3.30, и у меня это не сработало. После того, как я обновился до 1.3.41 (последней на момент написания), все заработало.
Примечание: версия kotlin должна быть такой же, как у этого плагина, например: вот как я добавил оба:
источник