Bonjour à tous,
Nous allons aborder aujourd’hui la conception d’une application reposant sur les bases de données.
Les bases de données sont un moyen de stocker une donnée dans un
format que vous aurez choisi, le stockage est de façon permanent.
Vous pouvez aussi stocker les données au format Xml, texte, json etc…
Ce tutoriel portera uniquement sur le stockage avec le langage SQL voir la
Définition SQL.
Android propose utilise le moteur SQL nommé SQLite voir sa
Définition.
Le moteur SQLite accepte les types suivants de données
| Type |
Définition |
| NULL |
Valeur vide |
| INTEGER |
Entier signé |
| REAL |
Nombre réel |
| TEXT |
Champ texte |
| BLOB |
Champ binaire |
Le type boolean n’est qu’un entier ayant pour valeur 0 et 1
Pour les dates il y a plusieurs manières de procéder
- Dans un champ TEXT : Format YYYY-MM-DD HH:MM:SS.SSS
- Dans un champ REAL : Nombre de jour depuis Greenwich le 24 Novembre 4714 avant J.C
- Dans un champ INTEGER : Nombre de seconde depuis le 01/01/1970 soit la date au format UNIX
Ne vous inquiétez pas SQLite possède des fonctions pour manipuler les dates.
Définition du projet
Nous allons créer une application pour faire nos courses, ça nous permettra d’avoir un contexte de compréhension.
Tout d’abord, il faut penser au format des données.
Dans notre cas une course sera :
- Un produit
- Une quantité
- Un flag acheté (pour suivre notre avancement dans le magasin)
Nous devrons pouvoir Ajouter / Supprimer / Modifier un produit, ainsi que lister les produits.
Nous avons désormais notre contexte pour travailler, il faut maintenant coder tout cela.
Commençons par créer notre objet Course
4 | private String produit; |
6 | private boolean achete; |
8 | public Course(String produit, int quantite) { |
9 | this.produit = produit; |
10 | this.quantite = quantite; |
17 | public void setId(int id) { |
20 | public String getProduit() { |
23 | public void setProduit(String produit) { |
24 | this.produit = produit; |
26 | public int getQuantite() { |
29 | public void setQuantite(int quantite) { |
30 | this.quantite = quantite; |
32 | public boolean isAchete() { |
35 | public void setAchete(boolean achete) { |
Abordons maintenant l’utilisation des bases de données.
SQLiteOpenHelper
Le but de cette classe est de permettre la création et l’utilisation des bases de données au sein de l’écosystème Android.
Commençons tout d’abord par créer une classe nommée
CourseOpenHelper héritant de la classe
SQLiteOpenHelper.
Puis nous allons rajouter des attributs statiques à notre classe
2 | private static final int DATABASE_VERSION = 1; |
5 | private static final String COURSE_BASE_NAME = "course.db"; |
8 | public static final String COURSE_TABLE_NAME = "Course"; |
11 | public static final String COLUMN_ID = "ID"; |
12 | public static final int NUM_COLUMN_ID = 0; |
13 | public static final String COLUMN_PRODUIT = "PRODUIT"; |
14 | public static final int NUM_COLUMN_PRODUIT = 1; |
15 | public static final String COLUMN_QUANTITE = "QUANTITE"; |
16 | public static final int NUM_COLUMN_QUANTITE = 2; |
17 | public static final String COLUMN_ACHETE = "ACHETE"; |
18 | public static final int NUM_COLUMN_ACHETE = 3; |
21 | private static final String REQUETE_CREATION_BDD = "CREATE TABLE " |
22 | + COURSE_TABLE_NAME + " (" + COLUMN_ID |
23 | + " INTEGER PRIMARY KEY AUTOINCREMENT, " + COLUMN_PRODUIT |
24 | + " TEXT NOT NULL, " + COLUMN_QUANTITE + " INTEGER NOT NULL, " |
25 | + COLUMN_ACHETE + " INTEGER NOT NULL);"; |
Nous avons donc :
- Version de la base de données (pour les mises à jour)
- Nom de la base (nom du fichier)
- Nom de la table (Course pour nous)
- Description des colonnes, ainsi que leur position (0..n)
- La requête de création de la base
Et pour finir nos méthodes
1 | public CourseOpenHelper(Context context, CursorFactory factory) { |
2 | super(context, COURSE_BASE_NAME, factory, DATABASE_VERSION); |
9 | public void onCreate(SQLiteDatabase db) { |
10 | db.execSQL(REQUETE_CREATION_BDD); |
14 | * Mise à jour de la base |
17 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { |
20 | if (newVersion > DATABASE_VERSION) { |
21 | db.execSQL("DROP TABLE " + COURSE_TABLE_NAME + ";"); |
Notre méthode onCreate sera appelée si la base n’existe pas, la méthode onUpgrade sera appelée si une mise à jour est à faire.
J’espère que vous suivez toujours
Repository
Vous en avez peut être déjà entendu parler, en français c’est un dépôt. Il sert à faire la jonction avec notre base de données.
Comme ça en cas d’évolution on n’impacte pas la vue (layout) si ce
n’est qu’un correctif ou une modification de la façon d’allez chercher
nos données, c’est tout de même plus propre et je vous le conseil
fortement.
Créons pour commencer une interface nommée IRepository qui sera générique (gné ?)
voir ce tutoriel
1 | public interface IRepository { |
4 | public T GetById(int id); |
6 | public void Save(T entite); |
7 | public void Update(T entite); |
8 | public void Delete(int id); |
10 | public List ConvertCursorToListObject(Cursor c); |
11 | public T ConvertCursorToObject(Cursor c); |
12 | public T ConvertCursorToOneObject(Cursor c); |
Alors nos méthodes :
- GetAll : Retourne la liste des données
- GetById : Retourne un objet unique
- Save : Enregistre un objet
- Update : Met à jour un objet
- Delete : Supprime un objet
- ConvertCursorToListObject : Converti un curseur en une liste d’objet
- ConvertCursorToObject : Converti un curseur en un objet
- ConvertCursorToOneObject : Converti un curseur en un seul objet
En suite il va nous falloir la base de notre Repository
Toujours une classe générique mais abstraite
1 | public abstract class Repository implements IRepository { |
3 | protected SQLiteDatabase maBDD; |
5 | protected SQLiteOpenHelper sqLiteOpenHelper; |
8 | * Constructeur par défaut |
15 | * Ouverture de la connection |
18 | maBDD = sqLiteOpenHelper.getWritableDatabase(); |
22 | * Fermeture de la connection |
Cette classe comprend deux méthodes et deux attributs.
Attributs :
- maBDD : Relation avec la base de données SQLite
- sqLiteOpenHelper : Notre helper pour traiter avec la base
Méthodes :
- Constructeur
- Open : Permet d’ouvrir la liaison avec la base
- Close : Ferme la liaison
Pour terminer implémentons notre CourseRepository
1 | public class CourseRepository extends Repository { |
3 | public CourseRepository(Context context) { |
4 | sqLiteOpenHelper = new CourseOpenHelper(context, null); |
8 | * Suppression d'un produit |
12 | public void DeleteProduit(int id) { |
13 | maBDD.delete(CourseOpenHelper.COURSE_TABLE_NAME, |
14 | CourseOpenHelper.COLUMN_ID + "=?", |
15 | new String[] { String.valueOf(id) }); |
19 | * Récupération de la liste de tous les produits |
22 | public List GetAll() { |
24 | Cursor cursor = maBDD.query(CourseOpenHelper.COURSE_TABLE_NAME, |
25 | new String[] { CourseOpenHelper.COLUMN_ID, |
26 | CourseOpenHelper.COLUMN_PRODUIT, |
27 | CourseOpenHelper.COLUMN_QUANTITE, |
28 | CourseOpenHelper.COLUMN_ACHETE }, null, null, null, |
31 | return ConvertCursorToListObject(cursor); |
35 | * Retourne un seul produit |
38 | public Course GetById(int id) { |
39 | Cursor cursor = maBDD.query(CourseOpenHelper.COURSE_TABLE_NAME, |
40 | new String[] { CourseOpenHelper.COLUMN_ID, |
41 | CourseOpenHelper.COLUMN_PRODUIT, |
42 | CourseOpenHelper.COLUMN_QUANTITE, |
43 | CourseOpenHelper.COLUMN_ACHETE }, |
44 | CourseOpenHelper.COLUMN_ID + "=?", |
45 | new String[] { String.valueOf(id) }, null, null, null); |
47 | return ConvertCursorToObject(cursor); |
51 | * Enregistre en produit dans la base |
54 | public void Save(Course entite) { |
55 | ContentValues contentValues = new ContentValues(); |
56 | contentValues.put(CourseOpenHelper.COLUMN_PRODUIT, entite.getProduit()); |
57 | contentValues.put(CourseOpenHelper.COLUMN_QUANTITE, |
58 | entite.getQuantite()); |
59 | contentValues.put(CourseOpenHelper.COLUMN_ACHETE, entite.isAchete()); |
61 | maBDD.insert(CourseOpenHelper.COURSE_TABLE_NAME, null, contentValues); |
65 | * Met à jour un produit |
68 | public void Update(Course entite) { |
69 | ContentValues contentValues = new ContentValues(); |
70 | contentValues.put(CourseOpenHelper.COLUMN_PRODUIT, entite.getProduit()); |
71 | contentValues.put(CourseOpenHelper.COLUMN_QUANTITE, |
72 | entite.getQuantite()); |
73 | contentValues.put(CourseOpenHelper.COLUMN_ACHETE, entite.isAchete()); |
75 | maBDD.update(CourseOpenHelper.COURSE_TABLE_NAME, contentValues, |
76 | CourseOpenHelper.COLUMN_ID + "=?", |
77 | new String[] { String.valueOf(entite.getId()) }); |
84 | public void Delete(int id) { |
85 | maBDD.delete(CourseOpenHelper.COURSE_TABLE_NAME, |
86 | CourseOpenHelper.COLUMN_ID + "=?", |
87 | new String[] { String.valueOf(id) }); |
91 | * Converti un curseur en une liste de produits |
94 | public List ConvertCursorToListObject(Cursor c) { |
95 | List liste = new ArrayList(); |
98 | if (c.getCount() == 0) |
107 | Course course = ConvertCursorToObject(c); |
110 | } while (c.moveToNext()); |
119 | * Méthode utilisée par ConvertCursorToObject et ConvertCursorToListObject |
122 | public Course ConvertCursorToObject(Cursor c) { |
124 | Course course = new Course( |
125 | c.getString(CourseOpenHelper.NUM_COLUMN_PRODUIT), |
126 | c.getInt(CourseOpenHelper.NUM_COLUMN_QUANTITE)); |
127 | course.setId(c.getInt(CourseOpenHelper.NUM_COLUMN_ID)); |
128 | course.setAchete((c.getInt(CourseOpenHelper.NUM_COLUMN_ACHETE) != 0)); |
134 | * Converti un curseur en un produit |
137 | public Course ConvertCursorToOneObject(Cursor c) { |
140 | Course course = ConvertCursorToObject(c); |
Oulala c’est du lourd !
Pour mieux comprendre, toutes actions effectuées de type Get en base (retour d’un ou plusieurs objets) retourne un curseur.
Ce curseur comprend l’intégralité de ce que vous avez demandé, il
faut juste passer sur chaque ligne retournée pour l’affectée à un objet,
dans notre cas un objet Course.
Pour faire un Save, Delete, Update, GetAll ou encore GetById on utilise simplement une commande fournie par Android.
- GetAll / GetById : Query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)
- Save : Insert (String table, String nullColumnHack, ContentValues values)
- Update : Update (String table, ContentValues values, String whereClause, String[] whereArgs)
- Delete : Delete (String table, String whereClause, String[] whereArgs)
Dans le cas d’un Save,Delete,Update nous utiliserons un objet ContentValues qui transportera nos données.
Enfin de compte il n’y a rien de plus simple !
N’oubliez pas de refermer vos curseurs après utilisations (c’est comme les BIC , sinon ça sèche).
De plus n’oubliez pas d’ouvrir votre connexion avec la base avant un traitement et de la refermée ensuite.
Exemple :
1 | public void DeleteItem(int id) { |
2 | courseRepository.Open(); |
3 | courseRepository.Delete(id); |
4 | courseRepository.Close(); |
Je n’expliquerais pas le programme au complet, car la suite ce n’est
qu’une Listview, un menu et des boutons (déjà vu dans les autres
chapitres).
Mais voici ce que vous devriez avoir à la fin
Enregistrer un commentaire