Naar content
Trending apps
  • Google Meet

  • Google Duo: videogesprekken van hoge kwaliteit

  • Maps: Navigatie en OV

  • WhatsApp Messenger

  • Messenger

Trending games
  • Fortnite

  • Minecraft Earth

  • Dr. Mario World

  • Harry Potter: Wizards Unite

  • Breaking Bad: Criminal Elements

Trending smartphones
  • OPPO Find X2 Pro

  • Nokia 8.3

  • Samsung Galaxy A51

  • Poco F2 Pro

  • Xiaomi Mi Note 10 Pro

Nieuwste tablets
  • Samsung Galaxy Tab S6

  • Samsung Galaxy Tab A 10.5

  • Samsung Galaxy Tab S4

  • Samsung Galaxy Tab S3 9.7

  • Asus Zenpad 3S 10

DevTutorial 9 – OrmLite for Android: relaties tussen je objecten opslaan

· 27 oktober 2011

 

Deze DevTutorial is een directe aansluiting op de vorige DevTutorial. De vorige DevTutorial ging over het opslaan van dezelfde objecten in één tabel van je SQLite database. Deze DevTutorial gaat je leren hoe je gegevens uit verschillende tabellen (in dit geval Twitter-gebruikers en Tweets) aan elkaar kan koppelen.

Wat gaan we doen

Al is deze DevTutorial een korte, er staan wel weer een aantal belangrijke onderwerpen in:
  • Hoe je de ForeignCollection<T> en verbinding kan houden tussen één object van je ene tabel en meerdere objecten van hetzelfde type uit een andere tabel.
  • Hoe je een leeg ForeignCollection<T> object kan aanmaken door de method getEmptyForeignCollection op je Dao object aan te roepen.
  • Hoe je meerdere objecten van één tabel kan koppelen uit één bepaald object uit de andere tabel (dit is 'de andere kant' van het eerste punt van deze opsomming).
  • Hoe je de DatabaseHelper class moet aanpassen als je een nieuw type object wilt gaan opslaan in de database.
Dit is de een-na-laatste DevTutorial uit deze reeks. Succes er weer mee!

TwitterUser class laten verwijzen naar meerdere Tweet-objecten

De vorige DevTutorial legde je aan het begin uit hoe kaartenbakken uit een ouderwets bedrijf te vergelijken zijn met tabellen in een database. Aan het einde van die uitleg werd geschreven over hoe je touwtjes kan knopen tussen kaarten uit verschillende kaartenbakken zodat je gemakkelijk kan zien welke kaarten bij kaarten horen uit een andere kaartenbak. In ons geval hebben we een kaartenbak met kaarten van Twitter-gebruikers en een kaartenbak met kaarten van Tweets. We willen touwtjes knopen tussen elke kaart uit de Twitter-gebruikers kaartenbak en de kaarten die daarbij horen uit de Tweets kaartenbak. Wanneer je dan een kaart uit de Twitter-gebruikers kaartenbak omhoog tilt kan je zien welke Tweets daarbij horen. Je kan ook zeggen: welke Tweets zijn gerelateerd aan welke Twitter-gebruiker. Of in database-taal: zodra je een TwitterUser object hebt wil je weten wat de gerelateerde Tweet objecten zijn. Het bijhouden van gerelateerde objecten in de database doe je met de ForeignCollection<T> interface, die is te zien op deze pagina. Deze class zorgt ervoor dat je een verzameling (of lijstje) van objecten kan bijhouden in de betreffende class die via de SQLite database van Android aan het object verbonden zijn. Net zoals een ArrayList<T> of Dao<T, ID> moet je aangeven met welk generiek object type je deze class wil gebruiken. Als je de ForeignCollection<T> class gebruikt in je TwitterUser class en je wil bijhouden welke Tweet classes erbij horen, kan je een variabelen definiëren van het type ForeignCollection<Tweet> met bijvoorbeeld de naam mTweets:Ook bij deze variabele moet je een annotatie toevoegen, maar dan van het type @ForeignCollectionField uit de package com.j256.ormlite.dao. Als je dan ook de get-method en set-method opschrijft dan kom je uit op de volgende annotatie, variabele en methods:
  • Voeg deze allen toe aan je class TwitterUser.
