SQLite-crashcursus voor Android-ontwikkelaars

Android-applicaties kunnen toepassingsgegevens opslaan in SQLite-databases. In deze tutorial leer je hoe SQLite-databases worden ontworpen en gemanipuleerd.

Hier beginnen we met het ontwerpen en gebruiken van een eenvoudige SQLite-database om scores voor schaaktoernooien te beheren. Deze zelfstudie is bedoeld als een kort overzicht van hoe SQLite-databases werken. Deze kennis zal vervolgens worden gebruikt in toekomstige ontwikkelingstutorials om Android-applicaties met databasesturing te implementeren.

Ermee beginnen

Android-applicaties kunnen hun eigen relationele SQLite-databases maken en manipuleren. Ontwikkelaars kunnen ook databases van een bepaalde Android-emulator of -apparaat inspecteren en aanpassen met behulp van de opdrachtregelprogramma sqlite3 die wordt geleverd als onderdeel van de Android SDK-tool Android Debug Bridge (adb).

In deze tutorial gaan we ervan uit dat je enige kennis hebt van relationele databases, in theorie, maar je hebt een beetje een opfriscursus nodig voordat je ze in je Android-apps gebruikt. Voor deze specifieke zelfstudie zijn geen tools vereist; het is meer een theoretische oefening.

Als u echter van plan bent Android-applicaties te ontwikkelen die op SQLite-databases zijn gebaseerd, moet u de tools installeren die nodig zijn voor Android-ontwikkeling, zoals de Android SDK en de Eclipse IDE. Bekijk de vele andere handige Android-zelfstudies die hier beschikbaar zijn op Mobiletuts + om u te helpen deze taken uit te voeren.

Wat is SQLite?

SQLite is een lichtgewicht relationele database-engine. SQLite is snel en heeft een kleine footprint, waardoor het perfect is voor Android-apparaten. In plaats van de zwaargewicht servergebaseerde databases zoals Oracle en Microsoft SQL Server, wordt elke SQLite-database opgeslagen in een enkel bestand op schijf. Android-apps kunnen ervoor kiezen om privétoepassingsgegevens op te slaan in een SQLite-database.

Opmerking: als u bekend bent met SQL, dan is SQLite zeer eenvoudig op te halen. SQLite is in feite een uitgeklede SQL-database-engine voor ingesloten apparaten. Raadpleeg de online documentatie van SQLite voor specifieke informatie over SQLite en de bijbehorende functies, functionaliteit en beperkingen.

Een snel overzicht van databasebeginselen

Een database is eenvoudigweg een gestructureerde manier om gegevens op een persistente manier op te slaan. Gegevens worden opgeslagen in tabellen. Een tabel heeft kolommen met verschillende datatypes. Elke rij in een tabel vertegenwoordigt een gegevensrecord. Misschien vindt u het handig om een ​​tabel te zien als een Excel-spreadsheet. Voor een objectgeoriënteerd programmeringsperspectief vertegenwoordigt elke tabel in een database vaak een object (weergegeven door een klasse). Elke tabelkolom vertegenwoordigt een klassenattribuut. Elke record in een tabel vertegenwoordigt een specifiek exemplaar van dat object.

Laten we een snel voorbeeld bekijken. Laten we zeggen dat u een bedrijfsdatabase hebt met een tabel met de naam Werknemer. De tabel Werknemer kan vijf getypte kolommen hebben: EmployeeID (nummer), FirstName (string), LastName (string), Title (string) en Salary (number). U kunt dan een record toevoegen aan de database voor een werknemer met de naam John Doe en een afzonderlijke record voor een werknemer genaamd Anne Droid.

Gegevens binnen een database moeten worden geïnspecteerd en gemanipuleerd. Gegevens binnen een tabel kunnen zijn:

  • Toegevoegd (met behulp van de INSERT commando)
  • Gewijzigd (met behulp van de BIJWERKEN commando)
  • Verwijderd (met behulp van de DELETE commando)

U kunt zoeken naar specifieke gegevens in een database met behulp van een query. Een query (met de opdracht SELECT) kan één tabel of meerdere tabellen zijn. Als u een query wilt maken, moet u de tabellen, datakolommen en relevante gegevenswaarden opgeven met behulp van SQL-opdrachttaal. Elke SQL-opdracht wordt beëindigd met een puntkomma (;).

De schaaktoernooi-database

