Вы находитесь на странице: 1из 26

JMF (Tous droits rservs) 1

La persistance des donnes


avec SQLite
Jean-marc Farinone
JMF (Tous droits rservs) 2
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 B est rser!e " l'application cratrice #
seule cette application cratrice peut $ accder% &i on !eut des
donnes partageables entre applications Android' !oir les
ContentProvider

(An$ databases $ou create )ill be accessible b$ name to an$ class in


the application' but not outside the application%(
source #
http://developer.android.com/guide/topics/data/data-
storage.html#db
JMF (Tous droits rservs) 3
Une BD Android = une
SQLiteDatabase

ans le code' une base de donnes est modlise par un ob*et de la


classe android.database.sqlite.SQLiteDatabase

+ette classe permet donc d'insrer des donnes (insert())' de les


modifier (update())' de les enle!er (delete())' de lancer des
requtes &,L,+- (par query()) ou des requtes qui ne retournent
pas de donnes par execSQL()

Les requtes +reate' .ead' /pdate' elete (0 12&,.-' &,L,+-'


/3A-,' ,L,-, de &4L) sont dites des requtes +./
JMF (Tous droits rservs) 4
Une classe d'aide !elper"

3our crer et5ou mettre " *our une B' on crit une classe qui hrite de la
classe abstraite android.database.sqlite.SQLiteOpenHelper

+ela ncessite de crer un contructeur qui appelera un des constructeurs


a!ec argument de SQLiteOpenHelper # il n'$ 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 B

factory est utilis pour crer des Cursor% ,n gnral on met null

version est le numro de !ersion de la B (commen6ant " 7)


JMF (Tous droits rservs) 5
Du !elper # SQLiteData$se

8 de la classe d'aide " la base de donnes

Le constructeur prcdent est un prox$ qui est xcut rapidement

La B 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 ob*et de la classe d'aide

Bref on a #
et un helper est (!idemment) associ " une base de donnes% 999
est soit :rit soit .ead
private class MaBaseOpenHelper extends SQLiteOpenHelper { ... }
MaBaseOpenHelper leHelper = new MaBaseOpenHelper(...);
SQLiteDatabase maBaseDonnees = leHelper.getXXXableDatabase();
JMF (Tous droits rservs) 6
%ration& mise # 'our d'une
BD ( code du !elper

&ur un ob*et d'une classe hritant de SQLiteOpenHelper (classe


d'aide)' certaines mthodes sont appeles automatiquement #

public void onCreate(SQLiteDatabase db) est appele


automatiquement par l'en!ironnement d'excution quand la B
n'existe pas% ;n 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 !ersion
de la B a t incrmente

+es deux mthodes sont abstraites dans la classe de base et doi!ent


donc tre implmentes dans la classe d'aide

maBaseDonnees de classe SQLiteDatabase sera obtenu comme


retour de getWritableDatabase() ou
getReadableDatabase()
JMF (Tous droits rservs) 7
%ode 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) 8
)nsrer des donnes dans
une SQLiteDatabase *+,"

A$ant 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 !aleurs indiques par values

values' de classe ContentValues' est une suite de couples (cl'


!aleur) o la cl' de classe String' est le nom de la colonne et
valeur' sa !aleur

Bref on prpare tout' la ligne " insrer en remplissant values par


des put() successifs puis on lance insert()

