Handige Java-libraries: MapStruct en Caffeine Cache

-

Een tijdje geleden mocht ik met een team goede collega’s een project voor stichting Roparun uitvoeren. Dat is een stichting die een sponsorloop organiseert, tussen Rotterdam en Parijs. Inmiddels zijn er meerdere looproutes toegevoegd en doen er teams mee uit heel Nederland. Het was een project wat ik erg leerzaam vond en het was ook leuk om samen met veel slimme collega’s leuke functies te bouwen.

Ik wil een paar technische dingen noemen die ik ben tegengekomen en welke best handig zijn om te gebruiken. Soms moet je het maar net een keertje horen of zien.
 
Eenvoudig Javabonen omzetten
Als je in de Java backend aan het ontwikkelen bent, kom je vaak situaties tegen waarin je objecten moet converteren van één type Java-object naar het andere. Je kunt hier prima zelf wat code voor gaan typen en dat werkt ook gewoon. Maar bij elke aanpassing in je originele object moet je die aanpassen in het doelobject en daarnaast ook nog in de converter-logica om het te laten werken. Het vaakst zie je dit bij het omzetten van een data transfer object (DTO) naar een domeinentiteit. Als je met Spring (Boot) werkt, kom je dat -als het goed is- vaak tegen. Je wilt bijvoorbeeld niet je gehele entiteit naar buiten publiceren, maar een gedeelte of je wilt nog nabewerkingen doen. Bij het Roparun project gebruiken we hiervoor de library ‘MapStruct’. Voor deze omzettingen is deze library prima geschikt. Als je geen bewerkingen hoeft te doen, hoef je alleen maar een declaratie te geven van je omzettingsmethode. Zoals bijvoorbeeld:

Deze plaats je dan in dit geval in de PassageMapper-class en door de MapStruct preprocessor (welke je eenmalig in je pom.xml declareert) wordt de code bijgehouden en is altijd beschikbaar om aan te roepen.
Als je een nabewerking wilt doen, welke niet al te ingewikkeld is, kun je het op deze manier met annotaties declareren:

Dus in de expressie staat een String, welke een methode aanroept om de data om te zetten (String naar datum). Syntaxvalidatie is helaas niet beschikbaar, aangezien het in een tekstblok staat.
Of als je juist een veld wilt overslaan, om te voorkomen dat die ook in je Rest-API op te vragen is, kun je dat zo eenvoudig doen:

Kortom, een handige library die je tijd bespaart en het werken met DTO’s en entiteiten een stuk gemakkelijker maakt. Als iemand voor Java nog een handigere manier weet, dan hoor of lees ik het uiteraard graag.
 
Caching met koffiebestanddelen
Een andere handige library welke ik heb leren kennen is een cache-library. Hij lijkt op de bekende Google Guava-cache-implementatie, omdat hij zijn API hierop heeft gebaseerd. Het gaat hier over ‘Caffeine Cache’ welke een in-memory caching library is. Hij zorgt ervoor dat je alleen nog maar hoeft na te denken wanneer je een cache wilt gebruiken en op welke manier een object mag verlopen. Bijvoorbeeld door een tijdgebaseerde aanpak, of met een max-grootte van de cache, of op basis van gebruik. Bij Roparun gebruiken we dit om onder andere Teamdata te cachen. We moeten namelijk 350 teams ophalen en voor elk team nog 3 subqueries (met logica erbij) doen om alle benodigde data bij elkaar te rapen. Dankzij een handige caching-aanpak loopt dit nu heel snel. Ik licht het hieronder toe.
 
Gebruik van deze cache-library in ons project
Onze service-functie getTeams() heeft een cache waarin na de eerste aanroep alle teams worden geladen. En om elk team te vullen met details, roept hij een andere methode aan, getTeam(id) welke weer een cache heeft per team-id.
Als er dan later een team wordt gewijzigd, kun je met een annotatie aangeven dat alleen dat specifieke team uit de cache weg moet.

En die wordt bij een volgende aanroep weer efficiënt gevuld door de losse methode-calls naar getTeam(id) die 99% van de keren daar weer uit die cache komen. Op deze manier zijn zowel de losse teams als de gehele lijst steeds paraat door een snelle cache. En als er een entiteit wordt verwijderd uit de cache, hoeven nooit alle entiteiten uit de database te worden gehaald.

In het begin hadden we eerst allerlei losse servicefuncties, zoals getTeamProgress, getTeamDetails etc. Maar die zijn nu allemaal overbodig geworden, omdat het nu goedkoper is om dat op te halen en te cachen en terug te geven in een TeamDTO als geheel. Dus dankzij de cache wordt de serviceopbouw eigenlijk simpeler. Waar je wel goed om moet denken is dat je op de juiste plaatsen je cache-entries laat verlopen. Anders wordt er iets geüpdated en ziet een eindgebruiker dat niet terug.

Welke handige tools/libraries raad jij ons aan? Zo kunnen we elkaar inspireren en worden we samen slimmer.