Comme JPA l'exige, les @Entity
classes doivent avoir un constructeur par défaut (non arg) pour instancier les objets lors de leur récupération à partir de la base de données.
Dans Kotlin, les propriétés sont très pratiques à déclarer dans le constructeur principal, comme dans l'exemple suivant:
class Person(val name: String, val age: Int) { /* ... */ }
Mais lorsque le constructeur non-arg est déclaré en tant que constructeur secondaire, il nécessite la transmission de valeurs pour le constructeur principal, donc certaines valeurs valides sont nécessaires pour eux, comme ici:
@Entity
class Person(val name: String, val age: Int) {
private constructor(): this("", 0)
}
Dans le cas où les propriétés ont un type plus complexe que juste String
et Int
et qu'elles ne sont pas Nullable, il semble totalement mauvais de leur fournir les valeurs, surtout quand il y a beaucoup de code dans le constructeur principal et les init
blocs et lorsque les paramètres sont activement utilisés - - quand ils doivent être réaffectés par réflexion, la plupart du code sera à nouveau exécuté.
De plus, val
-properties ne peut pas être réaffecté après l'exécution du constructeur, donc l'immuabilité est également perdue.
La question est donc: comment adapter le code Kotlin pour fonctionner avec JPA sans duplication de code, en choisissant des valeurs initiales "magiques" et en perte d'immuabilité?
PS Est-il vrai que Hibernate en dehors de JPA peut construire des objets sans constructeur par défaut?
la source
INFO -- org.hibernate.tuple.PojoInstantiator: HHH000182: No default (no-argument) constructor for class: Test (class must be instantiated by Interceptor)
- donc, oui, Hibernate peut fonctionner sans le constructeur par défaut.Réponses:
Depuis Kotlin 1.0.6 , le
kotlin-noarg
plugin du compilateur génère des constructeurs synthétiques par défaut pour les classes qui ont été annotées avec des annotations sélectionnées.Si vous utilisez gradle, l'application du
kotlin-jpa
plugin suffit à générer des constructeurs par défaut pour les classes annotées avec@Entity
:Pour Maven:
la source
data class foo(bar: String)
ne change pas". Ce serait juste bien de voir un exemple plus complet de la façon dont cela se met en place. Mercikotlin-noarg
etkotlin-jpa
avec des liens détaillant leur objectif blog.jetbrains.com/kotlin/2016/12/kotlin-1-0-6-is-here@Embeddable
attribut même si vous n'en avez pas besoin autrement. De cette façon, il sera récupéré parkotlin-jpa
.fournissez simplement des valeurs par défaut pour tous les arguments, Kotlin créera un constructeur par défaut pour vous.
voir l'
NOTE
encadré ci-dessous la section suivante:https://kotlinlang.org/docs/reference/classes.html#secondary-constructors
la source
@ D3xter a une bonne réponse pour un modèle, l'autre est une fonctionnalité plus récente de Kotlin appelée
lateinit
:Vous utiliseriez ceci lorsque vous êtes sûr que quelque chose remplira les valeurs au moment de la construction ou très peu de temps après (et avant la première utilisation de l'instance).
Vous remarquerez que j'ai changé
age
enbirthdate
parce que vous ne pouvez pas utiliser de valeurs primitives aveclateinit
et elles doivent aussi pour le moment êtrevar
(une restriction pourrait être libérée à l'avenir).Donc pas une réponse parfaite pour l'immuabilité, même problème que l'autre réponse à cet égard. La solution pour cela est des plugins pour les bibliothèques qui peuvent gérer la compréhension du constructeur Kotlin et le mappage des propriétés aux paramètres du constructeur, au lieu de nécessiter un constructeur par défaut. Le module Kotlin pour Jackson fait cela, donc c'est clairement possible.
Voir également: https://stackoverflow.com/a/34624907/3679676 pour l'exploration d'options similaires.
la source
lateinit
lorsque vous avez un cycle de vie bien défini garantissant l'initialisation peu de temps après la construction, il est destiné à ces cas. Alors que le délégué est plus destiné à «quelque temps avant la première utilisation». Bien que techniquement, ils aient un comportement et une protection similaires, ils ne sont pas identiques.false
pour Ints et Booleans respectivement. Je ne sais pas comment cela affecterait le code du frameworkLes valeurs initiales sont obligatoires si vous souhaitez réutiliser le constructeur pour différents champs, kotlin n'autorise pas les valeurs nulles. Ainsi, chaque fois que vous prévoyez d'omettre le champ, utilisez ce formulaire dans le constructeur:
var field: Type? = defaultValue
jpa ne nécessite aucun constructeur d'argument:
il n'y a pas de duplication de code. Si vous avez besoin d'une entité de construction et uniquement de l'âge de configuration, utilisez ce formulaire:
il n'y a pas de magie (il suffit de lire la documentation)
la source
Il n'y a aucun moyen de garder l'immuabilité comme ça. Les valeurs Vals DOIVENT être initialisées lors de la construction de l'instance.
Une façon de le faire sans immuabilité est:
la source
Je travaille avec Kotlin + JPA depuis un certain temps et j'ai créé ma propre idée sur la façon d'écrire des classes Entity.
J'étend juste légèrement votre idée initiale. Comme vous l'avez dit, nous pouvons créer un constructeur privé sans argument et fournir des valeurs par défaut pour les primitives , mais lorsque nous essayons d'utiliser une autre classe, cela devient un peu compliqué. Mon idée est de créer un objet STUB statique pour la classe d'entité que vous écrivez actuellement, par exemple:
et quand j'ai une classe d'entité liée à TestEntity, je peux facilement utiliser le stub que je viens de créer. Par exemple:
Bien sûr, cette solution n'est pas parfaite. Vous devez toujours créer un code standard qui ne devrait pas être requis. Il y a aussi un cas qui ne peut pas être résolu correctement avec le stubbing - relation parent-enfant dans une classe d'entité - comme ceci:
Ce code produira NullPointerException raison d'un problème d'oeuf de poule - nous avons besoin de STUB pour créer STUB. Malheureusement, nous devons rendre ce champ nullable (ou une solution similaire) pour que le code fonctionne.
De plus, à mon avis, avoir Id comme dernier champ (et nullable) est tout à fait optimal. Nous ne devrions pas l'attribuer à la main et laisser la base de données le faire pour nous.
Je ne dis pas que c'est une solution parfaite, mais je pense qu'elle exploite la lisibilité du code d'entité et les fonctionnalités de Kotlin (par exemple, la sécurité nulle). J'espère juste que les futures versions de JPA et / ou Kotlin rendront notre code encore plus simple et plus agréable.
la source
Comme indiqué ci-dessus, vous devez utiliser le no
no-arg
plugin fourni par Jetbrains.Si vous utilisez Eclispe vous devrez peut-être modifier les paramètres du compilateur Kotlin.
Fenêtre> Préférences> Kotlin> Compilateur
Activez le
no-arg
plugin dans la section Plugins du compilateur.Voir: https://discuss.kotlinlang.org/t/kotlin-allopen-plugin-doesnt-work-with-sts/13277/10
la source
Je suis moi-même un nub mais il semble que vous deviez explicitement initialiser et revenir à une valeur nulle comme celle-ci
la source
Similaire à @pawelbial, j'ai utilisé un objet compagnon pour créer une instance par défaut, mais au lieu de définir un constructeur secondaire, utilisez simplement des arguments de constructeur par défaut comme @iolo. Cela vous évite d'avoir à définir plusieurs constructeurs et simplifie le code (bien que cela soit accordé, la définition des objets compagnons "STUB" n'est pas exactement la simplicité)
Et puis pour les cours qui concernent
TestEntity
Comme @pawelbial l'a mentionné, cela ne fonctionnera pas là où la
TestEntity
classe "a une"TestEntity
classe puisque STUB n'aura pas été initialisé lorsque le constructeur est exécuté.la source
Ces lignes de construction Gradle m'ont aidé:
https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.jpa/1.1.50 .
Au moins, il se construit dans IntelliJ. Il échoue sur la ligne de commande pour le moment.
Et j'ai un
et
var chemin: LtreeType n'a pas fonctionné.
la source
Si vous avez ajouté le plugin gradle https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.jpa mais n'a pas fonctionné, il y a de fortes chances que la version soit obsolète . J'étais sur 1.3.30 et cela n'a pas fonctionné pour moi. Après la mise à niveau vers la version 1.3.41 (la dernière au moment de la rédaction), cela a fonctionné.
Remarque: la version de kotlin doit être la même que ce plugin, par exemple: c'est ainsi que j'ai ajouté les deux:
la source