Zoals vermeld in deel 1 gebruik ik nu Room als ORM in de nieuwe app. Het is voor mij ook de eerste kennismaking ermee, dus even wennen. Ik heb al eerder met ORM´s gewerkt, en ben vertrouwd met de algemene principes.

Als ik Room vergelijk met mijn eerdere ervaringen, dan valt één ding op. De meeste ORM´s implementeren foreign-key relaties door in de classes van de parent-entiteiten de children te representeren als een collectie (voor 1 -> n  of n -> m relaties) of een enkele referentie (voor 1 -> 1 of n -> 1, waarbij dan in de parent-class een referentie bevat naar de child-class, zodat je zoiets als book.author.name kunt schrijven. In dit voorbeeld ontstaat dan een impliciete query om de author-entiteit, behorende bij het boek, te verkrijgen. Dit is een bewuste keuze van de ontwerpers geweest, om te voorkomen dat op onverwachte momenten queries worden uitgevoerd om de onderliggende objecten te laden, en om het ¨1 + n¨ probleem te vermijden.

Een bijzondere situatie waarbij dit een probleem is, ontstaat als je een bij collectie objecten refereert aan een attribuut dat een query triggert. In dat geval voer je per object één query uit. Samen met de query om die objecten op te halen voer je dan n+1 query´s uit, waarbij n het aantal objecten is. Kort gezegd het n +1  probleem. Lastig is dat de extra queries niet expliciet, maar als een side-effect van het refereren aan de onderliggende objectcollectie uitgevoerd worden. Je kan wel een combinatie-class definiëren met daarin de parent en de childs met behulp van Relation-annotaties. In dat geval worden alle entiteiten meteen opgehaald mbv joins. Zo geef je dus expliciet aan dat je entiteiten uit een relatie ook wilt hebben.

Iets minder gaat dus vanzelf, maar de argumentatie is m.i. wel verdedigbaar. Ik heb in het verleden meegemaakt hoe dit n+1 probleem performanceproblemen in een project veroorzaakte. Eigenlijk is Room vrij eenvoudig in gebruik. Het omzetten van Kotlin data-classes naar database-rows gaat bijna automatisch, alleen het omzetten van datums en tijden daar een database-veld vereist extra code.

Bij die datums en tijden komt een opvallende keuze: welk datatype kies ik voor de representatie in de data-classes? Er blijken meer datum/tijd-classes beschikbaar binnen Android, en een keuze is dus nodig. Het is wel vreemd dat binnen Android er geen standaard keuze is. Ik kan kiezen uit java.util.Date, die is echter deprecated, dus valt af. Er is ook nog java.util.Calendar, maar die werkt samen met java.util.Date, en val dus ook af. Dan is er ook nog java.time.* namespace, met daarin genoeg mogelijkheden om goed mee te werken. Dit werkt wel pas vanaf Api level 26 (Android 8.0 Oreo). Dat is nogal krap. Op het moment van schrijven bestrijkt dat 60% van alle android-telefoons. Ik kies er toch voor, omdat de alternatieven meer gedoe zijn, en tegen de tijd dat dit af is, is de situatie vast verbeterd.

Het testen van de Room-database blijkt eigenlijk eenvoudig. Het is niet nodig om er een Instrumented test voor te gebruiken, de standaard unit-test volstaat. Dan ook nog een in-memory database gebruiken, en testen gaat dan prima. Handig als je, zoals ik, nog niet ervaren bent met Room. Zo kon ik gemakkelijk dingen uitproberen zonder meteen de hele app te draaien.

Het definiëren van de database doet Room zelf, aan de hand van de data-classes die erin komen. Er zijn annotaties om dat proces te beïnvloeden. Zo kun je een primary key aangeven, al of niet auto-incrementing, en kun je relaties definiëren