Kotlin avec JPA: constructeur par défaut de l'enfer
Comme JPA exige, @Entity
les classes doivent avoir une valeur par défaut (non-arg) constructeur pour instancier les objets lors de la récupération de la base de données.
Dans Kotlin, les propriétés sont très pratique à déclarer dans le cadre du premier constructeur, comme dans l'exemple suivant:
class Person(val name: String, val age: Int) { /* ... */ }
Mais lorsque la non-arg constructeur est déclaré comme une question secondaire, il exige des valeurs pour les premier constructeur à être transmis, de sorte que 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 une certaine catégorie plus complexe que juste String
et Int
et ils sont non nullable, il semble totalement mauvais pour fournir les valeurs pour eux, surtout quand il y a beaucoup de code dans le primaire constructeur et init
blocs et lorsque les paramètres sont activement utilisés, quand ils sont pour être réaffectés à travers la réflexion, la plupart du code est va être exécutée de nouveau.
En outre, val
-les propriétés ne peuvent pas être réaffectés après le constructeur s'exécute, donc immutabilité est également perdu.
La question est donc: comment peut-Kotlin code de l'adapter aux JPA sans duplication de code, le choix de "magie" des valeurs initiales et la perte de l'immuabilité?
P. S. Est-il vrai que Hibernate côté de JPA peut construire des objets avec pas de constructeur par défaut?
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.- La façon dont il le fait, c'est avec les poseurs - aka: la Mutabilité. Il instancie le constructeur par défaut et recherche des poseurs. Je veux des objets immuables. La seule façon qui peut être fait est si hiberne commence à regarder le constructeur. Il y a un billet sur ce hibernate.atlassian.net/browse/HHH-9440
Vous devez vous connecter pour publier un commentaire.
De Kotlin 1.0.6, le
kotlin-noarg
compilateur plugin génère synthétique par défaut construtors pour les classes qui ont été annotés avec les annotations sélectionnées.Si vous utilisez gradle, l'application de la
kotlin-jpa
plugin est suffisant pour générer des constructeurs par défaut pour les classes annotées avec@Entity
:Pour Maven:
data class foo(bar: String)
ne change pas". Il avait juste être sympa de voir un exemple plus complet de la façon dont cela s'inscrit dans la place. Merci@Entity
obtiendrez un constructeur par défaut automatiquement avec la configuration ci-dessus.kotlin-noarg
etkotlin-jpa
avec des liens détaillant leur but blog.jetbrains.com/kotlin/2016/12/kotlin-1-0-6-is-herekotlin-noarg
compilateur plugin qui vous permet de spécifier les annotations pour marquer les classes qui ont besoin d'un zéro argument du constructeur. Voir le post de blog qui est lié à 3 commentaires ci-dessus.il suffit de fournir des valeurs par défaut pour tous les arguments, Kotlin fera constructeur par défaut pour vous.
voir le
NOTE
encadré ci-dessous la section suivante:https://kotlinlang.org/docs/reference/classes.html#secondary-constructors
@D3xter a une bonne réponse pour un modèle, l'autre est une nouvelle caractéristique de Kotlin appelé
lateinit
:Vous utilisez cette fonction lorsque vous êtes sûr que quelque chose va remplir 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
àbirthdate
parce que vous ne pouvez pas utiliser des valeurs primitives aveclateinit
et ils ont aussi, pour le moment, doit êtrevar
(restriction pourrait être disponible dans le futur).Donc pas une réponse parfaite pour l'immuabilité, même problème que l'autre réponse à cet égard. La solution pour que les plugins pour les bibliothèques qui peuvent gérer la compréhension de la Kotlin constructeur et de la cartographie des propriétés pour paramètres du constructeur, au lieu d'exiger un constructeur par défaut. Le Kotlin module pour Jackson fait cela, donc il est clairement possible.
Voir aussi: https://stackoverflow.com/a/34624907/3679676 pour l'exploration des options similaires.
lateinit
lorsque vous avez un cycle de vie en garantissant l'initialisation peu après la construction, il est prévu pour ces cas. Considérant que le délégué est plus prévu pour "quelque temps avant la première utilisation". Bien que, techniquement, ils ont un comportement similaire et de protection, ils ne sont pas identiques.Valeurs initiales sont nécessaires, si vous souhaitez réutiliser constructeur pour les différents domaines, kotlin n'autorise pas les valeurs null. Donc, chaque fois que vous l'intention d'omettre champ, utilisez ce formulaire dans un constructeur:
var field: Type? = defaultValue
jpa requis aucun argument constructeur:
il n'y a pas de duplication de code. Si vous avez besoin de construire une entité et seulement de l'installation de l'âge, de l'utilisation de ce formulaire:
il n'y a pas de magie (suffit de lire la documentation)
Il n'y a aucun moyen de garder l'immuabilité de ce genre.
Vals-les-bains DOIT être initialisée lors de la construction de l'instance.
Une façon de le faire sans l'immuabilité est:
J'ai travaillé avec Kotlin + JPA pour un bon moment et j'ai créé ma propre idée de la façon d'écrire les classes d'Entité.
J'ai juste légèrement prolonger votre idée initiale. Comme vous l'avez dit, nous pouvons créer privée argumentless constructeur et de fournir des valeurs par défaut pour primitives, mais quand nous essayons besoin d'utiliser un autre classes, il devient un peu bordélique. Mon idée est de créer statique objet STUB de classe d'entité que vous avez actuellement écrit électronique.g:
et quand j'ai classe d'entité qui est liée à TestEntity je peux facilement utiliser stub j'ai juste créé. Par exemple:
Bien entendu, cette solution n'est pas parfaite. Vous avez encore besoin de créer un peu de code réutilisable qui ne devrait pas être nécessaire. Il ya aussi un cas qui ne peuvent être résolus bien avec stubbing - relation parent-enfant au sein d'une entité de la classe comme ceci:
Ce code va produire NullPointerException en raison de poulet-oeuf question - nous avons besoin de STUB pour créer un STUB. Malheureusement, nous devons faire de ce champ est nullable (ou autre solution) afin de rendre le code fonctionne.
Aussi, à mon avis avoir Id comme le dernier champ (et nullable) est tout à fait optimale. Nous ne devons pas céder à la main et de laisser la base de données de le faire pour nous.
Je ne dis pas que c'est la solution idéale, mais je pense qu'il tire profit de l'entité de la lisibilité du code et de Kotlin caractéristiques (par exemple, null sécurité). J'espère juste que les futures versions de JPA et/ou de Kotlin rendre notre code plus simple et plus agréable.
Similaire à @pawelbial j'ai utilisé compagnon de l'objet à créer une instance par défaut, cependant au lieu de définir un secondaire constructeur, il suffit d'utiliser le constructeur par défaut args comme @iolo. Cela vous évite d'avoir à définir plusieurs constructeurs et conserve le code plus simple (bien que délivré, la définition de "STUB" compagnon des objets n'est pas exactement le maintenir simple)
Et puis pour les classes qui se rapportent à la
TestEntity
Comme @pawelbial a mentionné, cela ne fonctionne pas lorsque le
TestEntity
de classe "a"TestEntity
classe depuis STUB de ne pas avoir été initialisé lorsque le constructeur est exécuté.Je suis un nœud sur moi-même, mais semble que vous avez explicite de l'initialiseur et de secours à la valeur null comme ce
Ces Gradle construire des lignes qui m'a aidé:
https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.jpa/1.1.50.
Au moins, il s'appuie à l'Ide. C'est à défaut sur la ligne de commande pour le moment.
Et j'ai un
et
var path: LtreeType n'a pas de travail.