sujet

publicité
IUT de Lannion
Dept Informatique
1.
Programmation Android
TP6 - Bases de données
P. Nerzic
2016-17
Base de données SQLite
Dans ce TP, nous allons voir comment créer et utiliser une base de données SQL locale pour
stocker les informations. La semaine prochaine, ça sera avec un WebService.
On repart de l’application AvosAvis de la semaine précédente. . . quelque soit son état d’avancement.
Vous pouvez même repartir de la version de base, AvosAvis.zip, mais évidemment la vôtre1 sera
plus complète.
Faites une copie du projet AvosAvis et appelez-la AvosAvisSQL, puis travaillez uniquement sur
cette copie.
1.1.
Essais en ligne de commande (optionnels)
Lancez un AVD (pas la tablette réelle, car sqlite3 n’y est pas installé en tant que programme
utilisateur). Relisez le TP1 en ce qui concerne la connexion en shell avec la commande adb.
Connectez-vous en shell sur l’AVD.
NB:
adb
adb
adb
il peut y avoir un bug. Si vous n’arrivez pas à vous connecter à la tablette, alors tapez :
kill-server
start-server
devices
Dans le shell de la tablette, lancez la commande sqlite3 sans paramètre. En faisant ainsi, ce
qu’on va créer ne sera qu’en mémoire vive, pas enregistré dans un fichier. Faites-vous la main avec
quelques requêtes SQL et directives spécifiques de SQLite :
CREATE TABLE Loisirs (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
nom TEXT NOT NULL);
.schema Loisirs
INSERT INTO Loisirs VALUES (1,'sport');
INSERT INTO Loisirs VALUES (2,'informatique');
INSERT INTO Loisirs VALUES (null,'cinéma');
INSERT INTO Loisirs VALUES (null,'santé');
.dump Loisirs
SELECT * FROM Loisirs;
.headers on
SELECT nom FROM Loisirs WHERE _id=3;
.dump Loisirs
UPDATE Loisirs SET nom='ordinateur' WHERE _id=2;
DELETE FROM Loisirs WHERE nom='santé';
SELECT * FROM Loisirs ORDER BY nom ASC;
.exit
Notez que l’identifiant d’une table doit TOUJOURS être un entier appelé _id. Si on fournit null
1
Ne copiez pas le projet d’un autre étudiant, ce serait très peu apprécié. Demandez plutôt de l’aide. Le but,
c’est que vous appreniez quelque chose, pas que le TP semble fini.
1
IUT de Lannion
Dept Informatique
Programmation Android
TP6 - Bases de données
P. Nerzic
2016-17
en tant qu’identifiant alors SQLite en attribue un automatiquement. C’est le rôle de l’option
AUTOINCREMENT (SERIAL dans PostgreSQL).
1.2.
Table Avis
On va commencer par définir une classe TableAvis qui représente la table Avis (libellé, note,
auteur et date). Relisez le cours de la semaine 6, notamment le transparent intitulé « Classe pour
une table ». L’idée, c’est que cette classe va remplacer le ArrayList des TPs précédents.
Complétez les méthodes de la classe TableAvis (cherchez les TODO) :
• void create(SQLiteDatabase bdd)
Cette méthode effectue la création de la table dans la base de données. Elle exécute une
requête CREATE TABLE Avis... à l’aide de la méthode execSQL sur le paramètre bdd. Les
attributs de la table sont :
– _id de type INTEGER PRIMARY KEY AUTOINCREMENT
– libelle de type TEXT NOT NULL
– note de type REAL
– auteur de type TEXT
– date de type INTEGER. Remarque : le type LONG ou TIMESTAMP n’existe pas.
• void drop(SQLiteDatabase bdd)
Cette méthode supprime totalement la table Avis de la base. Utiliser la méthode execSQL.
NB: utilisez l’option IF EXISTS afin de parer aux erreurs de double suppression.
• Long insertAvis(SQLiteDatabase bdd, Avis avis)
Cette méthode ajoute l’avis indiqué à la table Avis. Vous avez deux manières de le faire.
Soit utiliser la méthode execSQL, soit utiliser la méthode insert. La seconde est à préférer
car elle retourne l’identifiant du nouveau n-uplet. Il pourrait être repris pour créer une
relation, en tant que clé étrangère dans une autre table.
La méthode insert est plus compliquée que execSQL car il faut créer un ContentValues
et mettre les colonnes dedans. Relisez le cours, transparents « Exemples de méthodes » et
« Méthode insert ». Regardez aussi les accesseurs de la classe Avis.
En ce qui concerne la date, on vous suggère d’utiliser la méthode getTimestamp de l’avis.
Le principe est de transformer la date en un nombre de millisecondes écoulées depuis le
01/01/1970. Ça s’appelle un timestamp et si vous devez travailler avec des dates, vous
passerez beaucoup de temps à convertir des timestamps en date et vice-versa. Beaucoup de
SGBD possèdent les types DATE, TIME, DATETIME et TIMESTAMP, mais pas sqlite3.
• void updateAvis(SQLiteDatabase bdd, Long id, Avis avis)
Cette méthode modifie le n-uplet identifié par id. En fait, c’est presque la même méthode
que la précédente, mais on fait UPDATE au lieu de INSERT et il faut fournir une clause de
sélection. Vous pouvez également soit utiliser execSQL, soit update. Relisez le cours. Si
vous utilisez update, n’oubliez pas de convertir l’identifiant en chaîne.
• void deleteAvis(SQLiteDatabase bdd, Long id)
Cette méthode supprime le n-uplet identifié par id.
2
IUT de Lannion
Dept Informatique
Programmation Android
TP6 - Bases de données
P. Nerzic
2016-17
• Avis getAvis(SQLiteDatabase bdd, Long id)
Cette méthode retourne un nouvel objet Avis construit à partir du n-uplet identifié par
id. Elle crée un curseur, effectue une requête par rawQuery et extrait les colonnes qu’elle
place dans un nouvel avis retourné en résultat, puis elle ferme le curseur. La création d’un
Avis à partir d’un curseur se fait avec une méthode statique de la classe TableAvis appelée
fromCursor qu’on vous invite à aller voir.
1.3.
Helper pour gérer la base de données
L’étape suivante consiste à créer un helper pour gérer la base de données. Vous avez juste à
compléter celui qui est fourni, comme dans le cours : rajoutez-lui l’appel qui crée la table Avis et
celui qui supprime la table. Vous comprenez que dans une application ayant plusieurs tables, il
faudrait mettre tous les appels aux fonctions de création nécessaires et dans le bon ordre pour
respecter les contraintes de clés étrangères.
1.4.
Nettoyage
Avant d’attaquer la suite, vous allez supprimer la classe AvosAvisApplication et enlever l’attribut
android:name de l’application dans le manifeste. Cela va causer un grand nombre d’erreurs dans
MainActivity et EditActivity : chaque fois que vous utilisez encore l’ancienne liste. On va les
supprimer peu à peu.
1.5.
MainActivity
Maintenant, l’objectif est de supprimer partout le ArrayList global de l’application et de mettre
des appels aux méthodes de TableAvis à la place. Il y a quelques transformations pour remplacer
le ArrayList par la base SQL.
• Variables membres :
– Rajoutez deux variables membres :
MySQLiteHelper helper;
SQLiteDatabase bdd;
– Changer le type de la variable membre adapter en CursorAdapter. Ça va entraîner
de nouvelles erreurs tant que tout n’est pas cohérent.
• Méthode onCreate, devinez ce qu’il faut faire des deux membres helper et bdd. La réponse
est donnée dans le cours, transparent intitulé « Utilisation du Helper dans l’application ».
La base doit être ouverte en écriture si vous avez mis en place le menu contextuel, sinon en
lecture quand l’activité ne modifie jamais les données.
• Complétez la méthode onDestroy de l’activité en rajoutant la fermeture du helper.
• Méthode onCreate encore, vous pourrez tester deux adaptateurs différents (un seul à la
fois) :
– Le type SimpleCursorAdapter :
adapter = new SimpleCursorAdapter(
this,
3
Programmation Android
TP6 - Bases de données
IUT de Lannion
Dept Informatique
P. Nerzic
2016-17
android.R.layout.simple_list_item_2,
null,
new String[]{TableAvis.Libelle, TableAvis.Note},
new int[]{android.R.id.text1, android.R.id.text2},
0);
C’est un adaptateur qui affiche les champs dans un layout standard d’Android. Il ne
peut afficher que deux chaînes, un titre et un sous-titre. La note est automatiquement
convertie en texte.
– Le type AvisCursorAdapter :
adapter = new AvisCursorAdapter(this, null, R.layout.item_avis);
C’est un adaptateur dérivé de CursorAdapter similaire au AvisAdapter du TP4.
• Toujours dans onCreate, il faut créer un chargeur de curseur et le démarrer. Ça se fait avec
cette ligne à la fin de la méthode, après avoir ouvert la base de données et créé l’adapter :
getLoaderManager().initLoader(AVIS_LOADER_ID, null, this);
Vous allez avoir une erreur quand vous ferez ça, car il faudrait que this implémente une
interface. Allez voir dans le cours le transparent intitulé « Activité ou fragment d’affichage
d’une liste ». Les méthodes de cette interface sont déjà programmées, vous devez seulement
rajouter l’interface nécessaire à la liste implements. Attention, AndroidStudio vous propose
deux packages possibles pour importer Loader et LoaderManager, il ne faut pas prendre ceux
concernant appcompat, mais android.app.LoaderManager et android.content.Loader.
• Mise à jour de la liste : à la place des adapter.notifyDataSetChanged(), mettre ce qui
suit. Si c’est dans un écouteur, alors vous devez mettre le nom de la classe englobante devant
this :
getLoaderManager().restartLoader(AVIS_LOADER_ID, null, this);
• Repérez toutes les lignes marquées TODO et faites ce qui est indiqué : décommenter la
ligne suivante et parfois supprimer la suivante. Pour toutes les méthodes surchargées, vous
pouvez décommenter l’annotation @Override.
1.6.
EditActivity
Comme dans MainActivity, il faut remplacer les actions sur le ArrayList global par des requêtes
SQL à travers la classe TableAvis.
• Variables membres :
– Rajoutez deux variables membres :
MySQLiteHelper helper;
SQLiteDatabase bdd;
et recopiez les mêmes instructions pour ouvrir la base, cette fois toujours en écriture.
• Complétez la méthode onDestroy en rajoutant la fermeture du helper.
4
IUT de Lannion
Dept Informatique
Programmation Android
TP6 - Bases de données
P. Nerzic
2016-17
• Il faut maintenant remplacer les accès au ArrayList par un accès à la base. Par exemple,
dans la méthode display :
// récupérer l'item désigné par l'identifiant
Avis avis = TableAvis.getAvis(bdd, identifiant);
C’est la méthode valider qui vous posera le plus de problèmes pour comprendre exactement
ce qu’il faut faire, dans quel ordre et ce qui change par rapport à la liste.
1.7.
Réflexion
Un problème de réflexion pour finir : comment pourrait-on organiser les classes s’il y avait une
deuxième table dans la base ? Actuellement, il n’y a que la table Avis, mais si on rajoute une
table Categories contenant des catégories d’avis comme repas, sorties, sports, études. . . ; cette
catégorie étant une clé étrangère dans la table Avis. Il faudrait créer une classe TableCategories
avec ses requêtes comme dans TableAvis.
Cela paraît assez simple si c’est une relation 1-n (un avis a une et une seule catégorie, mais une
catégorie peut concerner plusieurs avis). Lors de la création de la base de données, il faut appeler
la méthode de création des deux tables dans le bon ordre. Ensuite, l’un des traitements est l’ajout
d’un avis ayant une nouvelle catégorie, il faut insérer la catégorie, récupérer son identifiant puis
insérer l’avis. À moins d’avoir une activité spécifiquement pour gérer les catégories et qu’on ne
puisse pas créer de catégorie dans un avis mais seulement choisir parmi les existantes.
Mais si la relation est du type n-n (un avis peut avoir plusieurs catégories et une catégorie peut
concerner plusieurs avis), représentée par une autre table (CategoriesAvis), comment pourrait-on
organiser les classes et quelles méthodes elles pourraient contenir (question ouverte) ?
2.
Travail à rendre
Avec le navigateur de fichiers, descendez dans le dossier app/src du projet AvosAvis du TP6.
Rajoutez un fichier appelé exactement IMPORTANT.txt dans le sous-dossier main si vous avez
rencontré des problèmes techniques durant le TP : plantages, erreurs inexplicables, perte du travail,
etc. mais pas les problèmes dus à un manque de travail ou de compréhension. Décrivez exactement
ce qui s’est passé. Le correcteur pourra lire ce fichier au moment de la notation et compenser
votre note.
Cliquez droit sur le dossier main, choisissez Compresser..., format tar.gz, cliquez sur Creer.
Déposez l’archive main.tar.gz sur Moodle au bon endroit.
5
Téléchargement