„Was genau ist dieses Projekt eigentlich?“
Das Projekt in meiner aktuellen Firma ist so etwas wie ein „Frankenstack“. MyBatis und JPA existieren im selben Projekt nebeneinander. Das Problem war nur: Die „Schöpfer“, also die ursprünglichen Teammitglieder, die diesen Code geschrieben hatten, waren längst alle aus der Firma weg.
Wir Zurückgebliebenen, also das aktuelle Team, waren verwirrt. Es gab kein einziges Dokument, das erklärte, nach welchen Kriterien diese Technologien gemischt worden waren. Dadurch wurde die Lage nur immer chaotischer.
- Entwickler A: „Mit SQL fühle ich mich wohler.“ -> schreibt die gesamte Logik mit MyBatis.
- Entwickler B: „Ich mag Objektorientierung.“ -> schreibt die gesamte Logik mit JPA.
Am Ende lief es darauf hinaus, dass innerhalb desselben Services manche Leute für ähnliche Funktionen XML wühlten, während andere Entities modellierten. Das Ganze war zu einer merkwürdigen Form technischer Anarchie geworden. Das Projekt verwandelte sich immer mehr in ein Monster, das kaum noch wartbar war, bis schließlich ein Meeting einberufen wurde.
„Lasst uns den Tech-Stack auf eine Technologie vereinheitlichen.“

„JPA ist doch zu langsam, oder?“
Die Stimmung im Meeting war angespannt. Vor allem Kolleginnen und Kollegen, die an MyBatis gewöhnt waren, lehnten eine Vereinheitlichung auf JPA stark ab. Ihr zentrales Argument war die Geschwindigkeit.
„Erinnert ihr euch nicht mehr an die Statistikseite, die wir mit JPA gebaut haben und die 20 Sekunden zum Laden gebraucht hat? Wenn wir das SQL direkt schreiben, kommt das Ergebnis in einer Sekunde. Ein Blackbox-Tool, das wir nicht voll kontrollieren können, ist riskant.“
Tatsächlich brauchten manche Logiken allein für das Laden der Daten mehrere Dutzend Sekunden. Aber ich war überzeugt, dass das kein Problem der Technologie war, sondern ein Problem der Erfahrung. Noch im Meeting klappte ich meinen Laptop auf und zeigte den berüchtigten „20-Sekunden-Code“.
Der Code war katastrophal. Es wurde ein List<Entity> abgefragt, und jedes Mal, wenn die Schleife ein verknüpftes Objekt berührte, wurde die Datenbank erneut angesprochen, Stück für Stück. Ein klassisches N+1-Problem.
„Das bedeutet nicht, dass JPA langsam ist. Es bedeutet, dass wir JPA ineffizient arbeiten lassen. Schaut her.“
Ich setzte spontan einen Fetch Join ein und reduzierte die Abfrage auf einen einzigen Zugriff. Nach dem Deployment sank die Ladezeit von 20 Sekunden auf 1 Sekunde. In diesem Moment veränderte sich der Blick meiner Kolleginnen und Kollegen.

