Enregistrer des données à l’aide de SQLite
Enregistrer des données dans une base de données est idéal pour les données répétitives ou structurées,telles que les informations de contact. Cette page suppose que vous êtes familier avec les bases de données SQL en général et vous aide à démarrer avec les bases de donnéesSQLite sur Android. Les API dont vous aurez besoin pour utiliser une base de données sur Android sont disponibles dans le paquet android.database.sqlite
.
Attention : Bien que ces API soient puissantes, elles sont assez bas niveau et nécessitent beaucoup de temps et d’efforts pour être utilisées :
- Il n’y a pas de vérification à la compilation des requêtes SQL brutes. Lorsque votre graphique de données change, vous devez mettre à jour manuellement les requêtes SQL concernées. Ce processus peut prendre du temps et être source d’erreurs.
- Vous devez utiliser beaucoup de code passe-partout pour convertir entre les requêtes SQL et les objets de données.
Pour ces raisons, nous avons fortement recommandé d’utiliser la bibliothèque de persistance Room comme couche d’abstraction pour accéder aux informations des bases de données SQLite de votre application.
Définir un schéma et un contrat
L’un des grands principes des bases de données SQL est le schéma : une déclaration formelle de la façon dont la base de données est organisée. Le schéma est reflété dans les déclarations SQL que vous utilisez pour créer votre base de données. Vous pouvez trouver utile de créer une classe compagnon, appelée classe de contrat, qui spécifie explicitement la disposition de votre schéma d’une manière systématique et auto-documentée.
Une classe de contrat est un conteneur pour les constantes qui définissent les noms des URI, des tables et des colonnes. La classe de contrat vous permet d’utiliser les mêmes constantes dans toutes les autres classes du même package. Cela vous permet de changer un nom de colonne à un endroit et de le voir se propager dans tout votre code.
Une bonne façon d’organiser une classe de contrat est de mettre les définitions qui sontglobales à toute votre base de données au niveau de la racine de la classe. Ensuite, créez une classe interne pour chaque table. Chaque classe interne énumère les colonnes de la table correspondante.
Note : En implémentant l’interface BaseColumns
, votre classe interne peut hériter d’un champ de clé primaire appelé _ID
que certaines classes Android telles que CursorAdapter
s’attendent à ce qu’elle possède. Ce n’est pas obligatoire, mais cela peut aider votre base de données à travailler harmonieusement avec le framework Android.
Par exemple, le contrat suivant définit le nom de la table et les noms des colonnes pour une table unique représentant un flux RSS :
Kotlin
object FeedReaderContract { // Table contents are grouped together in an anonymous object. object FeedEntry : BaseColumns { const val TABLE_NAME = "entry" const val COLUMN_NAME_TITLE = "title" const val COLUMN_NAME_SUBTITLE = "subtitle" }}
Java
public final class FeedReaderContract { // To prevent someone from accidentally instantiating the contract class, // make the constructor private. private FeedReaderContract() {} /* Inner class that defines the table contents */ public static class FeedEntry implements BaseColumns { public static final String TABLE_NAME = "entry"; public static final String COLUMN_NAME_TITLE = "title"; public static final String COLUMN_NAME_SUBTITLE = "subtitle"; }}
Créer une base de données en utilisant une aide SQL
Une fois que vous avez défini l’apparence de votre base de données, vous devez mettre en œuvre des méthodes qui créent et maintiennent la base de données et les tables. Voici quelques déclarations typiques qui créent et suppriment une table :
Kotlin
private const val SQL_CREATE_ENTRIES = "CREATE TABLE ${FeedEntry.TABLE_NAME} (" + "${BaseColumns._ID} INTEGER PRIMARY KEY," + "${FeedEntry.COLUMN_NAME_TITLE} TEXT," + "${FeedEntry.COLUMN_NAME_SUBTITLE} TEXT)"private const val SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS ${FeedEntry.TABLE_NAME}"
Java
private static final String SQL_CREATE_ENTRIES = "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" + FeedEntry._ID + " INTEGER PRIMARY KEY," + FeedEntry.COLUMN_NAME_TITLE + " TEXT," + FeedEntry.COLUMN_NAME_SUBTITLE + " TEXT)";private static final String SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;
Comme les fichiers que vous enregistrez sur le stockage interne de l’appareil, Android stocke votre base de données dans le dossier privé de votre application. Vos données sont sécurisées, car par défaut, cette zone n’est pas accessible aux autres apps ou à l’utilisateur.
La classe SQLiteOpenHelper
contient un ensemble utile d’API pour gérer votre base de données.Lorsque vous utilisez cette classe pour obtenir des références à votre base de données, le système effectue les opérations potentiellement longues de création et de mise à jour de la base de données uniquement lorsque cela est nécessaire et non au démarrage de l’app. Tout ce que vous avez à faire est d’appelergetWritableDatabase()
ougetReadableDatabase()
.
Note : Comme elles peuvent être longues, assurez-vous d’appeler getWritableDatabase()
ou getReadableDatabase()
dans un thread d’arrière-plan.Consultez Threading on Android pour plus d’informations.
Pour utiliser SQLiteOpenHelper
, créez une sous-classe qui surcharge les méthodes de rappel onCreate()
etonUpgrade()
. Vous pouvez également vouloir mettre en œuvre les méthodesonDowngrade()
ouonOpen()
,mais elles ne sont pas obligatoires.
Par exemple, voici une mise en œuvre de SQLiteOpenHelper
qui utilise certaines des commandes présentées ci-dessus :
Kotlin
class FeedReaderDbHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) { override fun onCreate(db: SQLiteDatabase) { db.execSQL(SQL_CREATE_ENTRIES) } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { // This database is only a cache for online data, so its upgrade policy is // to simply to discard the data and start over db.execSQL(SQL_DELETE_ENTRIES) onCreate(db) } override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { onUpgrade(db, oldVersion, newVersion) } companion object { // If you change the database schema, you must increment the database version. const val DATABASE_VERSION = 1 const val DATABASE_NAME = "FeedReader.db" }}
Java
public class FeedReaderDbHelper extends SQLiteOpenHelper { // If you change the database schema, you must increment the database version. public static final int DATABASE_VERSION = 1; public static final String DATABASE_NAME = "FeedReader.db"; public FeedReaderDbHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } public void onCreate(SQLiteDatabase db) { db.execSQL(SQL_CREATE_ENTRIES); } public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // This database is only a cache for online data, so its upgrade policy is // to simply to discard the data and start over db.execSQL(SQL_DELETE_ENTRIES); onCreate(db); } public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { onUpgrade(db, oldVersion, newVersion); }}
Pour accéder à votre base de données, instanciez votre sous-classe deSQLiteOpenHelper
:
Kotlin
val dbHelper = FeedReaderDbHelper(context)
Java
FeedReaderDbHelper dbHelper = new FeedReaderDbHelper(getContext());
Mettre de l’information dans une base de données
Insérer des données dans la base de données en passant un ContentValues
objet à la méthode insert()
:
Kotlin
// Gets the data repository in write modeval db = dbHelper.writableDatabase// Create a new map of values, where column names are the keysval values = ContentValues().apply { put(FeedEntry.COLUMN_NAME_TITLE, title) put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle)}// Insert the new row, returning the primary key value of the new rowval newRowId = db?.insert(FeedEntry.TABLE_NAME, null, values)
Java
// Gets the data repository in write modeSQLiteDatabase db = dbHelper.getWritableDatabase();// Create a new map of values, where column names are the keysContentValues values = new ContentValues();values.put(FeedEntry.COLUMN_NAME_TITLE, title);values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle);// Insert the new row, returning the primary key value of the new rowlong newRowId = db.insert(FeedEntry.TABLE_NAME, null, values);
Le premier argument de insert()
est simplement le nom de la table.
Le deuxième argument indique au framework ce qu’il faut faire dans le cas où la ContentValues
est vide (c’est-à-dire que vous n’avezput
aucune valeur).Si vous spécifiez le nom d’une colonne, le framework insère une ligne et définit la valeur de cette colonne à null. Si vous spécifiez null
, comme dans cet échantillon de code, le framework n’insère pas de ligne lorsqu’il n’y a pas de valeurs.
Les méthodes insert()
renvoient l’ID de la ligne nouvellement créée, ou bien elles renvoient -1 si une erreur s’est produite lors de l’insertion des données. Cela peut se produire si vous avez un conflit avec des données préexistantes dans la base de données.
Lire des informations à partir d’une base de données
Pour lire à partir d’une base de données, utilisez la méthode query()
, en lui passant vos critères de sélection et les colonnes souhaitées.La méthode combine des éléments de insert()
et update()
, sauf que la liste des colonnes définit les données que vous voulez récupérer (la « projection »), plutôt que les données à insérer. Les résultats de la requête vous sont retournés dans un objet Cursor
.
Kotlin
val db = dbHelper.readableDatabase// Define a projection that specifies which columns from the database// you will actually use after this query.val projection = arrayOf(BaseColumns._ID, FeedEntry.COLUMN_NAME_TITLE, FeedEntry.COLUMN_NAME_SUBTITLE)// Filter results WHERE "title" = 'My Title'val selection = "${FeedEntry.COLUMN_NAME_TITLE} = ?"val selectionArgs = arrayOf("My Title")// How you want the results sorted in the resulting Cursorval sortOrder = "${FeedEntry.COLUMN_NAME_SUBTITLE} DESC"val cursor = db.query( FeedEntry.TABLE_NAME, // The table to query projection, // The array of columns to return (pass null to get all) selection, // The columns for the WHERE clause selectionArgs, // The values for the WHERE clause null, // don't group the rows null, // don't filter by row groups sortOrder // The sort order)
Java
SQLiteDatabase db = dbHelper.getReadableDatabase();// Define a projection that specifies which columns from the database// you will actually use after this query.String projection = { BaseColumns._ID, FeedEntry.COLUMN_NAME_TITLE, FeedEntry.COLUMN_NAME_SUBTITLE };// Filter results WHERE "title" = 'My Title'String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?";String selectionArgs = { "My Title" };// How you want the results sorted in the resulting CursorString sortOrder = FeedEntry.COLUMN_NAME_SUBTITLE + " DESC";Cursor cursor = db.query( FeedEntry.TABLE_NAME, // The table to query projection, // The array of columns to return (pass null to get all) selection, // The columns for the WHERE clause selectionArgs, // The values for the WHERE clause null, // don't group the rows null, // don't filter by row groups sortOrder // The sort order );
Les troisième et quatrième arguments (selection
et selectionArgs
) sont combinés pour créer une clause WHERE. Comme les arguments sont fournis séparément de la requête de sélection, ils sont échappés avant d’être combinés. Ainsi, vos instructions de sélection sont à l’abri des injections SQL. Pour plus de détails sur tous les arguments, consultez laquery()
référence.
Pour regarder une ligne dans le curseur, utilisez l’une des Cursor
méthodes de déplacement, que vous devez toujours appeler avant de commencer à lire les valeurs. Comme le curseur commence à la position -1, l’appel de moveToNext()
place la « position de lecture » sur la première entrée des résultats et renvoie si le curseur a déjà dépassé ou non la dernière entrée du jeu de résultats. Pour chaque ligne, vous pouvez lire la valeur d’une colonne en appelant l’une des méthodes getCursor
, comme getString()
ou getLong()
. Pour chacune des méthodes get,vous devez passer la position de l’index de la colonne que vous désirez, que vous pouvez obtenir en appelantgetColumnIndex()
ougetColumnIndexOrThrow()
. Une fois l’itération des résultats terminée, appelez close()
sur le curseur pour libérer ses ressources.Par exemple, l’exemple suivant montre comment obtenir tous les ID d’éléments stockés dans un curseuret les ajouter à une liste :
Kotlin
val itemIds = mutableListOf<Long>()with(cursor) { while (moveToNext()) { val itemId = getLong(getColumnIndexOrThrow(BaseColumns._ID)) itemIds.add(itemId) }}
Java
List itemIds = new ArrayList<>();while(cursor.moveToNext()) { long itemId = cursor.getLong( cursor.getColumnIndexOrThrow(FeedEntry._ID)); itemIds.add(itemId);}cursor.close();
Supprimer des informations d’une base de données
Pour supprimer des lignes d’une table, vous devez fournir des critères de sélection qui identifient les lignes à la méthode delete()
. Le mécanisme de sélection fonctionne de la même manière que les arguments de sélection de la méthodequery()
. Il divise cette spécification de sélection en une clause de sélection et des arguments de sélection. La clause définit les colonnes à examiner et vous permet également de combiner les tests de colonnes. Les arguments sont des valeurs à tester qui sont liées à la clause.Comme le résultat n’est pas traité de la même manière qu’une instruction SQL ordinaire, il est immunisé contre les injections SQL.
Kotlin
// Define 'where' part of query.val selection = "${FeedEntry.COLUMN_NAME_TITLE} LIKE ?"// Specify arguments in placeholder order.val selectionArgs = arrayOf("MyTitle")// Issue SQL statement.val deletedRows = db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs)
Java
// Define 'where' part of query.String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";// Specify arguments in placeholder order.String selectionArgs = { "MyTitle" };// Issue SQL statement.int deletedRows = db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs);
La valeur de retour de la
// Define 'where' part of query.String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";// Specify arguments in placeholder order.String selectionArgs = { "MyTitle" };// Issue SQL statement.int deletedRows = db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs);
. valeur de retour de la méthodedelete()
indique le nombre de lignes qui ont été supprimées de la base de données.
Mettre à jour une base de données
Lorsque vous devez modifier un sous-ensemble des valeurs de votre base de données, utilisez la méthodeupdate()
.
La mise à jour de la table combine la syntaxe ContentValues
deinsert()
avec la syntaxe WHERE
de delete()
.
Kotlin
val db = dbHelper.writableDatabase// New value for one columnval title = "MyNewTitle"val values = ContentValues().apply { put(FeedEntry.COLUMN_NAME_TITLE, title)}// Which row to update, based on the titleval selection = "${FeedEntry.COLUMN_NAME_TITLE} LIKE ?"val selectionArgs = arrayOf("MyOldTitle")val count = db.update( FeedEntry.TABLE_NAME, values, selection, selectionArgs)
Java
SQLiteDatabase db = dbHelper.getWritableDatabase();// New value for one columnString title = "MyNewTitle";ContentValues values = new ContentValues();values.put(FeedEntry.COLUMN_NAME_TITLE, title);// Which row to update, based on the titleString selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";String selectionArgs = { "MyOldTitle" };int count = db.update( FeedReaderDbHelper.FeedEntry.TABLE_NAME, values, selection, selectionArgs);
La valeur de retour de la méthode update()
est le nombre de lignes affectées dans la base de données.
Connexion à la base de données persistante
Puisque getWritableDatabase()
et getReadableDatabase()
sont coûteux à appeler lorsque la base de données est fermée, vous devriez laisser votre connexion à la base de données ouverte aussi longtemps que vous avez éventuellement besoin d’y accéder. Typiquement, il est optimal de fermer la base de données dans la onDestroy()
de l’activité appelante.
Kotlin
override fun onDestroy() { dbHelper.close() super.onDestroy()}
Java
@Overrideprotected void onDestroy() { dbHelper.close(); super.onDestroy();}
Déboguer votre base de données
Le SDK Android comprend un sqlite3
outil shell qui vous permet de parcourir le contenu des tables, d’exécuter des commandes SQL et d’effectuer d’autres fonctions utiles sur les bases de données SQLited. Pour plus d’informations, consultez la rubrique comment émettre des commandes shell.