Gemäß JPA @Entity
sollten Klassen über einen Standardkonstruktor (ohne Argumente) verfügen, um die Objekte beim Abrufen aus der Datenbank zu instanziieren.
In Kotlin lassen sich Eigenschaften sehr bequem im primären Konstruktor deklarieren, wie im folgenden Beispiel:
class Person(val name: String, val age: Int) { /* ... */ }
Wenn der Nicht-Arg-Konstruktor jedoch als sekundärer deklariert wird, müssen Werte für den primären Konstruktor übergeben werden, sodass einige gültige Werte für sie erforderlich sind, wie hier:
@Entity
class Person(val name: String, val age: Int) {
private constructor(): this("", 0)
}
Für den Fall, dass die Eigenschaften einen komplexeren Typ als nur haben String
und Int
nicht nullwertfähig sind, sieht es völlig schlecht aus, die Werte für sie anzugeben, insbesondere wenn der primäre Konstruktor und die init
Blöcke viel Code enthalten und die Parameter aktiv verwendet werden. - Wenn sie durch Reflexion neu zugewiesen werden sollen, wird der größte Teil des Codes erneut ausgeführt.
Darüber hinaus können val
Eigenschaften nach der Ausführung des Konstruktors nicht neu zugewiesen werden, sodass auch die Unveränderlichkeit verloren geht.
Die Frage ist also: Wie kann Kotlin-Code für die Arbeit mit JPA ohne Codeduplizierung angepasst werden, indem "magische" Anfangswerte ausgewählt werden und die Unveränderlichkeit verloren geht?
PS Stimmt es, dass der Ruhezustand neben JPA Objekte ohne Standardkonstruktor erstellen kann?
quelle
INFO -- org.hibernate.tuple.PojoInstantiator: HHH000182: No default (no-argument) constructor for class: Test (class must be instantiated by Interceptor)
- Ja, der Ruhezustand kann ohne den Standardkonstruktor funktionieren.Antworten:
Ab Kotlin 1.0.6
kotlin-noarg
generiert das Compiler-Plugin synthetische Standardkonstrutoren für Klassen, die mit ausgewählten Annotationen versehen wurden.Wenn Sie gradle verwenden, reicht das Anwenden des
kotlin-jpa
Plugins aus, um Standardkonstruktoren für Klassen zu generieren, die mit@Entity
folgenden Anmerkungen versehen sind :Für Maven:
quelle
data class foo(bar: String)
ändert sich nicht" handelt. Es wäre einfach schön, ein vollständigeres Beispiel dafür zu sehen, wie dies zusammenpasst. Dankekotlin-noarg
undkotlin-jpa
mit Links, die ihren Zweck beschreiben. Blog.jetbrains.com/kotlin/2016/12/kotlin-1-0-6-is-here@Embeddable
Attribut markieren , auch wenn Sie es sonst nicht benötigen. Auf diese Weise wird es von abgeholtkotlin-jpa
.Geben Sie einfach Standardwerte für alle Argumente an. Kotlin erstellt einen Standardkonstruktor für Sie.
Siehe das
NOTE
Feld unter dem folgenden Abschnitt:https://kotlinlang.org/docs/reference/classes.html#secondary-constructors
quelle
@ D3xter hat eine gute Antwort für ein Modell, das andere ist eine neuere Funktion in Kotlin mit dem Namen
lateinit
:Sie würden dies verwenden, wenn Sie sicher sind, dass etwas die Werte zum Zeitpunkt der Erstellung oder sehr bald danach (und vor der ersten Verwendung der Instanz) ausfüllt.
Sie werden feststellen, dass ich zu geändert habe
age
,birthdate
weil Sie keine primitiven Werte mit verwenden könnenlateinit
und dies auch im Moment sein mussvar
(die Einschränkung wird möglicherweise in Zukunft aufgehoben).Also keine perfekte Antwort für Unveränderlichkeit, das gleiche Problem wie die andere Antwort in dieser Hinsicht. Die Lösung hierfür sind Plugins für Bibliotheken, die das Verständnis des Kotlin-Konstruktors und die Zuordnung von Eigenschaften zu Konstruktorparametern übernehmen können, anstatt einen Standardkonstruktor zu benötigen. Das Kotlin-Modul für Jackson macht das, also ist es eindeutig möglich.
Weitere Informationen zu ähnlichen Optionen finden Sie unter: https://stackoverflow.com/a/34624907/3679676 .
quelle
lateinit
wenn Sie einen genau definierten Lebenszyklus haben, der die Initialisierung kurz nach der Erstellung garantiert. Dies ist für diese Fälle vorgesehen. Während Delegate eher für "irgendwann vor dem ersten Gebrauch" gedacht ist. Obwohl sie technisch gesehen ein ähnliches Verhalten und einen ähnlichen Schutz aufweisen, sind sie nicht identisch.false
vorstellen, beim Instanziieren eines Objekts "Standardwerte" zu verwenden. Damit meine ich die Verwendung von 0 und für Ints bzw. Booleans. IchAnfangswerte sind erforderlich, wenn Sie den Konstruktor für verschiedene Felder wiederverwenden möchten. Kotlin erlaubt keine Nullen. Wenn Sie also ein Feld weglassen möchten, verwenden Sie dieses Formular im Konstruktor:
var field: Type? = defaultValue
jpa benötigt keinen Argumentkonstruktor:
Es gibt keine Codeduplizierung. Wenn Sie eine Konstruktionsentität und nur ein Einrichtungsalter benötigen, verwenden Sie dieses Formular:
Es gibt keine Magie (lesen Sie einfach die Dokumentation)
quelle
Es gibt keine Möglichkeit, diese Unveränderlichkeit so zu halten. Vals MUSS beim Erstellen der Instanz initialisiert werden.
Ein Weg, dies ohne Unveränderlichkeit zu tun, ist:
quelle
Ich arbeite seit einiger Zeit mit Kotlin + JPA und habe meine eigene Idee entwickelt, wie man Entitätsklassen schreibt.
Ich möchte Ihre ursprüngliche Idee nur geringfügig erweitern. Wie Sie sagten, können wir einen privaten argumentlosen Konstruktor erstellen und Standardwerte für Grundelemente bereitstellen. Wenn wir jedoch versuchen, andere Klassen zu verwenden, wird dies etwas chaotisch. Meine Idee ist es, ein statisches STUB-Objekt für eine Entitätsklasse zu erstellen, die Sie derzeit schreiben, z.
und wenn ich eine Entitätsklasse habe, die mit TestEntity zusammenhängt, kann ich einfach den gerade erstellten Stub verwenden. Beispielsweise:
Natürlich ist diese Lösung nicht perfekt. Sie müssen noch einen Code für das Boilerplate erstellen, der nicht erforderlich sein sollte. Es gibt auch einen Fall, der mit Stubbing - Eltern-Kind-Beziehung innerhalb einer Entitätsklasse - nicht gut gelöst werden kann:
Dieser Code wird produzieren Nullpointer aufgrund Henne-Ei - Problem - wir STUB brauchen STUB zu erstellen. Leider müssen wir dieses Feld nullbar machen (oder eine ähnliche Lösung), damit Code funktioniert.
Auch meiner Meinung nach ist es ziemlich optimal , Id als letztes Feld (und nullbar) zu haben. Wir sollten es nicht von Hand zuweisen und die Datenbank es für uns tun lassen.
Ich sage nicht, dass dies eine perfekte Lösung ist, aber ich denke, dass sie die Lesbarkeit von Entitätscode und Kotlin-Funktionen (z. B. Null-Sicherheit) nutzt. Ich hoffe nur, dass zukünftige Versionen von JPA und / oder Kotlin unseren Code noch einfacher und schöner machen.
quelle
Wie oben erwähnt, müssen Sie
no-arg
das von Jetbrains bereitgestellte No- Plugin verwenden.Wenn Sie Eclispe verwenden, müssen Sie möglicherweise die Kotlin Compiler-Einstellungen bearbeiten.
Fenster> Einstellungen> Kotlin> Compiler
Aktivieren Sie das
no-arg
Plugin im Abschnitt Compiler Plugins.Siehe: https://discuss.kotlinlang.org/t/kotlin-allopen-plugin-doesnt-work-with-sts/13277/10
quelle
Ich bin selbst ein Nub, aber anscheinend müssen Sie den Initialisierer explizit initialisieren und auf einen solchen Nullwert zurückgreifen
quelle
Ähnlich wie bei @pawelbial habe ich ein Begleitobjekt verwendet, um eine Standardinstanz zu erstellen. Anstatt jedoch einen sekundären Konstruktor zu definieren, verwenden Sie einfach Standardkonstruktorargumente wie @iolo. Dies erspart Ihnen die Definition mehrerer Konstruktoren und vereinfacht den Code (obwohl dies selbstverständlich ist, ist die Definition von "STUB" -Begleitobjekten nicht gerade einfach).
Und dann für Klassen, die sich beziehen
TestEntity
Wie @pawelbial erwähnt hat, funktioniert dies nicht, wenn die
TestEntity
Klasse eine Klasse "hat"TestEntity
, da STUB beim Ausführen des Konstruktors nicht initialisiert wurde.quelle
Diese Gradle-Build-Linien haben mir geholfen:
https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.jpa/1.1.50 .
Zumindest baut es in IntelliJ. Es schlägt momentan in der Kommandozeile fehl.
Und ich habe eine
und
var path: LtreeType hat nicht funktioniert.
quelle
Wenn Sie das Gradle-Plugin https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.jpa hinzugefügt haben , aber nicht funktioniert haben, ist die Version wahrscheinlich veraltet. Ich war am 1.3.30 und es hat bei mir nicht funktioniert. Nachdem ich auf 1.3.41 (spätestens zum Zeitpunkt des Schreibens) aktualisiert hatte, funktionierte es.
Hinweis: Die Kotlin-Version sollte mit diesem Plugin identisch sein, z. B.: So habe ich beide hinzugefügt:
quelle