Werft JPA/Hibernate weg
(stemlaur.com)Zusammenfassung
JPA/Hibernate ist zu einem weit verbreiteten Framework geworden, weil man damit in Java-Code angeblich kein SQL mehr schreiben muss. Ich möchte jedoch dafür plädieren, es in neuen Projekten nicht zu verwenden.
Gründe
Extrem lange offizielle Dokumentation
Die offizielle Dokumentation umfasst als PDF ganze 406 Seiten und ist damit länger als Der Herr der Ringe (231 Seiten) und das SQL-Standarddokument (288 Seiten). Man braucht kein Masterstudium, um Datenbankabfragen zu lernen.
Veränderlichkeit
- Selbst wenn eine Entity bestimmte Elemente zwingend benötigt, wird ein parameterloser Konstruktor erzwungen.
- Man kann Vererbung nicht verhindern, indem man in Entity-Klassen die Schlüsselwörter
finaloderabstractverwendet. - Reflection/Introspection missachtet das OOP-Prinzip der Kapselung.
- Jemand kann bösartigen Code einschleusen und die Daten vollständig vernichten.
Lazy Loading und Cache
- Die Annotation
@Lazyist für Anfänger eine der schlimmsten Techniken überhaupt. Dennoch lässt sie sich kaum vermeiden, wenn das Domain-Design nicht zu Hibernate passt oder man keine Queries schreiben kann. - Der Cache-Mechanismus ist schwer zu verstehen. Und selbst wenn man ihn versteht, muss man nicht Query-Ergebnisse, sondern die Entities selbst im Cache speichern.
Speicher-Datenbank-Synchronisierung (Flush)
Die Technik Flush synchronisiert im Speicher abgelegte Objekte mit der Datenbank. Das verursacht zwei Probleme.
- Sobald Flush greift, sind Änderungen im Speicher faktisch abgeschlossen, sodass andere Persistenzwerkzeuge als Hibernate praktisch ausgeschlossen sind, und
- wenn während des Flush Konflikte auftreten, können Stack-Trace-Fehler entstehen, die nichts mit dem eigentlichen Code zu tun haben.
Nur bestimmte Spalten einer Tabelle abrufen
Wenn man bei einer Entity nur auf eine einzelne Spalte zugreifen will, ist der SQL-Ansatz simpel:
select url from image
where id = 'F462E8D9-9DF7-4A58-9112-EDE0434B4ACE';
Hibernate lädt jedoch grundsätzlich alle Spalten der Entity. Um das zu vermeiden, ist ein komplizierter Umweg nötig.
Constraints für Spalten definieren
Um Constraints für eine bestimmte Spalte zu definieren, muss man mehrere Annotationen wie unten anbringen.
...
@NotNull
@NotEmpty
@Email
private String email;
...
Diese Methode bringt folgende Probleme mit sich.
- Für diese Bedingungen lassen sich keine Unit-Tests schreiben.
- Während eines Flush-Vorgangs ist es zu spät, diesen Verarbeitungsablauf noch nachvollziehen zu wollen.
- Die möglichen Exceptions sind allgemein und nutzlos.
- Geschäftsregeln werden nur als technische Regeln behandelt.
Probleme auf strategischer Ebene
- Framework-Updates sind schlimm, Abwärtskompatibilität wird ignoriert, und man wird zwangsläufig abhängig. Die Unternehmen dahinter gewöhnen sich daran, dass es selbstverständlich sei, ihr eigenes Framework zur Monopolisierung einzusetzen. Diesen Teufelskreis muss man stoppen.
- Einen Proof of Concept unbedingt nur über ein Framework gewinnen zu wollen, verengt lediglich den eigenen Blick — und bei JPA/Hibernate ganz besonders. Man sollte nicht das Geringste davon tolerieren.
Was sollte man tun?
Verwendet SQL
Mit SQL ist alles möglich. Jeder Programmierer kennt es, Queries sind intuitiv, und man braucht kein Framework.
Mein Vorgesetzter will aber Hibernate einsetzen
Kündigt oder arbeitet daran, den Code vom Framework zu entkoppeln.
Wenn ihr es bereits verwendet ...
- Macht den Sichtbarkeitsgrad des Standardkonstruktors und der Setter nicht zu
public. - Verwendet statt von SQL generierter IDs Zeichenketten wie UUIDs.
- Verwendet statt des Namens
XXXRepositorylieberXXXDao. - Verwendet die Annotation
@SequenceGeneratornicht. - Trennt Domain-Klassen und DAO-Klassen über
interface. - Verwendet keine Eins-zu-viele-Beziehungen (
@OneToManyusw.) und vermeidet nach Möglichkeit lieber Entity-Mapping.
Fazit
JPA/Hibernate: weg damit.
- Es gibt kürzere und bessere Dokumentation zur Lösung von Geschäftsproblemen.
- Haltet nicht zu starr an einem zu schnellen Designansatz fest.
- Zeigt dem nächsten Entwickler, der euren Code pflegen muss, etwas Großzügigkeit.
Was verwendet ihr?
- JPA/Hibernate
- Andere ORM-Techniken
22 Kommentare
Ich bin gegen die Meinung, JPA/Hibernate aufzugeben.
„Sehr lange offizielle Dokumentation“
Auch SQL ist schwer, wenn man es zum ersten Mal lernt. Ist es denn einfach, komplexe Joins, Subqueries, Stored Procedures und Funktionen vollständig zu verstehen?
Bei JPA reicht es völlig aus, anfangs nur die Kernkonzepte zu verstehen. Tiefergehende Inhalte kann man nachschlagen, wenn sie gebraucht werden.
Und es gibt LLMs.
„Probleme mit Mutabilität und Reflection“
Das ist eine Sorge, die daraus entsteht, dass man die Funktionsweise des Frameworks nicht versteht.
In der Praxis treten dadurch kaum jemals echte Probleme auf.
Im Gegenteil: Durch Reflection wird das Object Mapping automatisiert, was die Produktivität deutlich steigert.
„Lazy Loading und Cache“
@Lazy ist also „die schlimmste Technik“? Es ist eine sehr nützliche Funktion, um das N+1-Problem zu lösen und die Performance zu optimieren.
Der Cache-Mechanismus hilft im Gegenteil erheblich bei der Verbesserung der Performance.
„Nur bestimmte Spalten einer Tabelle abrufen“
Mit JPQL oder Projection lassen sich die benötigten Spalten leicht gezielt abfragen.
Und man kann es zusammen mit QueryDSL verwenden.
Ich denke, das Ziel von ORM ist nicht, SQL vollständig zu ersetzen, sondern Entwicklern dabei zu helfen, sich stärker auf die Business-Logik zu konzentrieren..
Ich bin zwar ein ORM-Skeptiker, aber ich glaube nicht, dass hier ausreichend Alternativen aufgezeigt wurden.
Wenn man zu stark auf ORM setzt, ist das wirklich ein Fass ohne Boden, und wie oben erwähnt, kann es passieren, dass man sich in einer noch größeren Dokumentationslandschaft als der SQL-Dokumentation abmüht und daran zugrunde geht.
Ich entwickle derzeit in einem privaten Projekt ohne ORM, aber sobald ich anfange, ein Design mit Blick auf Wiederverwendbarkeit zu entwerfen, gerate ich manchmal in eine Richtung, bei der ich denke: Eigentlich baue ich gerade einfach selbst ein ORM. haha
Dadurch, dass man ein Framework verwendet, kann man mit anderen Entwicklern, die dasselbe Framework im professionellen Umfeld einsetzen, ein gemeinsames Paradigma teilen — dieser Punkt scheint in solchen Artikeln, die immer dazu raten, so etwas nicht zu verwenden, stets ignoriert zu werden.
Wenn es viele Tabellen und viele Spalten gibt (zum Beispiel 50 Tabellen und mehr als 100 Spalten pro Tabelle), wird die Hölle losbrechen, wenn man einfach pures SQL verwendet.
Ich denke allerdings, dass es eine enorme Verschwendung ist, für kleine Services JPA/Hibernate einzusetzen.
Wie immer scheint so eine Meinung vom jeweiligen Fall abzuhängen.
(Das hier als Beispiel Genannte hat ja auch nur 3–4 Spalten ...)
Ich denke, die letzte Frage im obigen Beitrag sollte etwas angepasst werden.
Im Java-Umfeld könnte man es als 1. ORM vs. 2. Nicht-ORM zusammenfassen.
Sowohl 1 als auch 2 haben klare Vor- und Nachteile, daher ist es nicht angemessen, wie im obigen Beitrag zu einem so extremen Schluss zu kommen.
Bei uns ist es zum Beispiel so:
Wir verwenden mit JPA/Hibernate/QueryDSL ein ORM und gleichzeitig auch MyBatis.
Wir nutzen ORM, um die Produktivität so weit wie möglich zu steigern,
und für Queries, die sich mit ORM nur schwer abdecken lassen, verwenden wir MyBatis.
Und egal, ob man sich oben für 1 oder 2 entscheidet: Man sollte SQL gut beherrschen.
Ich würde es auch gern bearbeiten, aber die Website bietet keine solche Funktion ...
Ich glaube, hier wird so getan, als wüsste man gar nicht mehr, warum ORM überhaupt populär geworden ist.
Zwar gibt es gewisse Lernkosten, aber sobald man sich daran gewöhnt hat, steigt die Produktivität eindeutig.
SQL wirkt zwar einfach, aber diese Ermüdung, wenn man jedes einzelne SQL-Statement von Hand schreibt ... Und wenn sich dann Tabellen ändern, muss man auch alle betroffenen Queries einzeln anpassen, sodass die Wartung von SQL alles andere als trivial ist. Gerade weil es klein und einfach ist, steigt der Arbeitsaufwand stark an (deshalb kommt ja auch ständig das Thema Produktivität auf).
Außerdem treten Fehler in SQL erst zur Laufzeit auf und sind deshalb schwer zu finden. Wenn man dann auch noch Schutz gegen Angriffe wie SQL-Injection Stück für Stück selbst einbaut, landet man am Ende doch wieder bei zusätzlichem Code zur Query-Erzeugung (meistens fängt es mit einer einfachen Template-Form an ...). Wenn man das weiterführt, kommt am Ende wieder etwas ORM-Ähnliches heraus — warum dann nicht einfach gleich ORM verwenden?
Das erinnert mich an den Beitrag, der vor ein paar Tagen gepostet wurde.
https://de.news.hada.io/topic?id=17955
Ich stimme zu.
Ich habe oft den Eindruck, dass die Gründe für den Einsatz von ORM und seine Vorteile nicht vollständig verstanden werden.
Außerdem scheint es nicht viele Menschen zu geben, die versuchen, das über ORM tatsächlich ausgeführte SQL zu analysieren oder zu verstehen.
Es wäre gut, wenn stärker bekannt würde, dass es nicht nur über reine Bequemlichkeit hinausgeht, sondern auch dabei helfen kann, SQL-Optimierung und die Funktionsweise der Datenbank tiefgehend zu verstehen.
Ich finde, man muss sich nicht auf eines der beiden Extreme versteifen – dass man es unbedingt nutzen muss oder auf keinen Fall nutzen darf, haha;;
Wenn ich Produktivität brauche, nutze ich ein ORM,
und komplexe Queries, die sich mit ORM nicht abdecken lassen, oder Queries, die stärker optimiert werden müssen, behandle ich mit Raw Queries.
Ich denke, ob ORM oder Raw Query, sollte man je nach Situation passend danach auswählen, was man wie bauen will.
Im Allgemeinen kann ich mir vorstellen, dass das bei Daten funktioniert, deren DB-Normalisierung gut umgesetzt ist und bei denen keine großen Joins nötig sind.
Wenn man jedoch nicht alles – angefangen bei der DB-Normalisierung – sauber über einen DBA verwalten kann, kann auch ein ORM eine gute Wahl sein. Gerade die Vorteile, die entstehen, wenn man Daten auf der abrufenden Seite per Relationship statt per Join holt, sind meiner Meinung nach ein gutes Beispiel dafür, warum man überhaupt ORM verwendet.
Natürlich stimme ich der Ansicht zu, dass Frameworks das Wachstum von Entwicklern einschränken können und dass man die Abhängigkeit von Frameworks senken sollte,
aber der Meinung, man solle ORM grundsätzlich nicht verwenden, kann ich mich nicht ohne Weiteres anschließen.
Es wirkt auf mich so, als würde dabei vorausgesetzt, dass alle Unternehmen einen DBA haben und mit soliden Methoden wie DDD oder TDD entwickeln.
Wenn das in der Praxis tatsächlich so umgesetzt würde, kann ich mir gar nicht vorstellen, wie viel chaotischer der Code am Ende noch werden könnte.
Wenn ich ein Backend mit PYTHON baue, nutze ich eigentlich jedes Mal so etwas wie SQLALCHEMY oder DJANGO ORM.
Ob man SQL direkt schreibt oder ein ORM verwendet – sobald man sich daran gewöhnt hat, fühlt es sich für mich nicht nach einem großen Unterschied an, daher habe ich mir darüber nie groß Gedanken gemacht. Es gibt aber offenbar auch die Meinung, dass man ORM besser nicht verwenden sollte.
Der Ansicht, die Abhängigkeit vom Framework zu verringern, stimme ich zu. Man sollte nicht nur mit DJANGO ORM umgehen können und dann SQL selbst nicht beherrschen...
Hm, na ja, ich bin anderer Meinung. Ich betreibe derzeit einen Service mit etwa 3.000 Tabellen, und weil die Domain so komplex ist, habe ich für eine einzelne Query grundsätzlich oft schon mehrere Dutzend Zeilen geschrieben. Wenn man dann noch dynamische Queries bedenkt, wird es wirklich unerquicklich. Wegen der Komplexität sind auch viele Bugs entstanden, und die Wartung war schwierig. Ich denke, dass in komplexen Domains ein ORM eher im Vorteil ist.
In meinem Fall habe ich Erfahrung mit der Wartung einer nicht normalisierten Datenbank.
Damals habe ich dynamische Abfragen ohne ORM mit normalem SQL geschrieben,
und dadurch gab es Fälle, in denen der Code schwerer verständlich wurde.
Ich denke, dass es nicht nur bei komplexen Domains, sondern auch bei Domains mit unzureichender Normalisierung durchaus Raum für den Einsatz gibt.
Oh, dann muss man das wohl nicht negativ sehen.
Ich persönlich würde auch einfach empfehlen, direkt SQL zu verwenden. In der JS-Welt werden zwar oft Tools wie Prisma genutzt, aber SQL ist nun wirklich keine so schwierige Sprache, und ich finde es etwas abschreckend, dass für Datenbank-I/O so viel unnötige Abstraktion verlangt wird.
Vielleicht liegt es auch daran, dass es gerade im js/ts-Bereich auffallend viele ORMs gibt, die einfach unausgereift sind.
Dann reicht wohl so etwas wie JDBC, oder? Ich muss gerade daran denken, dass jemand, mit dem ich früher zusammengearbeitet habe, sagte: „JPA ist langsam, also nimm lieber etwas anderes.“
Klingt wie eine Legende.
Es wirkt fast so, als wäre es ein Trend, statt auf Frameworks eher zu den Grundlagen zurückzukehren.
HTMX, SQL und so weiter..
Es hat zwar den Nachteil, dass man das Rad neu erfinden muss.
Es hat schon auch Vorteile ..
2. MyBatis (ist zwar kein ORM, haha)
Ich hätte wohl besser nicht auf ein ORM, sondern auf DAO umsteigen sollen.