Blog

Lassen Sie Hibernate Ihre Welt verbinden!

Maarten Winkels

Aktualisiert Oktober 23, 2025
6 Minuten

In meinem aktuellen Projekt müssen wir eine große CSV-Datei mit zusammenhängenden Daten parsen. Jede Zeile enthält eine "Entität" oder ein "Objekt" und die meisten Zeilen sind durch Verweise auf andere Zeilen miteinander verbunden. Die Daten müssen an den Server gesendet werden, aber vorher müssen einige Validierungen und Manipulationen durchgeführt werden. Die Dateien könnten so groß werden, dass es ein Problem wäre, alle Objekte auf dem Client-Rechner im Speicher zu halten. Wir haben beschlossen, eine Datenbank zu verwenden, um die Daten vorübergehend zu speichern und nach zusammenhängenden Zeilen zu suchen. Die Zeilen sind nicht unbedingt geordnet, so dass wir warten müssen, bis die letzte Zeile geparst ist, bevor wir die Daten validieren und bearbeiten können. Wir möchten, dass die Daten in einem schönen objektorientierten Modell erfasst werden. Wir haben Hibernate und Apache Derby für unsere Implementierung verwendet und es hat sich als sehr praktisch erwiesen!

Schema-Erstellung

Hibernate ermöglicht die Generierung von Laufzeitschemata. Diese Funktion wird meist für Tests verwendet, aber in diesem Fall ist sie sehr nützlich. Da wir die Datenbank nach dem Senden der Daten nicht mehr benötigen, erstellen wir das Schema, wenn wir die Hibernate SessionFactory einrichten, und löschen das Schema kurz vor dem Schließen. Dadurch wird auch die Festplattennutzung nach dem Ausführen der Anwendung auf ein Minimum beschränkt. Apache Derby ist eine ziemlich ausgereifte Datenbank, die im Prozess ausgeführt werden kann. Sie verwendet die Festplatte zur Speicherung. Wir haben versucht, HSQLDB zu verwenden, aber die Suche in einer großen Datenbank erwies sich als sehr langsam.

Daten einfügen

Nehmen wir an, wir haben Zeilen wie diese: [code]10;hospitalId;... 20;hospitalId;patientId;... 30;hospitalId;patientId;treatmentId;... 30;hospitalId;patientId;treatmentId;... 20;hospitalId;patientId;... 30;hospitalId;patientId;treatmentId;... 10;hospitalId;... 20;hospitalId;patientId;... 30;hospitalId;patientId;treatmentId;... ... [/code] Wir wollen dies zunächst in Objekte wie diese zerlegen: [java] class Hospital { int hospitalId; String location; ... } class Patient { int hospitalId; int patientId; String name; ... } Klasse Behandlung { int hospitalId; int patientId; int treatmentId; int numberOfDays; ... } [/java] Jede Zeile steht für ein Objekt. Der erste Teil der Zeile identifiziert den Typ des Objekts. Die hospitalId identifiziert ein Krankenhaus eindeutig, aber die patientId ist möglicherweise nicht krankenhausübergreifend eindeutig. Das Gleiche gilt für die treatmentId für alle Patienten. Da wir nicht sicherstellen können, dass die Zeilen tatsächlich in der richtigen Reihenfolge sind, können wir die Zuordnungen zwischen Krankenhäusern, Patienten und Behandlungen nicht Zeile für Zeile vornehmen. Jede Behandlung muss daher ihre hospitalId behalten, um später die Zuordnung vornehmen zu können. Die Hibernate-Zuordnung zum Einfügen dieser Daten ist recht einfach: [xml] <hibernate-mapping default-access="Feld"> <class name="Krankenhaus"> <id type="int" column="ID"> <generator class="native"/> </id> <property name="hospitalId"/> <property name="Standort"/> </class> <class name="Patient"> <id type="int" column="ID"> <generator class="native"/> </id> <property name="hospitalId"/> <property name="patientId"/> <property name="name"/> </class> <class name="Krankenhaus"> <id type="int" column="ID"> <generator class="native"/> </id> <property name="hospitalId"/> <property name="patientId"/> <property name="treatmentId"/> <property name="numberOfDays"/> </class> </hibernate-mapping> [/xml] Wir machen uns keine Gedanken über Spaltennamen, da wir nur über Hibernate auf die Datenbank zugreifen werden. Beachten Sie außerdem das Fehlen der Eigenschaft name im id-Tag. Das bedeutet, dass Hibernate sich um die IDs kümmert und dass unser Code sie nicht behandeln muss, so dass es in keiner der Klassen ein id-Feld gibt. Dies hat einige weitreichende Konsequenzen, die es nur in einer kleinen Anzahl von Fällen nützlich machen, aber unser Fall ist einer davon (später im Projekt müssen wir das Feld vielleicht trotzdem einführen...).

Daten lesen

