Hibernate Best Practices für Anfänger / Sudo Null IT News

In diesem Artikel möchte ich Hibernate nicht im Detail beschreiben, es gibt eine Menge solcher Materialien im Netz. Es ist eher ein Nachschlagewerk, in das Sie mögliche Problembereiche und deren Lösungen einsehen und sehen können, wodurch Sie Fehler bei der Verwendung von Hibernate vermeiden können. Der Artikel richtet sich an Leser, die bereits mit Hibernate und Spring vertraut sind.

Haftungsausschluss: Ich beanspruche weder die Vollständigkeit der notwendigen Aktionen, noch ihre Einzigartigkeit, wenn ich etwas übersehen oder verzerrt habe, Kommentare sind willkommen.

Inhalt

Eindeutiger Bezeichner der Entität

Beginnen wir mit dem Offensichtlichsten – mit der Definition von Essenz. Legen Sie immer eine eindeutige Kennung (im Folgenden ID) für alle Entitäten fest, und es ist wünschenswert, dass es sich um eine Nummer handelt. Wenn Sie beispielsweise eine Kontoentität haben, die ein eindeutiges E-Mail-Feld hat, kann es Ihnen scheinen, dass dieses Feld als fungieren kann id, und zunächst könnte dies eine sehr praktikable Lösung sein. Aber kurz gesagt, es ist nicht perfekt und kann der Anwendung in Zukunft einige Einschränkungen auferlegen, und es können Leistungsprobleme auftreten.

Eindeutiger IdentifikatorEindeutiger Identifikator

Und so haben wir entschieden, dass die ID eine Ganzzahl sein wird, dann kommt die Frage nach der Generierung dieser ID auf die Bühne. Wenn das DBMS den Typ SEQUENCE (Autoinkrement) unterstützt, ist es immer vorzuziehen, ihn zu wählen, in einigen Fällen kann dies die Systemleistung erheblich verbessern.

Wenn Sie beispielsweise 5 Einfügevorgänge neuer Communities haben, kann Spring Data oder Hibernate selbst schlau genug sein, sie in den sogenannten Batch zu legen und sie gleichzeitig an das DBMS zu übergeben, aber dafür müssen Sie die ID kennen der neuen Datensätze im Voraus, hier kommt das gleiche Auto-Inkrement ins Spiel, indem der aktuelle Wert des Zählers angefordert wird. Es ist nicht schwer zu erraten, welche ID alle fünf Datensätze haben werden.

Auto-InkrementAuto-Inkrement

Equals- und HashCode-Methoden

Ob es immer notwendig ist, diese beiden Methoden zu definieren, die Antwort ist nein, im Durchschnitt reichen die Standardimplementierungen für die Anwendung. Es gibt aber auch solche Fälle von Code, die eine eigene Implementierung erfordern, da dies die Performance der Anwendung erheblich steigern oder bei einem Fehler verschlechtern kann.

Seien Sie vorsichtig bei der Auswahl von Kandidatenfeldern in Equals und HashCode, dies ist entscheidend, wenn Ihre Entitäten in einer Set- oder Map-Sammlung gespeichert werden oder wenn Sie mit getrennten Objekten arbeiten müssen. Die Verwendung wird allgemein empfohlen Unique Identifier und/oder sogenannte „Natural Keys“also ein Feld, das ein Objekt eindeutig identifiziert und eindeutig ist.

Vermeiden Sie die Verwendung von @Data- und @ToString-Lombok-Annotationen. Beide Annotationen verwenden standardmäßig alle Felder der Objekte, was zu Problemen führen kann. Wenn Sie wirklich wollen, können Sie @ToString verwenden, Hauptsache nicht vergessen, die LAZY-Felder Ihrer Entität mit @ToString.Exclude von der Verarbeitung auszuschließen.

@OneToMany(mappedBy = “account”, cascade = CascadeType.ALL) @ToString.Exclude private Set relatedPlatforms = new HashSet<>();