De beste manier om echt te begrijpen hoe SQLite-databases werken, is door een eenvoudig voorbeeld te doorlopen, dus laten we dit doen. Laten we doen alsof we een applicatie hebben die de scores van spelers bijhoudt van een casual schaaktoernooi. Spelersscores worden geregistreerd en aan het einde van een reeks wedstrijden wordt de winnaar bepaald. De totale toernooiscore van elke speler wordt berekend op basis van hun prestaties op:

  • Vier opwarmingsronden (gewicht: 10% van de totale score elk)
  • Eén halve finale (gewicht: 25% van totale score)
  • Eén finale (gewicht: 35% van totale score)

Opmerking: voor het toernooi kunnen spelerscores gebaseerd zijn op een formule die meespeelt in de tijd die nodig was om het spel te winnen en het type en het aantal stukken dat nog aanwezig was op het bord aan het einde van het spel. Op deze manier krijgt een sterke speler een hoge score voor het verliezen van enkele krachtige stukken en het snel winnen van het spel. Misschien worden stijl en houding door de juryleden opgenomen om plezierig en licht spel aan te moedigen. Hoe scores worden berekend, is echt niet belangrijk voor de manier waarop we onze database definiëren; we slaan ze gewoon op in de database. Voor de eenvoud gaan we ervan uit dat scores zijn gebaseerd op een schaal van 0-100.

Het database schema ontwerpen

Een databaseschema is eenvoudigweg de definitie van de structuur van de database in termen van tabellen, gegevenskolommen en dergelijke. Het schema voor onze toernooidatabase is vrij eenvoudig:

Het database schema van de TournamentScores heeft drie tabellen:

  • De tabel Spelers bevat spelersinformatie.
  • De speltafel bevat informatie over elk spel en hoeveel het meetelt voor de totale toernooiscore van de speler.
  • De GameResults-tabel bevat de game scores van alle spelers.

SQLite3 heeft ondersteuning voor de volgende algemene datatypes voor kolommen:

  • GEHEEL GETAL (getekende gehele getallen)
  • REAL (drijvende-kommawaarden)
  • TEKST (UTF-8 of UTF-16-string; gecodeerd met behulp van databasecodering)
  • BLOB (gegevensbrok)

Nadat u hebt bepaald welke kolommen nodig zijn voor elke tabel, bent u klaar om enkele tabellen in uw databaseschema te maken.

Werken met tabellen

Laten we beginnen met het creëren van de tabel Spelers. Deze tabel vereist een uniek spelers-ID om naar elke speler te verwijzen. We kunnen dit de primaire sleutel maken (om een ​​record in deze tabel uniek te identificeren) en het kenmerk auto-accredement in te stellen. Auto-inklaring betekent dat elke keer dat een nieuw spelersrecord wordt toegevoegd, het record een nieuw, uniek spelers-ID krijgt. We willen ook de voor- en achternaam van elke speler opslaan, geen toegestane nullen.
Hier kunnen we de CREATE TABLE SQL-instructie gebruiken om de Players-tabel te genereren:

CREATE TABLE Players (id INTEGER PRIMARY KEY AUTOINCREMENT, fname TEXT NOT NULL, lname TEXT NOT NULL); 

De speeltafel lijkt erg op elkaar. We hebben een uniek spel-ID nodig voor elke game. We willen ook een vriendelijke naam voor elk spel en een gewichtswaarde voor hoeveel het spel meetelt voor de uiteindelijke toernooiscore van de speler (als een percentage). Dit is de SQL-instructie om de Games-tabel te maken:

CREATE TABLE Games (id INTEGER PRIMARY KEY AUTOINCREMENT, gamename TEXT, gewicht REAL DEFAULT .10 CHECK (gewicht<=1)); 

U kunt ook tabellen verwijderen met de instructie DROP TABLE. Als u bijvoorbeeld de tabel Games wilt verwijderen, gebruikt u de volgende SQL-opdracht:

DROP TABLE Games; 

Tafels vullen met gegevensrecords

Laten we, voordat we verder gaan, wat gegevens toevoegen aan deze tabellen. Om een ​​record aan de tabel Spelers toe te voegen, moet u de kolomnamen en de waarden in volgorde opgeven. De volgende SQL-instructie gebruikt bijvoorbeeld de opdracht INVOEGEN om een ​​record toe te voegen voor schaakspeler Bobby Fisher:

INSERT into Players (fname, lname) VALUES ('Bobby', 'Fisher'); 

Terwijl we bezig zijn, voegen we nog twee spelers toe: Bart Simpson (een zeer zielige schaker) en Garry Kasparov (misschien wel de beste schaker ooit). Tegelijkertijd moeten we een aantal records toevoegen aan de tabel Games. Eerst voegen we de halve finale toe, die meetelt voor 25 procent van de toernooiscore van de speler:

INSERT into Games (gamename, weight) VALUES ('Halve finale', .25); 

