Vous êtes sur la page 1sur 26

La persistance des donnes

avec SQLite

Jean-marc Farinone

JMF (Tous droits rservs)

Remarques sur SQLite


La base de donnes FILENAME est stocke dans le smartphone
sous /data/data/NOM_PACKAGE_APPLI/databases/FILENAME
o NOM_PACKAGE_APPLI est le nom du package de l'application
Android (celui indiqu dans AndroidManifest.xml)
L'excution des requtes est effectue dans le mme processus que
l'application Android et une BD est rserve l'application cratrice :
seule cette application cratrice peut y accder. Si on veut des
donnes partageables entre applications Android, voir les
ContentProvider
"Any databases you create will be accessible by name to any class in
the application, but not outside the application."
source :
http://developer.android.com/guide/topics/data/datastorage.html#db
JMF (Tous droits rservs)

Une BD Android = une


SQLiteDatabase
Dans le code, une base de donnes est modlise par un objet de la
classe android.database.sqlite.SQLiteDatabase
Cette classe permet donc d'insrer des donnes (insert()), de les
modifier (update()), de les enlever (delete()), de lancer des
requtes SELECT (par query()) ou des requtes qui ne retournent
pas de donnes par execSQL()
Les requtes Create, Read, Update, Delete (~ INSERT, SELECT,
UPDATE, DELETE de SQL) sont dites des requtes CRUD

JMF (Tous droits rservs)

Une classe d'aide (helper)


Pour crer et/ou mettre jour une BD, on crit une classe qui hrite de la
classe abstraite android.database.sqlite.SQLiteOpenHelper
Cela ncessite de crer un contructeur qui appelera un des constructeurs
avec argument de SQLiteOpenHelper : il n'y a pas de constructeur sans
argument dans SQLiteOpenHelper
Le constructeur de SQLiteOpenHelper utilis est
public SQLiteOpenHelper (Context context, String name,
SQLiteDatabase.CursorFactory factory, int version)
context est le contexte de l'application
name est le nom du fichier contenant la BD
factory est utilis pour crer des Cursor. En gnral on met null
version est le numro de version de la BD (commenant 1)

JMF (Tous droits rservs)

Du helper SQLiteDatabse
= de la classe d'aide la base de donnes
Le constructeur prcdent est un proxy qui est xcut rapidement
La BD sera rellement cre au lancement de
getWritableDatabase() (pour une base en lecture et criture)
ou de getReadableDatabase() (pour une base en lecture seule)
sur cet objet de la classe d'aide
Bref on a :
private class MaBaseOpenHelper extends SQLiteOpenHelper { ... }
MaBaseOpenHelper leHelper = new MaBaseOpenHelper(...);
SQLiteDatabase maBaseDonnees = leHelper.getXXXableDatabase();

et un helper est (videmment) associ une base de donnes. XXX


est soit Writ soit Read
JMF (Tous droits rservs)

Cration, mise jour d'une


BD : code du helper
Sur un objet d'une classe hritant de SQLiteOpenHelper (classe
d'aide), certaines mthodes sont appeles automatiquement :
public void onCreate(SQLiteDatabase db) est appele
automatiquement par l'environnement d'excution quand la BD
n'existe pas. On met le code de cration des tables et leurs
contenus dans cette mthode
public void onUpgrade(SQLiteDatabase db, int
oldVersion, int newVersion) quand le numro de version
de la BD a t incrmente
Ces deux mthodes sont abstraites dans la classe de base et doivent
donc tre implmentes dans la classe d'aide
maBaseDonnees de classe SQLiteDatabase sera obtenu comme
retour de getWritableDatabase() ou
getReadableDatabase()
JMF (Tous droits rservs)

Code de la classe d'aide