In de method addTweets gebruik je als parameter een object dat de interface Collection<Tweet> implementeert (dus een object dat op zijn minst de methods heeft die op deze pagina beschreven staan). Dat het object de interface Collection<Tweet> implementeert, heb je nodig omdat je de method addAll wil aanroepen op het ForeignCollection<Tweet> object. Zoals je hier kan zien implementeert de ForeignCollection<T> op zijn beurt ook de Collection<T> interface. En die interface heeft de method addAll zoals je op die pagina kan zien. Als je de method addAll wilt kunnen gebruiken op mTweets moet je als parameter een object meegeven die de Collection<T> interface implementeert, of omdat je een ForeignCollection<Tweet> object gebruikt, moet je een object hebben dat de Collection<Tweet> interface implementeert. Je hebt nu wel een variabele van het type ForeignCollection<Tweet> gedefinieerd die je Tweet objecten kan opslaan, maar voordat je Tweet objecten in die variabele kan opslaan moet je hem eerst vullen met een object van het type ForeignCollection<Tweet>. Je zou daarvoor het woordje new kunnen gebruiken om daarna deze constructor aan te roepen. Het 'probleem' van die constructor is dat hij erg veel parameters nodig heeft (inclusief een Dao<T, ID object) om aangeroepen te kunnen worden. Je kan hiervoor beter de method getEmptyForeignCollection<T> gebruiken die je moet aanroepen op het Dao<T, ID> object waar je mee werkt. Op die method moet je als parameter meegeven voor welke variabele je een leeg ForeignCollection<T> object wil opvragen. Bijvoorbeeld in het geval van TwitterUser kan je daarvoor de volgende code gebruiken vanuit je MainActivity class:Maar  om deze code in te toekomst uit te kunnen voeren  moet je daarvoor inderdaad nog wel de method setTweets hebben.
  • Voeg deze method toe aan je TwitterUser class:
Je hebt nu dus een method om Tweets toe te voegen aan je verzameling van mTweets in TwitterUser  (addTweets) en je hebt een method om de variabele die de Tweet objecten bijhoudt in te stellen (setTweets).
Je hoeft de method setTweets alleen maar te gebruiken als je net zelf een nieuw TwitterUser object hebt aangemaakt via de constructor met het woordje new. Als je een object ophaalt van de SQLite database met een Dao object dan heeft het Dao object automatisch al de de waarde van variabele mTweets veranderd van null naar een ForeignCollection<Tweet>object: ook als er helemaal geen gerelateerde Tweet objecten bestaan (dan heb je een ForeignCollection<Tweet> object waar verder geen elementen in zitten, maar waar je dus al wel onder andere de method addAll op kan aanroepen).In de code die we zo gaan schrijven halen we telkens opnieuw een TwitterUser object op van de database die we in de vorige Activity al hadden aangemaakt: dan kunnen we dus meteen de method addTweets gebruiken.
De TwitterUser class is nu klaar om via je SQLite database gekoppeld te worden aan meerdere Tweet objecten. Of anders gezegd: de tabel die straks door OrmLite gegenereerd wordt voor je TwitterUser objecten kan aangesloten worden op de tabel die straks gegenereerd wordt voor je Tweet objecten. De tabel voor de Tweet objecten moeten we hier ook op voorbereiden: dat doen we zometeen in de Tweet class. Je wil straks per Twitter-gebruiker ook bijhouden wanneer je zijn of haar Tweets voor het laatst gedownload hebt. In de vorige DevTutorial heb je eenzelfde soort truukje geprogrammeerd toen je bij ging houden wanneer je de Twitter-gebruiker zelf voor het laatst had gedownload. Toen hield je dat bij met een heel groot getal waaruit je kon afleiden hoe lang het geleden was dat de Twitter-gebruiker gedownload was. Dit gaan we op dezelfde manier opslaan om bij te houden wanneer de Tweets die horen bij een bepaalde Twitter-gebruiker voor het laatst gedownload waren.
  • Voeg de variabele mLastTweetsUpdate toe aan je class en voeg ook de volgende annotatie en methods toe:
Deze variabele (die nu ook automatisch wordt opgeslagen in de SQLite database) gaan we straks een waarde geven om hem vóór een volgende Tweets-download uit te kunnen lezen.
Je hebt nu twee keer tegelijkertijd de variabelen (inclusief de annotatie) en de daarbij horende methods toegevoegd. De code die je moest invoegen kan je wel één-op-één kopieren, maar je kan beter al je variabelen bovenin je class zetten, en alle get-methods en set-methods onderaan je class zetten. De andere methods zet je dan in het midden van je class. Hierdoor houd je je class overzichtelijk.

Tweet class laten verwijzen naar één TwitterUser object

Nu de TwitterUser class klaar is om verbindingen te maken met meerdere Tweet objecten moeten we de Tweet class hierop aanpassen. Om überhaupt een Tweet op te kunnen slaan wil je de variabelen mCreatedAt en mText op kunnen slaan.
  • Voeg de benodigde notaties toe zodat je variabelen in de Tweet class er als volgt uit zien:
Zoals je weet moet elk object dat je in een tabel opslaat in een tabel een unieke naam of nummer hebben (tenminste, het gebruiken van een id is niet altijd verplicht, maar is meestal erg handig). Omdat Twitter.com zelf ook zijn Tweets opslaat gebruikt zij hiervoor ook al een id, en deze wordt standaard meegegeven in de JSON. Als je kijkt in de JSON die je terugkrijgt voor het opvragen van Tweets dan zie je het id element ergens staan:Dit element gaan we gebruiken als id.
  • Voeg de variabele mId van het type long toe aan je Tweet class en voeg daarbij de juiste annotatie toe met als parameter dat je deze variabele als id wil gebruiken:
  • Zorg er ook voor dat de variabele wordt ingelezen in je constructor; voeg deze code toe in je constructor:
Nu gaan we ervoor zorgen dat een Tweet object een relatie kan bijhouden naar een TwitterUser object. Gelukkig is dat een stuk makkelijker. Dat heeft te maken met het feit dat een Tweet object maar één TwitterUser object hoeft te onthouden terwijl een TwitterUser object meerdere Tweet objecten moet kunnen onthouden. Om er voor te zorgen dat een Tweet object een TwitterUser object kan bijhouden hoef je alleen maar een TwitterUser variabele te definieren en daarvoor een extra parameter (foreign = true) mee te geven in de annotatie. Verder heb je de gebruikelijke set-method en get-method nodig omdat je anders niet de koppeling naar een TwitterUser object kan opvragen of instellen:
  • Voeg deze code toe in je Tweet class.
Net zoals bij de class TwitterUser uit de vorige DevTutorial moet je ervoor zorgen dat je class een constructor heeft zónder parameters. Dit heeft OrmLite nodig als het automatisch objecten van je Tweet class gaat inladen vanaf je SQLite database.
  • Voeg deze code toe in je class block (en maak hem bij voorkeur niet-public omdat je hem zelf niet nodig hebt in je code):
Je Tweet class en je TwitterUser class zijn nu allebei klaar om aan elkaar gekoppeld te worden.

DatabaseHelper class aanpassen

Zoals je weet is je DatabaseHelper class de verbinding tussen de objecten die je wilt opslaan in de database en de database zelf. Omdat we ook Tweet objecten willen opslaan moeten we ervoor zorgen dat er bij de method onCreate en onUpdate respectievelijk een tabel voor de Tweet class wordt aangemaakt en wordt verwijderd. Ook moet er een method aangemaakt worden die een Dao<Tweet, Long> object aanmaakt en deze opslaat in de variabele mTweetsDao die je moet definieren in je class block. Verder moet in de method close de mTweetsVariabele op null worden gezet.
  • Voeg de volgende code toe op de juiste plekken in je DatabaseHelper class:

Tweets laden in de TweetsActivity class vanuit de SQLite database

Al je classes zijn gereed om nu ook Tweet objecten op te kunnen slaan, je hoeft ze alleen nog maar op de juiste manier aan te spreken in je TweetsActivity class. We gaan hiervoor dezelfde soort methods gebruiken die je gebruikte in de de vorige DevTutorial om toe te voegen aan je MainActivity class:
  • getDatabaseHelper
  • onDestroy
  • downloadOrShowFromDb
  • saveToDbAndShow
De eerste twee methods van dit lijstje zijn identiek aan de methods uit de vorige DevTutorial.
  • Voeg de eerste twee methods toe en definieer ook de variabelen van het type DatabaseHelper die je hiervoor nodig hebt.
In de method onCreate van je TweetsActivity class lees je de naam van de Twitter-gebruiker uit en stop je die in de variabele mUsername. Omdat we nu meer met het TwitterUser object gaan werken kan je beter meteen al in de method onCreate het juiste TwitterUser object opvragen.
  • Verwijder de variabelen van het type String met de naam mUsername uit je class block en haal de gerelateerde code weg die in onCreate staat.
  • Voeg een variabele toe in je class block van het type TwitterUser met de naam mTwitterUser.
  • Zorg ervoor dat in je method onCreate mTwitterUser gevuld wordt met het juiste TwitterUser object:
  • Maak de method downloadOrShowFromDb aan, en vul hem met een if block en een else block.
  • De vraag die je bij het if block moet stellen lijkt heel erg op de method downloadOrShowFromDb uit de vorige DevTutorial, maar in plaats daarvan moet je de method getLastTweetsUpdate gebruiken op de mTwitterUser variabele. Gebruik deze vraag in je if block.