Ausnahmebehandlung

Falls Sie plötzlich nackten Ruhezustand verwenden, behandeln Sie eine von Jdbc ausgelöste Ausnahme niemals als wiederherstellbar, d. h. versuchen Sie nicht, die Transaktion fortzusetzen, sondern setzen Sie sie einfach zurück und schließen Sie den EntityManager. Andernfalls garantiert Hibernate nicht die Aktualität der hochgeladenen Daten!

Wenn Sie Spring Data verwenden, können Sie beruhigt sein, er hat das für Sie erledigt.

AusnahmebehandlungAusnahmebehandlung

Zweiseitige Entitätsbeziehung

Wie Sie wissen, gibt es in Hibernate zwei Arten von Beziehungen zwischen Entitäten, einseitig und zweiseitig. Ohne auf Details einzugehen, möchte ich sagen, dass es wünschenswert ist, immer bidirektional zu verwenden. Wenn Sie sich zu 100% sicher sind, dass Sie niemals Feedback benötigen, reicht One-Way aus. Der Unterschied zwischen ihnen ist nicht sehr groß (wenn alles richtig gemacht wird), und die Vorteile des bilateralen sind viel größer.

Und wie macht man es richtig? Hier ist alles einfach, damit Hibernate genau eine Zwei-Wege-Beziehung (und nicht zwei Ein-Wege-Beziehungen) erstellen kann, müssen Sie klären, welche Seite der Besitzer der Beziehung ist und welche die Kehrseite ist. Das mappedBy-Attribut hilft uns dabei. Sie wird in der Anmerkung angegeben, die sich auf der gegenüberliegenden Seite der Beziehung befindet und den Eigentümer angibt.

Zweiseitige EntitätsbeziehungZweiseitige Entitätsbeziehung

Auch möchte ich im Rahmen dieses Themas darauf aufmerksam machen, wie ich den Zusammenhang in der Essenz selbst beschreibe, nämlich:

@OneToMany(mappedBy = “platform”, cascade = {CascadeType.PERSIST}) private Set statuses = new HashSet<>();

Es ist eine gute Angewohnheit, eine Sammlung zu initialisieren, was Sie manchmal vor einer unangenehmen NullPointerException oder vor der ständigen Überprüfung einer Sammlung auf Null bewahren kann. Außerdem stellen Sie vielleicht fest, dass ich anstelle von List Set verwende. Dieser Trick kann helfen, irgendwo Ressourcen zu gewinnen (vorausgesetzt, dass Equals und HashCode überschrieben werden), obwohl List auch gut funktioniert.

Faules Laden

Um das Thema der Beziehungen zwischen Entitäten fortzusetzen, verwendet Hibernate je nach Art der Beziehung den Ladetyp LAZY oder EAGER verwandter Entitäten. Stellen Sie sicher, dass der Typ LAZY festgelegt ist, es sei denn, Sie haben einen besonderen Grund, dies nicht zu tun.

Parametrisierte Abfragen

Wenn Sie plötzlich JPQL- oder native Abfragen schreiben, vergessen Sie nicht, Suchkriterien über Parameter zu übergeben, schreiben Sie sie niemals direkt in die Abfrage, weil Dadurch entsteht eine Schwachstelle für SQL-Injection-Angriffe

Parametrisierte AbfragenParametrisierte Abfragen

Cache der zweiten Ebene

