MyBatis vs JPA: det finns ingen dålig teknik.

”Vad är det här projektet egentligen?”

Projektet på mitt nuvarande jobb är vad man skulle kunna kalla en ‘frankenstack’. MyBatis och JPA samexisterar i samma projekt. Problemet var att alla de ursprungliga ‘skaparna’, alltså de första teammedlemmarna som skrev den här koden, redan hade slutat på företaget.

Vi som blev kvar, det nuvarande teamet, var förvirrade. Det fanns inte ett enda dokument som förklarade på vilka grunder teknikerna hade blandats. Därför blev situationen bara mer och mer kaotisk.

Till slut hade det blivit så att vissa satt och grävde i XML för att bygga liknande funktioner, medan andra designade entiteter, allt inom samma tjänst. Det hade förvandlats till en bisarr form av ‘teknisk anarki’. Projektet blev alltmer ett monster som var svårt att underhålla, och till slut kallades ett möte in.

”Vi borde standardisera det här till en enda teknikstack.”

Att blanda tekniker utan tydliga principer är inte frihet, det är bara förvirring.

”Är inte JPA för långsamt för att använda?”

Stämningen på mötet var spänd. Särskilt kollegor som var vana vid MyBatis motsatte sig starkt att standardisera allt på JPA. Deras huvudargument var hastighet.

”Kommer ni inte ihåg statistik-sidan vi byggde med JPA som tog 20 sekunder bara att ladda? Om vi skriver SQL direkt kommer resultatet på en sekund. Det är riskabelt att lita på en svart låda som vi inte kan kontrollera fullt ut.”

Det stämde att viss logik tog tiotals sekunder bara för att ladda data. Men jag var säker på att det inte var ett teknikproblem utan ett kompetensproblem. Jag slog upp laptopen direkt på plats och tog fram den beryktade ‘20-sekunderskoden’.

Koden var bedrövlig. Den hämtade en List<Entity>, men varje gång loopen kom åt ett relaterat objekt gick den tillbaka till databasen igen, ett i taget. Det var ett klassiskt N+1-problem.

”Det här betyder inte att JPA är långsamt. Det betyder att vi fick JPA att arbeta ineffektivt. Titta här.”

Jag lade direkt till ett Fetch Join och reducerade frågan till en enda träff. Efter deploy gick laddningstiden från 20 sekunder till 1 sekund. Det var då jag såg hur mina kollegors blickar förändrades.

Innan du skyller på verktyget, läs manualen först.

En oväntad upptäckt: inte ett hopkok, utan en hybrid

När missförstånden kring JPA var utredda, borde vi då bara standardisera allt på JPA? När vi tittade närmare visade det sig att inte heller det var hela svaret.

Att låta JPA hantera komplexa statistikfrågor eller Excel-exporter med hundratusentals rader blev snabbt opraktiskt: objektmappningen kostade för mycket och dynamiska frågor var besvärliga att skriva. MyBatis var däremot betydligt mer intuitivt och snabbare för just sådant arbete. Jag började undra: ”Hur löser andra bolag det här?” Frustrerad satt jag uppe hela natten och sökte på Google och läste teknikbloggar.

Det förvånade mig att många teknikbolag inte alls höll fast vid bara JPA. Vissa använde QueryDSL för komplex läsprestanda, och andra använde, precis som vi, även MyBatis vid sidan av.

Då föll bitarna på plats. ”Aha, de som lämnade hade alltså inte blandat det här utan eftertanke.”

De kände redan till varje verktygs styrkor och svagheter och hade medvetet delat upp ansvarsområden efter teknik. Det verkliga problemet var bara att de försvann utan att dokumentera besluten, så vi som tog över systemet stod och bråkade om varför allt aldrig hade standardiserats.

Bortom JPA:s gränser: QueryDSL och Projection

Då kom nästa fråga: kunde vi ta bort MyBatis och lösa problemet enbart med teknik från JPA-ekosystemet? Det är här QueryDSL och Projection kommer in.

// 1. Ett granssnitt som bara deklarerar de falt du behover (inget DTO)
public interface DailyStat {
    String getDate();
    Long getTotalSales();
}

// 2. Hamta via QueryDSL eller ett JPA Repository
// Bara de tva kolumnerna SELECTeras fran databasen, sa det ar lika snabbt som MyBatis.
List<DailyStat> stats = repository.findDailySales();

När jag väl förstod de verktygen fanns det mycket mindre anledning att gå tillbaka till MyBatis XML-helvete. Det gick ju att skriva riktigt snabba frågor inom JPA-ekosystemet också.

Praktiskt råd: sluta bråka och låt dem samexistera

Till slut valde vårt team inte ‘total standardisering’, utan ‘principfast samexistens’. (Och på lång sikt siktade vi på att gå över till QueryDSL.)

Det intressanta är att den här överlevnadsstrategin vi valde, ‘JPA för writes, ett specialiserat verktyg för reads’, ur ett arkitekturperspektiv egentligen är en mycket grundläggande form av mönstret CQRS(Command and Query Responsibility Segregation).

Man behöver inte ens dela upp databasen på ett storslaget sätt. Bara filosofin att skilja ansvar för kommandon och läsningar på kodnivå gör projektet mycket renare.

Avslutning: tekniken är inte skyldig

Vi hamnar ofta i diskussioner som ”JPA är bäst” eller ”Nej, MyBatis är det som faktiskt fungerar i verkligheten”. Men det jag lärde mig av den här erfarenheten är att det inte finns någon dålig teknik. Det finns bara situationer där en teknik används till fel uppgift.

JPA är som en trollstav, men om du uttalar formeln fel exploderar den. MyBatis är som en stadig hammare, men den kan få allt att se ut som en spik.

Om ditt team just nu brottas med legacy-kod eller olika teknikpreferenser, stanna upp en stund. Kanske finns det djupa överväganden från tidigare utvecklare dolda i all den här röran. Att hitta dem och förvandla dem till tydliga regler, det är så ett utvecklingsteam blir riktigt starkt.

Lämna en kommentar