Binnenin het if block moet je de method getTweets gebruiken op de mTwitterUser variabele. Hierdoor krijg je de tweets die bij de Twitter-gebruiker horen. Deze zijn dan aanwezig in mTwitterUser omdat je net de annotaties @ForeignCollectionField en@DatabaseField (foreign = true) hebt toegevoegd aan beide classes. Hierdoor zoekt OrmLite namelijk automatisch de Tweets objecten op die bij dit TwitterUser object horen.
  • Maak een nieuwe object aan van het type ArrayList<Tweet> en stop die in de variabele mTweets.
  • Gebruik de method addAll op mTweets en geef als parameter het resultaat van de method getTweets mee.
  • Vervolgens kan je method updateView aanroepen in je if block.
  • Kopieer de inhoud van method startDownloadingTweets naar het else block in de method downloadOrShowFromDb.
  • Je method downloadOrShowFromDb moet er nu als volgt uit zien:
  • Verwijder de method startDownloadingTweets en zorg ervoor dat deze niet meer wordt aangeroepen vanuit de method onCreate. In plaats daarvan moet je zorgen dat de method downloadOrShowFromDb wordt aangeroepen aan het einde van de method onCreate.
Voor het maken van de method saveToDbAndShow kunnen we de al bestaande method processResults gebruiken. Om deze method af te schrijven moeten eerst de oude Tweet objecten verwijderd worden die bij de huidige Twitter-gebruiker horen. Daarna moet je voor elk Tweet object dat je aanmaakt de method setTwitterUser gebruiken om de Tweet te koppelen aan de Twitter-gebruiker om deze vervolgens op te slaan in de database met de method create van het Dao<Tweet, Long> object. Vervolgens moet je nog van het TwitterUser het getal veranderen dat aangeeft wanneer de Tweets voor het laatst waren gedownload door de method setTweetsLastUpdate aan te roepen. En deze wijzigingen op je TwitterUser object moet je natuurlijk ook opslaan. Voor het verwijderen van de oude Tweets die bij de Twitter-gebruiker horen kan je deze code gebruiken (dit kan je het beste doen vóór het for block):Voor het het koppelen van elke Tweet aan een TwitterUser en voor het vervolgens wegschrijven naar de database kan je deze code gebruiken (dit kan je het beste doen onderin het for block):Voor het aanpassen van het getal dat aangeeft wanneer voor de laatste keer de Tweets waren gedownload kan je deze code gebruiken (dat kan je het beste doen ná het for block).
  • Verander de naam van de method processResults naar saveToDbAndShow en voer deze wijziging ook door in de method onPostExecute van je inner class DownloadTweetsTask.
  • Voeg al deze drie bovenstaande stukken code toe op de juiste pekken in je method saveToDbAndShow.
  • Je krijgt een suggestie om een extra catch block toe te voegen, voeg deze toe.
  • Zorg ervoor dat aan het einde van de method saveToDbAndShow de method updateView wordt aangeroepen; haal dit juist weg in de method onPostExecute van je inner class DownloadTweetsTask.
  • Je method moet er nu als volgt uitzien:
Je hebt net meerdere annotaties toegevoegd aan de TwitterUser class en de Tweet class. Hierdoor heb je één nieuwe tabel in je database nodig waarin Tweets opgeslagen moeten kunnen worden. Ook wordt de tabel voor Twitter-gebruikers iets aangepast zodat bijgehouden kan worden wanneer je de Tweets van een bepaalde Twitter-gebruiker voor het laatst gedownload hebt (er komt een extra kolom in die tabel). Je moet daarom aangeven in de DatabaseHelper class dat je met een nieuwere database-versie wil werken. Hiervoor moet je de statische int variabele DATABASE_VERSION ophogen naar het volgende getal (bij mij staat dat getal inmiddels op 4, maar het kan zijn dat die van jou hoger of lager is, afhankelijk van hoe vaak je annotaties of clases hebt verwijderd of toegevoegd). Als je nu je app opstart worden alle tweets die je downloadt, automatisch opgeslagen in de database. Nu hoef je niet meer opnieuw te downloaden als je binnen één minuut de Tweets opvraagt van dezelfde gebruiker:).

Sourcecode en Forum

Als je vragen hebt over deze DevTutorial dan kan je ze stellen aan mij en andere programmeurs in deze forum thread. Als je wil weten hoe de code van deze app er uiteindelijk uit moet zien, kan je hier de sourcecode bekijken op github.

Spelfouten, taalfouten of inhoudelijke fouten ontdekt? Stuur dan een mailtje naar de auteur van dit artikel!

Reacties (8)
Bezig met laden van reacties...