Base de données SQLite3
- SQLite3 : moteur de base de données sur fichiers sans support de concurrence ; supporte les requêtes SQL ; pas de typage fort
- Permet la persistance de données structurées en tables (définies par des colonnes)
- Relations possibles entre différentes tables (jointure de tables)
- Maintenance d'index de tri sur les enregistrements : requêtes de sélection rapides
Gestion de tables SQLite3
-
Création de table avec CREATE TABLE :
- CREATE TABLE IF NOT EXISTS gpstrace(date INTEGER PRIMARY KEY, latitude REAL NOT NULL, longitude REAL NOT NULL, altitude REAL) ;
-
Création d'index avec CREATE INDEX :
- CREATE INDEX IF NOT EXISTS latitude ON gpstrace (latitude)
-
Renommage de table avec ALTER TABLE :
- ALTER TABLE gpstrace RENAME TO newgpstrace
-
Effacement de table avec DROP TABLE :
- DROP TABLE gpstrace
Requêtes SQL sur enregistrements
-
Requêtes :
- Sélection : SELECT col1,col2,...,coln FROM table WHERE expr GROUP BY expr HAVING expr {UNION, INTERSECT, EXCEPT} SELECT ...
- Insertion : INSERT INTO table (col1,col2,...,coln) VALUES (val1,val2,...,valn)
- Mise à jour : UPDATE table SET col1=val1, col2=val2, ...,coln=valn WHERE expr
- Suppression : DELETE FROM table WHERE expr
-
Expressions :
- Opérateurs classiques : || * / % + - << >> & | < <= > >= == != NOT
- colname LIKE x : suit le format de la chaîne x ( %: joker 0, 1 ou plusieurs caractères, _ : joker 1 caractère) ; exploite les index
- colname REGEXP x : valide l'expression régulière x
- Penser aux index lors de l'écriture d'expressions (pour ne pas parcourir tous les enregistrements)
SQLiteOpenHelper
- Classe à dériver pour l'ouverture de base SQLite
-
Deux méthodes à redéfinir :
- onCreate(SQLiteDatabase) : on y exécute le script de création de base (création des tables et index)
- onUpgrade(SQLiteDatabase, int, int) : pour mettre à jour le schéma d'une base d'une version i à une version j > i
- onOpen(SQLiteDatabase) peut également être redéfini pour agir lors de l'ouverture de la base
- Il faut expliciter un constructeur, par exemple :
public static final String DB_NAME = "gpsLog"; public static final int VERSION = 1; public GPSLogBaseHelper(Context context) { super(context, DB_NAME, null, VERSION); }
SQLiteDatabase
-
SQLiteDatabase = Instance représentant une base SQLite
-
base ouvrable depuis un SQLiteOpenHelper
- Exemple : SQLiteDatabase base = new GPSLogBaseHelper(this).get{Readable, Writable}Database()
- base devant être fermée après utilisation : base.close()
-
base ouvrable depuis un SQLiteOpenHelper
-
Requêtage avec méthode query() :
- Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
-
Exemple : récupération des 100 traces GPS les plus récentes de latitude supérieure à 45°
- Cursor c = db.query("gpstraces", new String[]{"date", "latitude", "longitude"}, "latitude > ?", new String[]{"45"}, null, null, "date DESC", 100);
- Cursor c = db.rawQuery("SELECT date, latitude, longitude FROM gpstraces WHERE latitude > ? ORDER BY date DESC limit 100", new String[]{"45"})
Ne jamais intégrer directement les arguments dans la requête (faille d'injection de commandes SQL)
Cursor
- On parcourt les résultats d'une requête avec une instance de Cursor (thread unsafe)
-
Méthode utiles :
- int getCount() : nombre d'éléments dans le Cursor
- X get{Short, Int, Long, Float, Double, String, Blob}(int i) : valeur de la colonne #i de l'enregistrement courant
- boolean moveToNext() : déplacement sur le prochain enregistrement (retourne false si fin des enregistrements)
Écriture d'enregistrements
-
Insertion avec SQLiteDatabase.insert(String table, String nullColumnHack, ContentValues values)
- ContentValues est une sorte de Map où les valeurs des colonnes sont insérées avec put(String key, X value)
- Mise à jour : int SQLiteDatabase.update(String table, ContentValues values, String whereClause, String[] whereArgs)
- Suppression : SQLiteDatabase.delete(String table, String whereClause, String[] whereArgs)
ContentProvider
Principe de fonctionnement
- Fournit une interface d'accès aux fichiers ou données structurées d'une application
- Les données sont identifiées par une URI, e.g. content://fr.upemlv.gpslogger.provider/log/ID
-
Opérations CRUD disponibles (à implanter de façon thread-safe) :
- C : Création d'enregistrement avec insert…
- R : Récupération de données avec query…
- U : Mise à jour d'enregistrements avec update…
- D : Suppression d'enregistrements avec delete…
-
Autres méthodes à redéfinir :
- onCreate() appelé lors de la création du ContentProvider
- String getType(Uri uri) retourne le type MIME d'un contenu identifié par son Uri
- Il est conseillé de créer une classe compagnon contenant des sous-classes pour chaque table avec le nom des colonnes en constantes ainsi que l'Uri vers la table.
Types MIME d'URI
-
ContentProvider.getType(Uri uri) : retourne le type MIME d'une table ou d'un enregistrement
- Table : vnd.android.cursor.dir/vnd.fr.upemlv.gpslog.provider.gpstrace
- Enregistrement d'une table : vnd.android.cursor.item/vnd.fr.upemlv.gpslog.provider.gpstrace
-
ContentProvider.getStreamTypes(Uri uri, String mimeTypeFilter) : retourne les types MIME supportés pour un fichier dont l'URI est spécifié. Un filtre indique quels sont les types qui nous intéressent.
- Par exemple, on peut retourner {"image/jpeg", "image/png"} pour une image
ContentProvider.query(...)
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
- Méthode généralement implantée en appelant SQLiteDatabase.query()
- Il faut auparavant reconnaître quelle table ou enregistrement est concerné : on analyse pour cela l'URI
- ID indiqué en fin d'URI : on ne récupère que l'enregistrement demandé dans la table
- Sinon on requête la table avec les paramètres de sélection et de tri
- L'analyse de l'URI peut être aidée avec UriMatcher (on y enregistre les schémas des URIs
ContentProvider.{insert(), update(), delete()}
- Uri insert(Uri uri, ContentValues values) : ajoute un enregistrement (values contient les couples nom de colonne, valeur) ; retourne une URI avec l'identificateur de l'enregistrement
- int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) : met à jour les enregistrements suivant selection avec selectionArgs ; retourne le nombre d'enregistrements mis à jour
- int delete(Uri uri, String selection, String[] selectionArgs) : efface des enregistrements ; retourne le nombre d'enregistrements effacés
Fournitures de méthodes et fichiers par un ContentProvider
-
Mécanisme de RPC intégré :
- Redéfinition de Bundle call(String method, String arg, Bundle extras)
-
Accès à des fichiers :
- Redéfinition de ParcelFileDescriptor openFile(Uri uri, String mode)
- Mode = "r", "rw", "rwt" (troncature de fichier existant)
- Création d'un ParcelFileDescriptor à partir d'un fichier : ParcelFileDescriptor.open(File file, int mode)
ContentResolver
- L'accès à un ContentProvider est réalisé depuis un ContentResolver. Le ContentResolver du Context est récupérable avec getContentResolver()
- Propose les méthodes CRUD query(), insert(), update() et delete()
- Propose des méthodes de requêtage et d'ajout en lots : applyBatch() et bulkInsert()