===== Android(4) : Gestion des données avec SQLite ===== Réaliser les manipulations avec méthode et application pour comprendre le processus de création et manipulation de données avec SQLite ==== 1. Contexte : suite du projet Inventaire. ==== Les articles saisis devront être mémorisés dans une base de données SQLite sous Android. ==== 2. La base de données SQLite ==== === a. Généralités === SQLite est un système de gestion de base de données compatible SQL **léger et embarqué**, qui ne requiert pas de serveur externe. Tout est inclus dans une seule bibliothèque (d'où la caractéristique //"embarqué"// avec l'application), ce qui facilite son intégration dans une large variété d’applications. Son architecture légère la rend adaptée aux environnements avec des ressources limitées, comme les appareils mobiles et les systèmes embarqués. Par ailleurs, SQLite garantit la conformité aux **propriétés ACID** : Atomicité, Cohérence, Isolation, Durabilité. Ceci assure la fiabilité des opérations de base de données, même en cas d’interruption soudaine du système. SQLite est disponible sur n'importe quel terminal sous Android. Utiliser une base SQLite ne requiert aucune étape d'administration ou de paramétrage préalable. Il suffit simplement d'écrire les requêtes SQL permettant de créer ou de mettre à jour la base de données. Celle ci est ensuite gérée automatiquement par la plateforme Android. Accéder à une base de données SQLite implique d'accéder à un fichier stocké dans le répertoire **/data/data/nomDuPackage/databases**. Cette opération peut être plus ou moins longue. Il est donc recommandé de réaliser toutes les opérations sur la base de données de manière **asynchrone**, par exemple via **la classe AsyncTask de l'API Android**. Ce que nous ne ferons pas ici pour des raisons de simplification. === b. Utilisation de SQLite === Le sous package **android.database.sqlite** contient les classes spécifiques à SQLite. **La classe SQLiteDatabase** La classe SQLiteDatabase est la classe principale permettant de travailler avec les bases de données SQLite. Elle offre plusieurs méthodes pour accéder à la base de données, exécuter des requêtes de sélection ou de mise à jour et fermer la base de données : * insert() : méthode pour insérer des donnése dans une table * execSQL() : exécuter une requête SQL qui ne retourne pas de réponses * query() : retourne un Cursor de données en fonction de paramètres fournis * rawQuery() : retourne un Cursor en prenant en entrée directement une requête SQL * update() : méthode pour mettre à jour un ensemble de données * delete() : méthode pour supprimer un ensemble de données Les méthodes insert() et update() utilisent en paramètre la classe ContentValues qui décrit sous forme de clé / valeur la donnée à insérer en base. Cette classe fonctionne comme la classe HashMap (Dictionnaire). **L'Objet Cursor** Les méthodes query() et rawQuery() retournent un objet de type Cursor. Cet objet représente le résultat de la requête exécutée et pointe généralement sur un élément de la liste du résultat. Ce principe permet d'éviter de devoir tout charger en mémoire. La classe Cursor offre les méthodes nécessaires pour naviguer dans la liste des éléments du résultat de la requête, en voici quelques unes : * getCount() : retourne le nombre d'éléments du résultat de la requête * move() : méthodes permettant de déplacer le curseur dans le résultat * isAfterLast() : permet de déterminer si on a atteint la fin de la liste du résultat * get…(int columnIndex) : ensemble de méthodes permettant de retourner la valeur typée d'une colonne donnée selon son index. Exemple : getLong() ou getString() * getColumnIndex() : retourne l'index correspondant au nom de la colonne ou retourne -1 si le nom de la colonne n'existe pas * getColumnIndexOrThrow(String columnName) : retourne l'index correspondant au nom de la colonne ou lève une exception si le nom de la colonne n'existe pas Pour plus d'informations, cf https://developer.android.com/reference/android/database/Cursor **Les types de données gérés** * SQLite supporte les types suivants : * NULL : valeur non définie * TEXT : équivalent à String en Java * INTEGER : équivalent à int en Java * REAL : équivalent à double en Java * BLOB : la donnée est enregistrée comme elle a été donnée ==== 3. Mise en œuvre ==== === a. Création de la couche métier === * Dans //app/src/main/java//, créer un nouveau package nommé **metier** (Clic droit sur app/java) * Dans ce package, créer la classe Java nommée **Article**. Cette classe nous servira de passerelle vers la base de données. * La classe Article comprendra les attributs //id//(int initialisé à 0), //reference//(String), //designation//(String), //prix//(float) et **qte**(int). * Elle devra disposer des accesseurs (//getters//), de la méthode //toString()// et de 2 constructeurs (clic droit puis **Generate**) : * l'un avec tous les attributs * l'autre sans l'attribut id. === b. Création de la base de données, classe CreateBdInventaire === * Toujours dans //app/src/main/java// créer un nouveau package nommé **bdd** * Dans ce package, créer la classe Java **CreateBdInventaire**. Cette classe hérite de la classe **SQLiteOpenHelper**. Elle permet de définir la structure de la base de données. * Renseigner la superclass * Redéfinir les méthodes onCreate et onUpdate * Générer automatiquement le constructeur, faites-le en utilisant la première proposition : {{ :promo_2025:slam:3sqllite1.png?600 |}} * Lors de l'instanciation de cette classe, le système vérifiera si la base existe : * si elle n'existe pas, il la créera en faisant appel à la méthode //onCreate// * si elle existe, il vérifie la version, si la version de la base a changé, la base sera regénérée, (méthode //onUpgrade//). * Code de la classe **CreateBdInventaire** : public class CreateBdInventaire extends SQLiteOpenHelper { public static final String TABLE_ARTICLE = "article"; private static final String CREATE_TABLE_ARTICLE = "CREATE TABLE " + TABLE_ARTICLE + "(" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "ref TEXT NOT NULL, " + "des TEXT NOT NULL, " + "pu REAL," + "qte INTEGER);"; // Constructeur, à générer automatiquement public CreateBdInventaire(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); } /** * Création de la base de données si elle n'existe pas * @param db base */ @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_TABLE_ARTICLE); Log.d("bdd", "Base créée"); } /** * Création d'une nouvelle base en cas de changement de version * @param db * @param oldVersion * @param newVersion */ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE " + TABLE_ARTICLE + ";"); Log.d("bdd", "Table " + TABLE_ARTICLE + " supprimée"); onCreate(db); } } **NB** Les instructions Log.d permettent de pister le déroulement du programme. (d = debug) === c. Création de la classe gérant les accès à la base de données : DAO === Dans le package bdd, nous allons ajouter une nouvelle classe **DAO** (Data Access Object) chargée de gérer l'accès à la base de données. Cette classe disposera * des attributs suivants : private static final int VERSION_BDD = 1; private static final String NOM_BDD = "inventaire.db"; private CreateBdInventaire createBD = null; private SQLiteDatabase db = null; * d'un constructeur : {{ :promo_2025:slam:3sqlliteconstructeur.png?600 |}} * d'une méthode open pour ouvrir la base de données :{{ :promo_2025:slam:3sqlliteopen.png?400 |}} * et d'une méthode close pour la fermer : {{ :promo_2025:slam:3sqlliteclose.png?400 |}} === d. Création de la classe gérant les accès à la table article : Article DAO === Enfin, dans le package bdd, nous allons ajouter la classe **ArticleDAO** chargée de gérer les accès à la table article (méthodes CRUD). Cette classe disposera d'abord d'un constructeur et d'une méthode permettant de fermer la base de données : {{ :promo_2025:slam:3sqllitearticledao.png?400 |}} Puis des méthodes CRUD, notamment la méthode create(Article unArticle), chargée de créer l'article passé en paramètre dans la base de données. Les autres méthodes d'accès à la base seront insérées par la suite. {{ :promo_2025:slam:3sqllitecreate.png?600 |}} === e. Insertion des données saisies === Modification de la classe **AjoutArticleActivity** : jusqu'à présent seul le bouton Quitter était géré, il faut désormais gérer le bouton Ajout. * 1) Ajouter des propriétés à cette classe : * a. Déclarer la variable d'accès à la table article : //private ArticleDAO articleDAO = null;// * b. Déclarer un objet de type Article : //Article art = null;// * c. Déclarer les différents contrôles graphiques : // Contrôles graphiques private EditText etRef; private EditText etDes; private EditText etPrix; private EditText etQte; private Button btAjout; * d. Déclarer les zones de réception des saisies // Valeurs saisies String ref; String des; String prix; double prixN; String qte; int qteN; Article art; long idArticleCree; ArticleDAO artDAO; * 2) À la fin de la méthode **onCreate**, appeler la méthode **creationArticle** que l'on va écrire. * 3) Ajouter la méthode creationArticle() private void creationArticle() { // Reconnaissance des contrôles graphiques de la vue etRef = (EditText) findViewById(R.id.etRef); etDes = (EditText) findViewById(R.id.etdesign); etPrix = (EditText) findViewById(R.id.etPrix); etQte = (EditText) findViewById(R.id.etQte); btAjout = (Button) findViewById(R.id.btAjouter); // Accès à la table article articleDAO = new ArticleDAO(this); // Gestion de l'événement onClick sur le boutn Ajouter btAjout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { // Récupération des zones saisies ref = etRef.getText().toString(); des = etDes.getText().toString(); prix = etPrix.getText().toString(); prixN = Float.valueOf(prix); qte = etQte.getText().toString(); qteN = Integer.valueOf(qte); // Création de l'article correspondant art = new Article(ref, des, prixN, qteN); // Insertion de l'article dans la base de données long idArticleCree = articleDAO.create(art); // Message à l'écran Toast.makeText(getApplicationContext(), "Produit ajouté + " + "(no : " + idArticleCree + " )",Toast.LENGTH_SHORT).show(); // Zones de saisies effacées etRef.setText(""); etRef.requestFocus(); etDes.setText(""); etPrix.setText(""); etQte.setText(""); } }); } * 4) Fermer la base de données quand l'utilisateur quitte la fonctionnalité, ajouter : //artDAO.close();// avant //finish();// * 5) Tester : Ajouter des articles. Le message indiquant le numéro de l'article ajouté doit s'afficher. Montrer l'application à Mme Thevenot ==== 4. Vérifications ==== === a. Vérifier les entrées du Logcat === Après le lancement de l'application, sélectionner l'onglet Logcat en bas de l'écran au niveau de la partie Android Monitor. Si cet onglet n'est pas présent, lier le debugger au process, icone (Attach debugger to Android process), cocher show all processes, puis sélectionner l'application Inventaire. À droite de la liste des onglets, dans le filtre indiquer : **bdd** afin de sélectionner les messages de debug (log.d) dont le nom est bdd Créer un article, l'affichage suivant devrait apparaitre : === b. Utilisation de Database Inspector (avec un périphérique Android ) === Cf https://developer.android.com/studio/inspect/database **Remarque** : l'outil d'inspection de bases de données fonctionne uniquement avec la bibliothèque SQLite incluse dans le système d'exploitation Android à partir du niveau d'API 26. ==== 5. Questions ==== Répondre aux questions suivantes **à la suite du document déposé sur le Drive**. - Présenter brièvement le travail à effectuer - Citer les 3 classes permettant la gestion de la base de données, et préciser le rôle de chacune - Quand la base de données est-elle créée ? - Tester le changement de version et copier l'affichage de la console Logcat correspondant - En effectuant quelques recherches sur Internet, présentez les caractéristiques d'une base de données SQLite implémentée sur un smartphone. - Expliquer comment procéder pour ajouter la table categorie(id, libelle) à la base de données et la relier à la table article (clé étrangère idCategorie). Montrer le document à Mme Thevenot