MaBaseOpenHelper
private class MaBaseOpenHelper extends SQLiteOpenHelper {
private static final String REQUETE_CREATION_TABLE = "create table "
+ TABLE_PLANETES + " (" + COLONNE_ID
+ " integer primary key autoincrement, " + COLONNE_NOM
+ " text not null, " + COLONNE_RAYON + " text not null);";
public MaBaseOpenHelper(Context context, String nom,
CursorFactory cursorfactory, int version) {
super(context, nom, cursorfactory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(REQUETE_CREATION_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Dans notre cas, nous supprimons la base et les donnes pour en
// crer une nouvelle ensuite. Vous pouvez crer une logique de mise
// jour propre votre base permettant de garder les donnes la
// place.
db.execSQL("drop table" + TABLE_PLANETES + ";");
// Cration de la nouvelle structure.
onCreate(db);
}
}

JMF (Tous droits rservs)

Insrer des donnes dans


une SQLiteDatabase (1/2)
Ayant obtenu une SQLiteDatabase, on utilise les mthodes de
cette classe pour faire des oprations sur la base de donnes
public long insert (String table, String
nullColumnHack, ContentValues values) insert dans la table
table les valeurs indiques par values
values, de classe ContentValues, est une suite de couples (cl,
valeur) o la cl, de classe String, est le nom de la colonne et
valeur, sa valeur
Bref on prpare tout, la ligne insrer en remplissant values par
des put() successifs puis on lance insert()
Exemple : /**
* Insre une plante dans la table des plantes.
*/
public long insertPlanete(Planete planete) {
ContentValues valeurs = new ContentValues();
valeurs.put(COLONNE_NOM, planete.getNom());
valeurs.put(COLONNE_RAYON,
planete.getRayon());
JMF (Tous droits rservs)
8
return maBaseDonnees.insert(TABLE_PLANETES, null, valeurs);
}

Insrer des donnes dans


une SQLiteDatabase (2/2)
Le second argument nullColumnHack est le nom de colonne qui
aura la valeur NULL si values est vide. Cela est du au fait que
SQLite ne permet pas de lignes vides. Ainsi avec cette colonne, au
moins un champ dans la ligne aura une valeur (= NULL). Bref cela
sert seulement lorsque values est vide !
Cette mthode insert() retourne le numro de la ligne insre ou
-1 en cas d'erreur
En fait cette insertion est le Create (C) de CRUD

JMF (Tous droits rservs)

Rcuprer des donnes dans


une SQLiteDatabase
La mthode la plus simple (!) pour rcuprer des donnes (~ SELECT) est :
public Cursor query (String table, String[] columns, String
whereClause, String[] selectionArgs, String groupBy, String
having, String orderBy)
columns est la liste des colonnes retourner. Mettre null si on veut toutes les
colonnes
whereClause est la clause WHERE d'un SELECT (sans le mot WHERE). Mettre
null si on veut toutes les lignes
selectionArgs est utile si dans whereClause (~ WHERE), il y a des
paramtres nots ?. Les valeurs de ces paramtres sont indiqus par
selectionArgs. Bref en gnral on met null
groupBy est la clause GROUP BY d'un SELECT (sans les mots GROUP BY). Utile
pour des SELECT COUT(*). Bref on gnral on met null
having indique les groupes de lignes retourner (comme HAVING de SQL = un
WHERE sur rsultat d'un calcul, pas sur les donnes)
orderBy est la clause ORDER BY d'un SELECT (sans les mots ORDER BY). Mettre
null si on ne tient pas compte de l'ordre
JMF (Tous droits rservs)

10

Exemple de requte SELECT


pour Android
Euh, la classe
android.database.sqlite.SQLiteQueryBuilder est
(semble) faite pour cela
Voir http://sqlite.org/lang.html
Sinon, quelques exemples
Rappel : SELECT peut tre obtenu avec : public Cursor query
(String table, String[] columns, String
whereClause, String[] selectionArgs, String
groupBy, String having, String orderBy)
db.query(TABLE_CONTACTS, new String[] { KEY_ID,
KEY_NAME, KEY_PH_NO }, KEY_ID + "=?", new String[]
{ String.valueOf(id) }, null, null, null, null); est
l'quivalent de "SELECT KEY_ID, KEY_NAME, KEY_PH_NO FROM
TABLE_CONTACTS WHERE KEY_ID='" + id + "'"
JMF (Tous droits rservs)

11

rawQuery() : requte SELECT


pour Android
La mthode rawQuery() de SQLiteDatabase permet de lancer
des simples requtes SELECT comme SELECT * FROM " +
TABLE_CONTACTS WHERE condition paramtre
Sa signature est public Cursor rawQuery (String sql,
String[] selectionArgs) o sql est une requte SELECT (qui
ne doit pas tre termine par ;) et selectionArgs est le tableau
qui fixe les valeurs des paramtres (not ?) dans la clause WHERE
Par exemple :
String countQuery = "SELECT * FROM " + TABLE_CONTACTS;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(countQuery, null);

JMF (Tous droits rservs)

12

L'objet Cursor (1/2)


La mthode query() retourne un android.database.Cursor
(~ java.sql.ResultSet). android.database.Cursor est une
interface. Ce qui est retourn est un objet d'une classe qui
implmente cette interface
C'est similaire JDBC. Le Cursor reprsente un ensemble de
"lignes" contenant le rsultat de la requte SELECT
public int getCount() retourne le nombre de lignes contenues
dans le Cursor
On se positionne au dbut du Cursor (= avant la premire ligne)
par la mthode public boolean moveToFirst() (qui retourne
false si le Cursor est vide
On teste si on a une nouvelle ligne lire par la mthode public
boolean moveToNext() (qui retourne false si on tait positionn
aprs la dernire ligne)
JMF (Tous droits rservs)

13

L'objet Cursor (2/2)


On rcupre la columnIndex cellule de la ligne par la mthode :
public XXX getXXX(int columnIndex). columnIndex est
(videmment) le numro de la cellule dans la requte. XXX est le
type retourn (String, short, int, long, float, double)
Il n'y a pas de getXXX(String nomDeColonne) contrairement JDBC
On referme le Cursor (et libre ainsi les ressources) par public
void close ()
On peut avoir des renseignements sur le rsultat de la requte
SELECT (* FROM ...) (Mta donnes) l'aide du Cursor comme :
public int getColumnCount() qui retourne le nombre de
colonnes contenues dans le Cursor
public String getColumnName(int columnIndex) qui
retourne le nom de la columnIndex ime colonne
JMF (Tous droits rservs)

14

L'accs aux donnes : un


DAO
Manipuler le Cursor, c'est bien. C'est "un peu" de la programmation
"bas niveau"
Bref un DAO (= Data Access Object), voire une faade s'impose !
Pour accder aux donnes, on masque les bidouilles sous jacentes
(requte SQL, etc.) par un objet d'une classe DAO : un bon design
pattern !
Les bidouilles SQL masques par le DAO sont :
insrer des donnes dans la BD par la mthode insert() de la
classe SQLiteDatabase
retourner toutes les donnes d'une table par la mthode
query() de la classe SQLiteDatabase

JMF (Tous droits rservs)

15

Les constantes de
l'application
Ce sont :
private static final int BASE_VERSION = 1;
private static final String BASE_NOM = "planetes.db";
private static final String TABLE_PLANETES = "table_planetes";
public
public
public
public
public
public

static
static
static
static
static
static

final
final
final
final
final
final

String COLONNE_ID = "id";


int COLONNE_ID_ID = 0;
String COLONNE_NOM = "nom";
int COLONNE_NOM_ID = 1;
String COLONNE_RAYON = "rayon";
int COLONNE_RAYON_ID = 2;

JMF (Tous droits rservs)

16

Le code du DAO (1/4)


public class PlanetesDB_DAO {
private SQLiteDatabase maBaseDonnees;
private MaBaseOpenHelper baseHelper;
public PlanetesDB_DAO(Context ctx) {
baseHelper = new MaBaseOpenHelper(ctx, BASE_NOM, null, BASE_VERSION);
}
public SQLiteDatabase open() {
maBaseDonnees = baseHelper.getWritableDatabase();
return maBaseDonnees;
}
public void close() {
maBaseDonnees.close();
}
/**
* Rcupre une plante en fonction de son nom.
* @param nom
*
Le nom de la plante retourner.
* @return La plante dont le nom est gale au paramtre 'nom'.
*/
public Planete getPlanete(String nom) {
Cursor c = maBaseDonnees.query(TABLE_PLANETES, new String[] {
COLONNE_ID, COLONNE_NOM, COLONNE_RAYON }, null, null, null,
COLONNE_NOM + " LIKE " + nom, null);
JMF (Tous droits rservs)
17
return cursorToPlanete(c);
}

Le code du DAO (2/4)


private Planete cursorToPlanete(Cursor c) {
// Si la requte ne renvoie pas de rsultat
if (c.getCount() == 0)
return null;
Planete retPlanete = new Planete();
// Extraction des valeurs depuis le curseur
retPlanete.setId(c.getInt(COLONNE_ID_ID));
retPlanete.setNom(c.getString(COLONNE_NOM_ID));
retPlanete.setRayon(c.getFloat(COLONNE_RAYON_ID));
// Ferme le curseur pour librer les ressources
c.close();
return retPlanete;
}

JMF (Tous droits rservs)

18

Le code du DAO (3/4)


/**
* Retourne toutes les plantes de la base de donnes.
*
* @return Un ArrayList<Planete> contenant toutes les plantes de la BD
*/
public ArrayList<Planete> getAllPlanetes() {
Cursor c = maBaseDonnees.query(TABLE_PLANETES, new String[] {
COLONNE_ID, COLONNE_NOM, COLONNE_RAYON }, null, null, null,
null, null);
return cursorToPlanetes(c);
}
private ArrayList<Planete> cursorToPlanetes(Cursor c) {
// Si la requte ne renvoie pas de rsultat
if (c.getCount() == 0)
return new ArrayList<Planete>(0);
ArrayList<Planete> retPlanetes = new ArrayList<Planete>(c.getCount());
c.moveToFirst();
do {
Planete planete = new Planete();
planete.setId(c.getInt(COLONNE_ID_ID));
planete.setNom(c.getString(COLONNE_NOM_ID));
planete.setRayon(c.getFloat(COLONNE_RAYON_ID));
retPlanetes.add(planete);
} while (c.moveToNext());
// Ferme le curseur pour librer les ressources
c.close();
JMF (Tous droits rservs)
19
return retPlanetes;
}

Le code du DAO (4/4)


/**
* Insre une plante dans la table des plantes.
*
* @param planete
*
La plante insrer.
*/
public long insertPlanete(Planete planete) {
ContentValues valeurs = new ContentValues();
valeurs.put(COLONNE_NOM, planete.getNom());
valeurs.put(COLONNE_RAYON, planete.getRayon());
return maBaseDonnees.insert(TABLE_PLANETES, null, valeurs);
}
public void videLaBase() {
// Dans notre cas, nous supprimons la base et les donnes pour en
// crer une nouvelle ensuite. Vous pouvez crer une logique de mise
// jour propre votre base permettant de garder les donnes la
// place.
maBaseDonnees.execSQL("drop table " + TABLE_PLANETES + ";");
// Cration de la nouvelle structure.
maBaseDonnees.execSQL(REQUETE_CREATION_TABLE);
}
}

JMF (Tous droits rservs)

20

Dmonstration
Projet ProgAndroidBDJMFProjet dans ...\Travail

JMF (Tous droits rservs)

21

Retour sur l'IHM : le


TableLayout
Pour afficher des enregistrements, une table est approprie
Android propose le TableLayout calqu sur l'attribut table de HTML :
<table>
<tr><td>et 1</td><td>et 2</td></tr>
</table>

a pour quivalent dans les fichiers d'IHM d'Android :


<TableLayout>
<TableRow><TextView ...>et 1</TextView><TextView ...>et 2</TextView></TableRow>
</TableLayout>

Nanmoins, il faut souvent construire cette TableLayout dynamiquement


(on ne connait pas le nombre d'articles afficher) :
TableLayout table = (TableLayout) findViewById(R.id.tableLayoutLesContacts);
Iterator<Contact> it = arContacts.iterator();
while (it.hasNext()) {
Contact ct = (Contact)it.next();
// cration d'une nouvelle TableRow
TableRow row = new TableRow(this);
TextView tNom = new TextView(this);
tNom.setText(ct.getName());
row.addView(tNom);
TextView tNumTel = new TextView(this);
tNumTel.setText(ct.getPhoneNumber());
row.addView(tNumTel);
JMF (Tous droits rservs)
table.addView(row,new TableLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
}

22

Modifier une table : UPDATE


de SQL pour Android
Pour mettre jour des lignes dans une table, la mthode utilise (de la classe
SQLiteDatabase) est public int update (String table,
ContentValues values, String whereClause, String[]
whereArgs) o :
table est la table qui doit tre mise jour
values est une suite de couples (cl, valeur) o la cl, de classe
String, est le nom de la colonne et valeur, sa valeur
whereClause est la clause WHERE filtrant les lignes mettre jour. Si
la valeur est null, toutes les lignes sont mises jour
whereArgs indiquent les valeurs passer aux diffrents arguments de
la clause WHERE qui sont nots ? dans whereClause
Cette mthode retourne le nombre de ligne qui ont t affectes
Exemple :

SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_NAME, contact.getName());
values.put(KEY_PH_NO, contact.getPhoneNumber());
JMF (Tous droits
rservs)
db.update(TABLE_CONTACTS,
values,
KEY_ID + " = ?",
new String[] { String.valueOf(contact.getID()) });

23

Supprimer des lignes dans


une table : DELETE
Pour supprimer des lignes dans une table, la mthode utilise (de la classe
SQLiteDatabase) est public int delete (String table, String
whereClause, String[] whereArgs)
table est la table manipuler
whereClause est la clause WHERE filtrant les lignes supprimer. Si la
valeur est null, toutes les lignes sont dtruites
whereArgs indiquent les valeurs passer aux diffrents arguments de
la clause WHERE qui sont nots ? dans whereClause
Cette mthode retourne le nombre de ligne qui ont t supprimes
Exemple :

public void deleteContact(Contact contact) {


SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_CONTACTS, KEY_ID + " = ?",
new String[] { String.valueOf(contact.getID()) });
db.close();
}

JMF (Tous droits rservs)

24

Bibliographie pour ce
chapitre
Programmation Android, De la conception au dploiement avec lee
SDK Google Android 2, Damien Guignard, Julien Chable, Emmanuel
Robles ; editions Eyrolles, chapitre 6
Un tutorial sur SQLite :
http://www.androidhive.info/2011/11/androidsqlite-database-tutorial/

JMF (Tous droits rservs)

25

Fin

JMF (Tous droits rservs)

26

Vous aimerez peut-être aussi