,xemple #
/**
* 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());
return maBaseDonnees.insert(TABLE_PLANETES, null, valeurs);
}
JMF (Tous droits rservs) 9
)nsrer des donnes dans
une SQLiteDatabase ,+,"

Le second argument nullColumnHack est le nom de colonne qui


aura la !aleur 2/LL si values est !ide% +ela est du au fait que
&4Lite ne permet pas de lignes !ides% Ainsi a!ec cette colonne' au
moins un champ dans la ligne aura une !aleur (8 2/LL)% Bref cela
sert seulement lorsque values est !ide <

+ette mthode insert() retourne le numro de la ligne insre ou


=7 en cas d'erreur

,n fait cette insertion est le +reate (+) de +./


JMF (Tous droits rservs) 10
Rcuprer des donnes dans
une SQLiteDatabase

La mthode la plus simple (<) pour rcuprer des donnes (0 &,L,+-) 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% >ettre null si on !eut toutes les
colonnes

whereClause est la clause :?,., d'un &,L,+- (sans le mot :?,.,)% >ettre
null si on !eut toutes les lignes

selectionArgs est utile si dans whereClause (0 :?,.,)' il $ a des


paramtres nots ?% Les !aleurs de ces paramtres sont indiqus par
selectionArgs% Bref en gnral on met null

groupBy est la clause @.;/3 BA d'un &,L,+- (sans les mots @.;/3 BA)% /tile
pour des &,L,+- +;/-(B)% Bref on gnral on met null

having indique les groupes de lignes " retourner (comme ?AC12@ de &4L 8 un
:?,., sur rsultat d'un calcul' pas sur les donnes)

orderBy est la clause ;.,. BA d'un &,L,+- (sans les mots ;.,. BA)% >ettre
null si on ne tient pas compte de l'ordre
JMF (Tous droits rservs) 11
-.emple de requ/te SELECT
pour Android

,uh' la classe
android.database.sqlite.SQLiteQueryBuilder est
(semble) faite pour cela

Coir " http://sqlite.org/lang.html

&inon' quelques exemples

.appel # SELECT peut tre obtenu a!ec # 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'qui!alent de (SELECT KEY_ID, KEY_NAME, KEY_PH_NO FROM
TABLE_CONTACTS WHERE KEY_ID='" + id + "'"
JMF (Tous droits rservs) 12
rawQuery() ( requ/te SELECT
pour Android

La mthode rawQuery() de SQLiteDatabase permet de lancer


des simples requtes SELECT comme SELECT * FROM " +
TABLE_CONTACTS WHERE condition paramtre

&a signature est public Cursor rawQuery (String sql,


String[] selectionArgs) o sql est une requte &,L,+- (qui
ne doit pas tre termine par D) et selectionArgs est le tableau
qui fixe les !aleurs des paramEtres (not F) dans la clause :?,.,

3ar exemple #
String countQuery = "SELECT * FROM " + TABLE_CONTACTS;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(countQuery, null);
JMF (Tous droits rservs) 13
L'o$'et Cursor *+,"

La mthode query() retourne un android.database.Cursor


(0 java.sql.ResultSet)% android.database.Cursor est une
interface% +e qui est retourn est un ob*et d'une classe qui
implmente cette interface

+'est similaire " GB+% Le Cursor reprsente un ensemble de


(lignes( contenant le rsultat de la requte &,L,+-

public int getCount() retourne le nombre de lignes contenues


dans le Cursor

;n se positionne au dbut du Cursor (8 a!ant la premiEre ligne)


par la mthode public boolean moveToFirst() (qui retourne
false si le Cursor est !ide

;n teste si on a une nou!elle ligne " lire par la mthode public


boolean moveToNext() (qui retourne false si on tait positionn
aprEs la derniEre ligne)
JMF (Tous droits rservs) 14
L'o$'et Cursor ,+,"

;n rcupEre la columnIndex cellule de la ligne par la mthode #


public XXX getXXX(int columnIndex)% columnIndex est
(!idemment) le numro de la cellule dans la requte% XXX est le
t$pe retourn (&tring' short' int' long' float' double)

1l n'$ a pas de get999(&tring nome+olonne) contrairement " GB+

;n referme le Cursor (et libEre ainsi les ressources) par public


void close ()

;n peut a!oir des renseignements sur le rsultat de la requte


&,L,+- (B H.;> %%%) (>ta 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 iEme colonne
JMF (Tous droits rservs) 15
L'acc0s au. donnes ( un
DA1

>anipuler le Cursor' c'est bien% +'est (un peu( de la programmation


(bas ni!eau(

Bref un A; (8 ata Access ;b*ect)' !oire une fa6ade s'impose <

3our accder aux donnes' on masque les bidouilles sous *acentes


(requte &4L' etc%) par un ob*et d'une classe A; # un bon design
pattern <

Les bidouilles &4L masques par le A; sont #

insrer des donnes dans la B 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) 16
Les constantes de
l'application

+e 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 static final String COLONNE_ID = "id";
public static final int COLONNE_ID_ID = 0;
public static final String COLONNE_NOM = "nom";
public static final int COLONNE_NOM_ID = 1;
public static final String COLONNE_RAYON = "rayon";
public static final int COLONNE_RAYON_ID = 2;
JMF (Tous droits rservs) 17
Le code du DA1 *+2"
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);
return cursorToPlanete(c);
}
JMF (Tous droits rservs) 18
Le code du DA1 ,+2"
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) 19
Le code du DA1 3+2"
/**
* 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();
return retPlanetes;
}
JMF (Tous droits rservs) 20
Le code du DA1 2+2"
/**
* 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) 21
Dmonstration

3ro*et 3rogAndroidBG>H3ro*et dans %%%I-ra!ail


JMF (Tous droits rservs) 22
Retour sur l')45 ( le
TableLayout

3our afficher des enregistrements' une table est approprie

Android propose le TableLayout calqu sur l'attribut table de ?->L #


a pour qui!alent dans les fichiers d'1?> d'Android #

2anmoins' il faut sou!ent construire cette TableLayout d$namiquement


(on ne connait pas le nombre d'articles " afficher) #
<table>
<tr><td>et 1</td><td>et 2</td></tr>
</table>
<TableLayout>
<TableRow><TextView ...>et 1</TextView><TextView ...>et 2</TextView></TableRow>
</TableLayout>
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);
table.addView(row,new TableLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
}
JMF (Tous droits rservs) 23
5odi6ier une ta$le ( UPDATE
de SQL pour Android

3our mettre " *our 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 " *our

values est une suite de couples (cl' !aleur) o la cl' de classe


String' est le nom de la colonne et !aleur' sa !aleur

whereClause est la clause :?,., filtrant les lignes " mettre " *our% &i
la !aleur est null' toutes les lignes sont mises " *our

whereArgs indiquent les !aleurs " passer aux diffrents arguments de


la clause :?,., qui sont nots ? dans whereClause

+ette mthode retourne le nombre de ligne qui ont t affectes

,xemple #
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_NAME, contact.getName());
values.put(KEY_PH_NO, contact.getPhoneNumber());
db.update(TABLE_CONTACTS, values, KEY_ID + " = ?",
new String[] { String.valueOf(contact.getID()) });
JMF (Tous droits rservs) 24
Supprimer des li7nes dans
une ta$le ( DELETE

3our 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 :?,., filtrant les lignes " supprimer% &i la
!aleur est null' toutes les lignes sont dtruites

whereArgs indiquent les !aleurs " passer aux diffrents arguments de


la clause :?,., qui sont nots ? dans whereClause

+ette mthode retourne le nombre de ligne qui ont t supprimes

,xemple #
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) 25
Bi$lio7rap!ie pour ce
c!apitre

3rogrammation Android' e la conception au dploiement a!ec lee


&J @oogle Android K' amien @uignard' Gulien +hable' ,mmanuel
.obles D editions ,$rolles' chapitre L

/n tutorial sur &4Lite #


http://www.androidhive.info/2011/11/android-
sqlite-database-tutorial/
JMF (Tous droits rservs) 26
Fin