Dies ist eine sehr nützliche Hibernate-Funktion. In der Phase des Erlernens des Frameworks wird dem nicht viel Aufmerksamkeit geschenkt, und im Prinzip kann ein kleines Projekt auch ohne Second-Level-Cache existieren. Aber wenn Sie auf die Entwicklung in Enterprise abzielen oder einfach nur ein Projekt mit einer mehr oder weniger großen Last sägen, dann machen Sie sich unbedingt mit diesem Mechanismus vertraut. Hier ist Feintuning gefragt, dieses Thema ist umfangreich und der Preis eines Fehlers ist hier hoch. Daher können Sie, während Sie am Anfang Ihrer Reise stehen und noch kein „Zauberer“ sind, zumindest einen „sicheren“ Ansatz wählen, der dunkler ist, weniger effektiv ist und für das Projekt möglicherweise völlig ausreicht. Die Sicherheit der Methode liegt darin, dass wir nur mit statischen Entitäten arbeiten, also Kandidaten für den Second-Level-Cache, das sind Entitäten, die sich in Ihrer Anwendung nicht ändern, sie werden entweder angelegt oder gelöscht. In diesem Szenario ist es weniger wahrscheinlich, dass Sie einen Fehler machen.

Hibernate enthält keine Implementierung dieser Funktion, daher müssen Sie Implementierungen von Drittanbietern verwenden, von denen es viele gibt, ich verwende Ehcache 3. Um Hibernate 5 mit Ehcache 3 zu befreunden, müssen Sie die Verbindung herstellen Hibernate-jcache-Abhängigkeit.

Erforderliche Abhängigkeiten Erforderliche Abhängigkeiten

Nachdem Sie die Abhängigkeiten verbunden haben, müssen Sie Spring mitteilen, dass Sie mit ihnen arbeiten müssen, und insbesondere die jpa persistence sharedCache- und Hibernate-Cache-Eigenschaften festlegen.

AnwendungseigenschaftendateiAnwendungseigenschaftendatei

Als nächstes muss die ausgewählte Cache-Kandidatenentität mit drei Annotationen @Cacheable @Cache und @Immutable markiert werden

Entität, die zwischengespeichert werden sollEntität, die zwischengespeichert werden soll

Das war’s, Sie haben einen Thread-sicheren Second-Level-Cache implementiert! Es gibt immer noch solche Feinheiten wie das Einstellen der Lebensdauer von Objekten im Cache, der Cache-Größe usw. Dies funktioniert jedoch auch mit Standardeinstellungen. Fangen Sie an, es zu benutzen, und tauchen Sie dann in die Details ein, Google wird Ihnen helfen 😉

@Column- und @Table-Annotation

Bei der Verwendung dieser Anmerkungen empfiehlt es sich außerdem, immer den Feld-/Tabellennamen anzugeben. Wenn Sie dies nicht tun, wird Hibernate dies für Sie tun, und der Name entspricht möglicherweise nicht immer Ihren Erwartungen.

Feld- und TabellennamenFeld- und Tabellennamen

Anzahl der geladenen Entitäten

Dies ist kein sehr dringendes Problem und Sie werden wahrscheinlich nicht darauf stoßen, aber ich denke, Sie sollten sich dessen bewusst sein. Tatsache ist, dass verwaltete Entitäten nicht nur Speicher belegen, sondern auch Prozessorzeit verbrauchen. Hibernate überprüft periodisch den Zustand geladener Objekte auf Übereinstimmung in der Datenbank, dafür ist das sogenannte Dirty Checking zuständig. In einer Mehrbenutzeranwendung kann dies eine erhebliche Belastung darstellen. Hier kommt Lazy Loading ins Spiel.

N+1 Problem auswählen

Es gibt keine universelle Lösung für dieses Problem, vieles hängt vom Projekt ab. Ich überspringe die offensichtlichsten Fälle.

Beim Abrufen einer Entität oder einer Liste von Communities trifft Hibernate eine Auswahl und alles wäre in Ordnung, aber wenn diese Entitäten eine EAGER-Beziehung haben (manytoone und onetoone standardmäßig EAGER), dann wird für jede von ihnen eine zusätzliche Auswahl getroffen!

Der Ausweg aus dieser Situation kann darin bestehen, die Standardeinstellung EAGER in LAZY zu ändern.

@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = “status”) private StatusEntity status;

