Kotlin mit JPA: Wie man die Hölle des Default-Konstruktors besiegt

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Lernen Sie kennen: „Die Hölle des leeren Konstruktors“ in Kotlin + JPA

\n\n

Wenn Sie jemals versucht haben, Kotlin mit JPA (Java Persistence API) zu verbinden, sind Sie sicherlich auf die Situation gestoßen, dass Hibernate beim Start der Anwendung eine Ausnahme wie diese wirft:

\n\n
org.hibernate.InstantiationException: No default constructor for entity: com.example.User\n
\n\n

Kommt Ihnen bekannt vor? Allerdings. Das ist genau das Problem, das tausende Aufrufe auf Stack Overflow gesammelt hat und den düsteren Namen „Kotlin with JPA: default constructor hell“ trägt.

\n\n

In diesem Artikel werden wir ruhig, Schritt für Schritt, Folgendes durchgehen:

\n
    \n
  • Warum dieser Fehler überhaupt auftritt
  • \n
  • Warum Kotlin „keinen“ leeren Konstruktor generieren „will“
  • \n
  • Drei bewährte Methoden zur Lösung des Problems (von einfach bis professionell)
  • \n
  • Welche Methode Sie für Ihr Projekt wählen sollten
  • \n
\n\n

Los geht's!

\n\n

Warum benötigt Hibernate einen parameterlosen Konstruktor?

\n\n

Beginnen wir mit Java. Im klassischen Java sind wir es gewohnt, so zu schreiben:

\n\n
public class User {\n    private Long id;\n    private String name;\n\n    // Erforderlicher leerer Konstruktor\n    public User() {}\n\n    // Getter, Setter...\n}\n
\n\n

Hibernate (und jede JPA-Implementierung) verwendet Reflektion, um Objekte zu erstellen. Wenn es eine Zeile aus der Datenbank liest, muss es eine Instanz der Klasse User erstellen und dann die Felder per Reflektion füllen. Dafür benötigt es einen parameterlosen Konstruktor (default constructor).

\n\n

In Java erstellt der Compiler automatisch einen leeren Konstruktor, wenn Sie keinen anderen definiert haben. Daher tritt das Problem normalerweise nicht auf.

\n\n

Und was ist die Besonderheit von Kotlin?

\n\n

Kotlin ist eine Sprache mit starkem Fokus auf Unveränderlichkeit und Sicherheit. Betrachten Sie eine typische Kotlin-Klasse:

\n\n
data class User(\n    val id: Long = 0,\n    val name: String = ""\n)\n
\n\n

Hier passieren zwei wesentliche Dinge:

\n
    \n
  • Alle Eigenschaften werden im primären Konstruktor (primary constructor) deklariert
  • \n
  • Eigenschaften werden als val (unveränderlich) deklariert und haben Standardwerte
  • \n
\n\n

Kotlin generiert keinen separaten leeren Konstruktor, wenn Sie einen primären Konstruktor mit Parametern haben. Selbst wenn allen Parametern Standardwerte zugewiesen sind, wird dies auf JVM-Bytecode-Ebene nicht als „echter“ parameterloser Konstruktor betrachtet.

\n\n

Wenn Hibernate versucht, User.class.newInstance() per Reflektion aufzurufen, findet es keinen leeren Konstruktor – und stürzt mit einem Fehler ab.

\n\n

Methode 1: Die einfachste – explizit einen sekundären Konstruktor hinzufügen

\n\n

Der direkteste Weg ist, manuell einen zweiten, leeren Konstruktor hinzuzufügen:

\n\n
data class User(\n    val id: Long = 0,\n    val name: String = ""\n) {\n    // Sekundärer Konstruktor ohne Parameter\n    constructor() : this()\n}\n
\n\n

Hier bedeutet constructor() : this(): „Rufe den primären Konstruktor mit den Standardwerten auf“.

\n\n

Vorteile:

\n
    \n
  • Sehr einfach und verständlich
  • \n
  • Erfordert keine Plugins oder zusätzliche Einstellungen
  • \n
\n\n

Nachteile:

\n
    \n
  • Man muss diesen Konstruktor für jede Entität schreiben
  • \n
  • Wenn Sie 10-15 Entitäten haben, wird es mühsam
  • \n
  • Man kann leicht vergessen, ihn hinzuzufügen
  • \n
\n\n

Methode 2: open class verwenden und alle Eigenschaften zu var machen

\n\n

Viele Beispiele im Internet schlagen diese Lösung vor:

\n\n
open class User {\n    var id: Long = 0\n    var name: String = ""\n}\n
\n\n

Hier machen wir die Klasse open (damit Hibernate einen Proxy erstellen kann) und die Eigenschaften zu var (veränderlich). Und kein primärer Konstruktor – also generiert Kotlin standardmäßig einen leeren Konstruktor.

\n\n

Vorteile:

\n
    \n

Blogs

Buchempfehlungen