Unerwartete Erkenntnis: kein Mischmasch, sondern ein Hybrid
Nachdem das Missverständnis rund um JPA ausgeräumt war, hätten wir dann einfach alles auf JPA vereinheitlichen sollen? Wenn man genauer hinschaute, war auch das nicht die richtige Antwort.
Komplexe Statistikabfragen oder Excel-Downloads mit Hunderttausenden von Datensätzen über JPA abzuwickeln war unpraktisch: Die Kosten für die Objektabbildung waren zu hoch, und dynamische Queries ließen sich nur umständlich schreiben. MyBatis war für solche Aufgaben dagegen deutlich intuitiver und schneller. Ich fragte mich: „Wie lösen andere Firmen dieses Problem?“ Also suchte ich die halbe Nacht lang auf Google und durchforstete Tech-Blogs.
Überraschenderweise beharrten viele Technologieunternehmen gar nicht nur auf JPA. Manche nutzten QueryDSL für performante komplexe Leseabfragen, andere setzten, genau wie wir, zusätzlich auf MyBatis.
Da machte es bei mir Klick. „Ah, die Leute vor uns haben das also doch nicht gedankenlos zusammengemischt.“
- Zuständigkeit von JPA: Business-Logik wie das Speichern, Ändern und Löschen von Daten. Die Vorteile der Objektorientierung werden genutzt, um die Wartbarkeit zu erhöhen.
- Zuständigkeit von MyBatis: Reine Leselogik wie komplexe Bildschirmausgaben oder Statistiken. Die Stärke von SQL wird genutzt, um die Performance zu maximieren.
Sie kannten bereits die Vor- und Nachteile der einzelnen Werkzeuge und hatten die Technologien bewusst nach Aufgaben getrennt. Das eigentliche Problem war nur, dass sie ohne Dokumentation gegangen waren und wir, die das System übernommen hatten, deshalb darüber stritten, warum man es nicht vereinheitlicht hatte.
Über die Grenzen von JPA hinaus: QueryDSL und Projection
Dann stellte sich die nächste Frage: Können wir MyBatis entfernen und dieses Problem nur mit Technologien aus dem JPA-Ökosystem lösen? Genau hier kommen QueryDSL und Projection ins Spiel.
- Der König der dynamischen Queries: QueryDSL Statt mit den XML-Tags von MyBatis schreibt man Queries als Java-Code. Tippfehler werden schon als Compile-Fehler abgefangen, das ist sicher, und dynamische Queries zusammenzubauen, indem man Methoden wie
where(nameEq(name))kombiniert, macht richtig Spaß. - Der Retter der Statistik-Queries: Interface Projection Statistiken bestehen meist aus aggregierten Daten wie
SUM,AVGoderCOUNT. Das ist keinEntity. Dafür mit JPA die schweren Entities komplett zu laden, ist Verschwendung. Setzt man hier eine Interface Projection ein, werden aus der DB nur genau die benötigten Spalten (skalare Werte) herausgeholt und direkt in ein Interface gesteckt. Man muss nicht einmal ein DTO anlegen. Leicht, schnell und unkompliziert.
// 1. Interface, das nur die benoetigten Felder definiert (kein DTO noetig)
public interface DailyStat {
String getDate();
Long getTotalSales();
}
// 2. Abfrage ueber QueryDSL oder ein JPA-Repository
// Es werden nur diese beiden Spalten aus der DB geSELECTet, also so schnell wie MyBatis.
List<DailyStat> stats = repository.findDailySales();
Als ich diese Werkzeuge verstanden hatte, gab es deutlich weniger Grund, in die XML-Hölle von MyBatis zurückzukehren. Auch im JPA-Ökosystem lassen sich schließlich sehr performante Queries bauen.
Praxis-Tipp: Nicht streiten, sondern sinnvoll koexistieren lassen
Am Ende entschied sich unser Team nicht für eine bedingungslose Vereinheitlichung, sondern für eine „Koexistenz mit klaren Prinzipien“. (Langfristig wollten wir allerdings in Richtung QueryDSL gehen.)
- Grundprinzip: Die gesamte Business-Logik (Registrierung, Bestellung, Zahlung) wird mit JPA entwickelt, um die Vorteile der Objektorientierung zu nutzen.
- Ausnahmeprinzip: Für komplexe, rein lesende Logik wie Statistiken, Dashboards oder große Excel-Downloads ist MyBatis erlaubt. (Datenänderungen sind damit allerdings tabu.)
Interessant ist, dass diese Überlebensstrategie, für die wir uns entschieden haben, also „Schreiben (Command) mit JPA, Lesen (Query) mit spezialisierten Werkzeugen“, aus architektonischer Sicht eine sehr einfache Form des CQRS(Command and Query Responsibility Segregation)-Musters ist.
Man muss die Datenbank dafür nicht groß aufsplitten. Schon die Philosophie, Verantwortlichkeiten für Commands und Queries auf Code-Ebene zu trennen, macht ein Projekt deutlich sauberer.
Zum Schluss: Technologie ist nicht schuld
Wir diskutieren oft: „JPA ist das Beste“ oder „Nein, MyBatis funktioniert in der Praxis viel besser“. Aber was ich aus dieser Erfahrung gelernt habe, ist: Es gibt keine schlechte Technologie. Es gibt nur Situationen, in denen man eine Technologie für den falschen Zweck einsetzt.
JPA ist wie ein Zauberstab, aber wenn man den falschen Spruch aufsagt, explodiert er. MyBatis ist wie ein robuster Hammer, aber er kann dazu führen, dass alles wie ein Nagel aussieht.
Wenn euer Team gerade wegen Legacy-Code oder wegen unterschiedlicher Technologievorlieben aneinandergerät, haltet kurz inne. Vielleicht steckt in diesem Chaos das tiefe Nachdenken früherer Entwicklerinnen und Entwickler. Es zu finden und in klare Regeln zu verwandeln, genau das ist der Weg zu einem wirklich starken Entwicklungsteam.