Dennoch ist diese Lösung nicht immer geeignet, sagen wir, Sie erhalten eine Liste von AccountEntiy-Entitäten, die eine Beziehung zu StatusEntity enthält, und Sie müssen den Status jeder Entität überprüfen (natürlich tun Sie dies innerhalb einer einzigen Transaktion, sonst müssen Sie erhält eine LazyInitializationException), führt dies erneut zu demselben Problem, beim Zugriff auf den Status jeder Entität wird Hibernate eine separate Auswahl treffen, und jetzt ist das Problem wieder da.

In diesem Fall hilft die JOIN FETCH-Anweisung, das Problem zu lösen, die Hibernate anzeigt, dass verwandte Entitäten ebenfalls geladen werden müssen, hier ist ein Beispiel für eine JPQL-Abfrage:

Wählen Sie das Konto aus AccountEntity Konto links beitreten Konto abrufen.status

Diese Lösung hat ein Problem, wenn die Liste der Entitäten plötzlich sehr stark anwächst. Stellen Sie sich vor, dass die Anzahl der AccountEntity-Datensätze fünfzigtausend beträgt, es wird nicht einfach sein, eine solche Anzahl von Communities zu laden, und dann werden weitere fünfzigtausend StatusEntity in die Ladung geladen, das ist überhaupt keine leichte Aufgabe. Um diese Schwierigkeiten zu vermeiden, müssen Sie zum Lazy-Loading-Schema zurückkehren, da das System jeweils nur AccountEntity lädt, was die Last verringert, aber gleichzeitig das Problem von N + 1 zurückgibt Anfragen. Ohne Panik gibt es einen Ausweg aus dieser Situation, sozusagen die „goldene Mitte“.

Die Batch-Fetching-Ladestrategie ermöglicht es Ihnen, verwandte Entitäten nicht durch einzelne Anforderungen, sondern in Stapeln abzurufen. Die Stapelgröße wird durch die Annotation @BatchSize angegeben.

@ManyToOne @JoinColumn(name = “status”) @BatchSize(size = 10) private StatusEntity status;

Die Verwendung dieser Anmerkung mit dem Parameter “size=10” veranlasst Hibernate, die zu lesende Entität und die nächsten neun der Reihe nach zu laden.

Fazit

Und so habe ich Ihnen die Fallstricke gezeigt und wie sie umgangen werden können. Aber keine Lösung ist universell. Ich fordere Sie auf, sich nicht mit Capypasta zu beschäftigen, sondern sich mit dem Kern des Problems zu befassen. Wie ich oben geschrieben habe, ist dies eine Anleitung mit Beispielen, keine detaillierte Anleitung.

Am Beispiel einer eindeutigen Kennung zeige ich, warum in Ihrem speziellen Fall die in diesem Artikel vorgestellte Lösung möglicherweise nicht die effektivste ist.

Stellen wir uns vor, dass Sie in Ihrem Projekt PostgreSQL als Datenbank verwenden und sich entscheiden, eine SEQUENCE-Generierungsstrategie festzulegen, so wie es im Artikel beschrieben ist. Das wird ohne Probleme funktionieren. Es gibt nur ein „aber“, in diesem Fall richtet Hibernate die Generierung von Indizes auf der Datenbankseite ein und diese Einstellung ist nicht optimal.

Es wird ein gemeinsamer Index für Ihre gesamte Datenbank erstellt.

Stellen Sie sich vor, Sie haben 10 Tabellen (Entitäten) in der Datenbank und der Indextyp ist Integre, dann haben Sie am Ende Integre.MAX_SIZE / 10 für jede Tabelle.

Die Annotation @SequenceGenerator hilft Ihnen, diesen Mangel zu vermeiden.

SequenzgeneratorSequenzgenerator

Das ist alles, ich hoffe, dieser Artikel war für Sie nützlich.

Similar Posts

Leave a Reply

Your email address will not be published.