Beim Lesen der Daten möchten wir nun Objektverknüpfungen zwischen Patientenobjekten und Krankenhausobjekten sowie Behandlungsobjekten und Patientenobjekten verwenden. Eigentlich muss eine Behandlung auch das zugehörige Krankenhaus kennen, da die Patientendaten beschädigt werden könnten. Die Klassen müssen wie folgt angepasst werden: [java] class Hospital { int hospitalId; String location; ... } Klasse Patient { int hospitalId; int patientId; Krankenhaus hospital; String name; ... } Klasse Behandlung { int hospitalId; int patientId; int treatmentId; Krankenhaus hospital; Patient patient; int numberOfDays; ... } [/java] Jetzt möchten wir, dass Hibernate diese Felder mit den entsprechenden Objekten ausfüllt. Schauen wir uns zunächst die Patienten-Zuordnung an. Wir brauchen eine Art von Mehrfachzuordnung in der Zuordnung: [xml] ... <class name="Patient"> <id type="int" column="ID"> <generator class="native"/> </id> <many-to-one name="Krankenhaus" column="hospitalId" property-ref="hospitalId" update="false" insert="false" foreign-key="none"> <property name="hospitalId"/> <property name="patientId"/> <property name="name"/> </class> ... [/xml] Die Zuordnung der Assoziationen ist ziemlich kompliziert:

  1. Das Spaltenattribut zeigt an, dass die Assoziation auf die Spalte Patient.hospitalId abgebildet wird.
  2. Das Attribut property-ref gibt an, dass das andere Ende der Assoziation auf die Spalte Hospital.hospitalId abgebildet wird. Dies ist nicht die Eigenschaft id der Entität Hospital!
  3. Die Eigenschaft hospitalId ist bereits zugeordnet. Hibernate wird sich beschweren, wenn Sie eine Spalte zweimal zuordnen , es sei denn, die Zuordnung wird mit update="false" insert="false" attributiert .
  4. Wenn das Attribut Fremdschlüssel auf "none" gesetzt ist, verhindert Hibernate die Erstellung einer Fremdschlüssel-Beschränkung zwischen den Spalten. Da die Reihenfolge, in der die Zeilen eingefügt werden, ungewiss ist, würde die Einschränkung verletzt werden, wenn der Datensatz des Kunden zuerst eingefügt wird. Außerdem würden Beschädigungen in den Daten das Einfügen von Datensätzen verhindern, während wir die Daten nach dem Einfügen in die Datenbank überprüfen möchten.

Pff, da steckt ganz schön viel in dieser einen Zeile der Karte! Werfen wir nun einen Blick auf die Zuordnung Behandlung. Behandlung wird auf Patient abgebildet, aber die Referenz bezieht sich auf zwei Spalten. Wir müssen diese Spalten als ein spezielles Konstrukt in der Zuordnung Client angeben. [xml] <class name="Patient"> <id type="int" column="ID"> <generator class="native"/> </id> <many-to-one name="krankenhaus" column="krankenhausId" property-ref="krankenhausId" update="false" insert="false" foreign-key="none"/> <Eigenschaften name="Referenz"> <property name="hospitalId" index="IDX_PATIENT_REFERENCE"/> <property name="patientId" index="IDX_PATIENT_REFERENCE"/> </properties> <property name="name"/> </class> <class name="Krankenhaus"> <id type="int" column="ID"> <generator class="native"/> </id> <many-to-one name="krankenhaus" column="krankenhausId" property-ref="krankenhausId" update="false" insert="false" foreign-key="none"/> <many-to-one name="patient" property-ref="reference" update="false" insert="false" foreign-key="none"> <Spalte name="hospitalId"/> <Spalte name="patientId"/> </many-to-one> <property name="hospitalId"/> <property name="patientId"/> <property name="treatmentId"/> <property name="numberOfDays"/> </class> [/xml] Die Zuordnung ist im Wesentlichen die gleiche. Der einzige Unterschied besteht darin, dass property-ref auf ein properties-Konstrukt in der Patient-Zuordnung verweist. Dieses Konstrukt gruppiert eine Reihe von Eigenschaften. Die Many-to-One-Zuordnung benötigt die gleiche Anzahl von Spalten als Unterelemente in der gleichen Reihenfolge. Wenn Sie den Eigenschaften in der Patient-Zuordnung denselben Indexnamen zuweisen, ist eine schnelle Suche gewährleistet.

Ihre Welt ist jetzt vernetzt!

Jetzt können wir also mit Hibernate nicht verwandte Objekte in die Datenbank einfügen, aber wenn wir sie mit Hibernate aus der Datenbank holen... BAM!... Ihre Welt ist verbunden! Die gesamte Datenverarbeitungslogik kann nun sicher davon ausgehen, dass die zugehörigen Objekte vorhanden sind. Natürlich erst, nachdem wir überprüft haben, dass die Referenzen existieren. Es gibt ein kleines Problem: Wir sind noch nicht gegen Duplikate geschützt. Wenn Hibernate versucht, ein Treatment-Objekt zu laden und feststellt, dass es zwei Patient-Objekte gibt, die die Referenz erfüllen, wird eine Ausnahme geworfen. Dies lässt sich nicht vermeiden: Hibernate kann nicht entscheiden, welche Zeile zum Lesen des Patientenobjekts verwendet werden soll. In unserem Fall ignorieren wir die Fehler, da es eine Validierung gibt, die die Einzigartigkeit von patientId überprüft. Die Validierung meldet einen Fehler und die weitere Verarbeitung der Datensätze ist nutzlos. In anderen Fällen könnte es etwas komplizierter sein.

Verfasst von

Maarten Winkels

Contact

Let’s discuss how we can support your journey.