Vervolgens voegen we een paar warming-up-koersen toe, die het standaardgewicht van 10 procent gebruiken:

INSERT into Games (gamename) VALUES ('Warm-up Heat 1'); 

Uiteindelijk voegen we een uiteindelijke waarde toe van 35 procent van de totale toernooiscore:

INSERT into Games (gamename, weight) VALUES ('Final', .35); 

Querytabellen voor resultaten met SELECT

Hoe weten we dat de gegevens die we hebben toegevoegd in de tabel staan? Nou, dat is makkelijk. We vragen eenvoudigweg om alle rijen in een tabel met behulp van een SELECT-instructie:

SELECT * FROM Games; 

Hiermee worden alle records in de tabel Games geretourneerd:

id gamenaam gewicht ----- --------------- ------ 1 Halve finale 0,25 2 Warm-up Heat 1 0,1 3 Warm-up Heat 2 0,1 4 Warm -up Heat 3 0,1 5 Opwarming Heat 4 0,1 6 Uiteindelijk 0,35 

Kolomaliassen en berekende kolommen gebruiken

We kunnen ook onze eigen kolommen maken en deze een andere naam geven. We kunnen bijvoorbeeld een kolomalias maken genaamd PlayerName die een berekende kolom is: het zijn de voor- en achternaam van de speler aaneengeschakeld met behulp van de || operator, gescheiden door een spatie:

SELECT fname || "|| lname AS PlayerName, id FROM Players; 

Deze zoekopdracht levert de volgende resultaten op:

PlayerName id ------------ - Bobby Fisher 1 Bart Simpsen 2 Garry Kasparov 3 

Gegevens in tabellen wijzigen

Bart's (speler id 2) achternaam is verkeerd gespeld. Om de tabel Spelers aan te passen om de juiste spelling weer te geven, kunt u de opdracht UPDATE gebruiken:

UPDATE Spelers SET lname = "Simpson" WHERE playerid = 2; 

U kunt rijen uit een tabel verwijderen met behulp van de DELETE-functie. Om bijvoorbeeld de record te verwijderen die we zojuist hebben bijgewerkt:

DELETE FROM Players WHERE playerid = 2; 

U kunt alle rijen in een tabel verwijderen door de WHERE-component niet op te geven:

DELETE FROM Players; 

Vreemde sleutels en samengestelde sleutels gebruiken

Nu we onze spelers en spellen allemaal hebben ingesteld, kunnen we de tabel GameResults maken. Dit is een meer gecompliceerde tabel. De GameResults-tabel koppelt speler-ID's uit de spelers-tabel met game-id's uit de spelentabel en geeft vervolgens de score weer die de speler heeft verdiend voor het specifieke spel. Kolommen, die op deze manier naar andere tabellen linken, worden vaak vreemde sleutels genoemd. We willen unieke combinaties van spelers en games, dus we maken een samengestelde primaire sleutel van de speler en vreemde sleutels van het spel om een ​​unieke GameResults-record te identificeren. Ten slotte handhaven we dat de scores hele getallen zijn tussen 0 en 100.

CREATE TABLE GameResults (playerid) INTEGER REFERENTIES Spelers (id), gameid INTEGER REFERENTIES Games (id), score INTEGER CONTROLEREN (score<=100 AND score>= 0), PRIMAIRE SLEUTEL (playerid, gameid)); 

(Opmerking: SQLite dwingt externe-sleutelbeperkingen niet af, maar u kunt ze hoe dan ook instellen en de beperkingen afdwingen door triggers te maken.)
Nu is het tijd om wat gegevens in te voegen in de tabel GameResults. Laten we zeggen dat Bobby Fisher (speler id 1) een score van 82 punten behaalde in de halve finale (game id 1). U kunt de volgende SQL-opdracht gebruiken om de juiste record in de tabel GameResults in te voegen:

INSERT into GameResults (playerid, gameid, score) VALUES (1,1,82); 

Laten we nu aannemen dat het toernooi wordt gespeeld en de scores worden toegevoegd aan de GameResults-tabel. Bobby is een goede speler, Bart is een vreselijke speler en Garry speelt altijd een perfect spel. Nadat de records zijn toegevoegd aan de tabel GameResults, kunnen we een SELECT * -opdracht uitvoeren om alle records in de tabel weer te geven, of kunnen we kolommen expliciet als volgt specificeren:

SELECT playerid, gameid, score FROM GameResults; 

Dit zijn de resultaten van deze zoekopdracht:

spelerid gameid score ---------- ---------- ----- 1 1 82 1 2 88 1 3 78 1 4 90 1 5 85 1 6 94 2 1 10 2 2 60 2 3 50 2 4 55 2 5 45 2 6 65 3 6 100 3 5 100 3 4 100 3 3 100 3 2 100 3 1 100 

Zoals u kunt zien, is deze lijst niet bijzonder "menselijk leesbaar".

Query's over meerdere tabellen gebruiken met JOIN

Zou het niet nuttiger zijn als de namen van de spelers en spellen werden getoond in plaats van hun numerieke id's? Het opvragen en combineren van gegevens in SELECT-instructies wordt vaak afgehandeld door een JOIN uit te voeren met meerdere tabelbronnen; er zijn verschillende soorten JOINS. Wanneer u met meerdere tabellen werkt, moet u opgeven tot welke tabel een kolom behoort (met name wanneer kolommen hetzelfde worden genoemd, bijvoorbeeld met al deze verschillende id-kolommen). U kunt naar kolommen verwijzen op basis van hun kolomnaam of hun tabelnaam, vervolgens een punt (.) En vervolgens de kolomnaam.

Laten we de spelerscores opnieuw opnieuw aanbieden, maar deze keer bevatten we de naam van het spel en de naam van de speler. Ook beperken we onze resultaten alleen tot de score voor de Finale (spel-ID 6):

SELECT Players.fname || "|| Players.lname AS PlayerName, Games.gamename, GameResults.score FROM GameResults JOIN Players ON (GameResults.playerid = Players.id) JOIN Games ON (GameResults.gameid = Games.id) WHERE gameid = 6; 

wat ons de volgende resultaten geeft (je zou de WHERE kunnen verlaten om alle spellen te krijgen):

PlayerName gamename score ------------------ -------------- ----- Bobby Fisher Final 94 Bart Simpson Final 65 Garry Kasparov Laatste 100 

Meer complexe zoekopdrachten uitvoeren

De hemel is de limiet als het gaat om de vragen die u hier zou kunnen uitvoeren. Voor ons voorbeeld is de belangrijkste vraag degene die ons vertelt wie het toernooi heeft gewonnen.
Hier is de laatste vraag voor het berekenen van de toernooilanglijst:

SELECT Players.fname || "|| Players.lname AS PlayerName, SUM ((Games.weight * GameResults.score)) AS TotalWeightedScore FROM GameResults JOIN Players ON (GameResults.playerid = Players.id) JOIN Games ON (GameResults.gameid = Games.id) GROUP BY GameResults.playerid BESTELLING VOLGEGENWaardedScore DESC; 

Deze query verzamelt informatie uit verschillende tabellen met JOINs en berekent de toernooiresultaten op een leesbare manier. Laten we dus kijken naar wat deze zoekopdracht doet:

  • De volledige naam van elke speler wordt weergegeven als de eerste resultaatkolom (berekend op basis van hun voor- en achternaam)
  • De toernooiscore van elke speler wordt weergegeven als de kolom met het tweede resultaat (berekend op basis van de gewogen scores van alle spellen die door een bepaalde speler worden gespeeld)
  • De resultaatrecords worden in aflopende volgorde weergegeven op basis van de toernooiscores (eerste plaats, tweede plaats, etc.)

De resultaten van deze zoekopdracht worden hieronder weergegeven:

PlayerName TotalWeightedScore ------------------------- ----------------- Garry Kasparov 100.0 Bobby Fisher 87.5 Bart Simpson 46.25 

Conclusie

Dit concludeert onze verkenning van een eenvoudig SQLite-database-voorbeeld: een schaaktoernooi-database. Hopelijk heb je jezelf opnieuw vertrouwd gemaakt met relationele databaseconcepten zoals tabellen, records en query's en hebt je jezelf vertrouwd gemaakt met veel van de veelgebruikte SQLite-opdrachten. Ten slotte hebt u het ontwerp en gebruik van een voorbeelddatabase doorlopen. Nu u weet hoe SQLite-databases werken, bent u klaar om ze te gebruiken in uw Android-toepassingen (het onderwerp van onze volgende zelfstudie in deze reeks).

Over de Auteurs

Mobiele ontwikkelaars Lauren Darcey en Shane Conder hebben samen meerdere boeken geschreven over Android-ontwikkeling: een diepgaand programmeerboek getiteld Android Wireless Application Development en Sams TeachYourself Android Application Development binnen 24 uur. Wanneer ze niet schrijven, besteden ze hun tijd aan het ontwikkelen van mobiele software bij hun bedrijf en het leveren van consultingservices. Ze zijn te bereiken via e-mail naar [email protected], via hun blog op androidbook.blogspot.com, en op Twitter @androidwireless.