0% ont trouvé ce document utile (0 vote)
115 vues29 pages

Cours - Room

Ce module de formation sur la création d'applications Android sécurisées se concentre sur l'utilisation de Room avec Kotlin pour la gestion des données. Les participants apprendront à implémenter une base de données en suivant l'architecture MVVM, à effectuer des opérations CRUD, et à gérer les relations entre entités tout en utilisant des fonctionnalités avancées comme LiveData et les Coroutines. Le cours met en avant les avantages de Room par rapport à SQLite, notamment la réduction du code répétitif et une meilleure gestion des threads.

Transféré par

alaabendahane331
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
115 vues29 pages

Cours - Room

Ce module de formation sur la création d'applications Android sécurisées se concentre sur l'utilisation de Room avec Kotlin pour la gestion des données. Les participants apprendront à implémenter une base de données en suivant l'architecture MVVM, à effectuer des opérations CRUD, et à gérer les relations entre entités tout en utilisant des fonctionnalités avancées comme LiveData et les Coroutines. Le cours met en avant les avantages de Room par rapport à SQLite, notamment la réduction du code répétitif et une meilleure gestion des threads.

Transféré par

alaabendahane331
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Module: Elaborer une application Android sécurisée

Manipuler les données sous Android


Maîtriser Room sur Android avec Kotlin

Room
Page
Formateur :EL MERNISSI Abderazzak DevOAM 2024/2025 ISTA NTIC SAFI 1
Plan de Cours
Objectifs

À la fin du cours, on sera tous capables de :

● Comprendre le fonctionnement de Room et ses avantages par rapport à SQLite.

● Implémenter une base de données avec Room en respectant l’architecture MVVM.

● Effectuer des opérations CRUD (Create, Read, Update, Delete).

● Gérer les relations entre les entités.

● Utiliser LiveData et Suspend functions avec Room.

● Mettre en place la migration et la gestion des versions.

2
Problèmes avec SQLite
● Problèmes majeurs :

○ Beaucoup de code répétitif (boilerplate)

○ Gestion des requêtes SQL manuelle (risque d’erreurs)

○ Pas de vérification des requêtes SQL à la compilation

○ Manipulation des curseurs complexe

○ Gestion des threads difficile

3
Pourquoi Room est une Meilleure Alternative ?
● Moins de code, plus simple à lire et à maintenir

● Vérification SQL à la compilation

● Intégration avec LiveData et Flow pour observer les changements

● Gestion des threads facilitée avec Coroutines

● Abstraction complète de SQLite

4
Insertion d’un Utilisateur - SQLite vs Room
Avec SQLite : Avec Room :

val db = [Link] @Insert


val values = ContentValues().apply { suspend fun insert(user: User)
put("name", "Ahmed")

put("email", "Ahmed@[Link]")

[Link]("users", null, values)

[Link]()

✅ Moins de code, pas de gestion manuelle de ContentValues et de writableDatabase.


5
Lecture des Données - SQLite vs Room
Avec SQLite : Avec Room :

val db = [Link] @Query("SELECT * FROM users")


val cursor = [Link]("SELECT * FROM users", null)
fun getAllUsers(): LiveData<List<User>>
val users = mutableListOf<User>()

while ([Link]()) {

val id = [Link](0)

val name = [Link](1)

val email = [Link](2)

[Link](User(id, name, email))

[Link]()

[Link]()

✅ Pas besoin de gérer les curseurs, les données sont observables en temps réel avec LiveData ou
Flow. 6
Mise à jour et Suppression - SQLite vs Room
Avec SQLite : Avec Room :

val db = [Link] @Update


val values = ContentValues().apply {
suspend fun update(user: User)
put("name", "Updated Name")

[Link]("users", values, "id = ?",


arrayOf([Link]()))

[Link]()

✅ Beaucoup plus simple avec Room !


7
Introduction à l’Architecture de Room
● Room suit l'architecture MVVM (Model-View-ViewModel) .

● Séparation claire des responsabilités :

○ Les données sont gérées indépendamment de l’UI.

○ Facilite la testabilité et la maintenance.

● Schéma général :
🔹 UI (Activity/Fragment) → 🔹 ViewModel →

→🔹 Repository → 🔹 DAO → 🔹 Database

8
Les Composants de Room
Composant Rôle

Entity Représente une table en base de données

DAO Fournit les méthodes pour interagir avec


la base de données

Database Point d'accès central, gère la connexion


et la migration

Repository Interface entre la base de données et le


ViewModel

ViewModel Fournit des données à l'UI sans affecter


son cycle de vie

UI Affiche les données à l’utilisateur


(Activity/Frag
ment) 9
Entité (@Entity)
● Représente une table dans la base de données.

● Chaque champ correspond à une colonne.

● La clé primaire est définie avec @PrimaryKey.

Exemple de classe User

@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val name: String,
val email: String
)

✅ Facile à comprendre, pas besoin de SQL !

10
DAO (@Dao) et ses méthodes principales
● DAO (Data Access Object) contient les méthodes d’accès aux données.

● Utilisation d’annotations comme @Insert, @Update, @Delete, @Query.

📌 Exemple de DAO UserDao


@Dao
interface UserDao {
@Insert
suspend fun insert(user: User)

@Update
suspend fun update(user: User)

@Delete
suspend fun delete(user: User)

@Query("SELECT * FROM users")


fun getAllUsers(): Flow<List<User>>
}

✅ Avantages :
● Plus besoin de rawQuery().

● Intégration avec Coroutines (suspend) et Flow. 11


Base de données (@Database)
● Point central de Room.

● Doit être une classe abstraite qui hérite de RoomDatabase.

● Contient une liste des DAO.

📌 Exemple : AppDatabase

@Database(entities = [User::class], version = 1)

abstract class AppDatabase : RoomDatabase() {

abstract fun userDao(): UserDao

📌 Initialisation de la base de données dans Application :

val db = [Link](

applicationContext,

AppDatabase::[Link], "my_database"

).build()

✅ Permet d’avoir une base de données unique en Singleton.


12
Repository et son rôle
● Pourquoi utiliser un Repository ?

○ Sépare la logique métier de l'UI.

○ Permet de gérer plusieurs sources de données (ex. API + Room).

○ Facilite la gestion des Coroutines.

📌 Exemple : UserRepository

class UserRepository(private val userDao: UserDao) {


val allUsers: Flow<List<User>> = [Link]()

suspend fun insert(user: User) {


[Link](user)
}

suspend fun update(user: User) {


[Link](user)
}

suspend fun delete(user: User) {


[Link](user)
}
}

✅ Le ViewModel ne connaît pas la base de données directement !


13
ViewModel et gestion des données
● ViewModel : Fournit les données à l’UI et survit aux changements de configuration.

● Utilisation de LiveData ou Flow pour observer les données.

📌 Exemple : UserViewModel

class UserViewModel(private val repository: UserRepository) : ViewModel() {


val allUsers: LiveData<List<User>> = [Link]()

fun insert(user: User) = [Link] {


[Link](user)
}

fun delete(user: User) = [Link] {


[Link](user)
}
}

✅ Pas d’accès direct à Room, tout passe par le Repository !


14
Intégration avec l’UI (Activity/Fragment)
● Afficher la liste des utilisateurs avec un RecyclerView.

● Ajouter un utilisateur en cliquant sur un bouton.

📌 Exemple : MainActivity

class MainActivity : AppCompatActivity() {


private lateinit var userViewModel: UserViewModel

override fun onCreate(savedInstanceState: Bundle?) {


[Link](savedInstanceState)
setContentView([Link].activity_main)

userViewModel = ViewModelProvider(this).get(UserViewModel::[Link])

[Link](this) { users ->


println("Utilisateurs : $users")
}
}
}

✅ L’UI observe les changements en temps réel.


15
Résumé et Schéma Global
✅ Architecture complète :
📌 Schéma MVVM avec Room

UI (Activity/Fragment)

ViewModel

Repository

DAO (UserDao)

Room Database

16
Annotation @ColumnInfo(name = "...")
Problème : Si le nom d'une colonne dans la base de données est différent du nom de l’attribut dans l’Entity.

Solution : Utilisation de @ColumnInfo(name = "nom_colonne")

Exemple :

@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true) val id: Int = 0,

@ColumnInfo(name = "full_name")
val name: String,

@ColumnInfo(name = "email_address")
val email: String
)

✅ Cela permet d’avoir une correspondance claire entre la base de données et l’Entity.

17
Clés étrangères avec @ForeignKey
Problème : Comment lier une table à une autre avec une clé étrangère ?
Solution : Utilisation de @ForeignKey dans l'annotation @Entity.

Exemple : Un utilisateur peut avoir plusieurs commandes.

@Entity(
tableName = "orders",
foreignKeys = [
ForeignKey( Explication :
entity = User::class, ● entity = User::class → Indique que cette entité dépend de la table
parentColumns = ["id"],
User.
childColumns = ["user_id"],
onDelete = [Link] ● parentColumns = ["id"] → Désigne la colonne clé primaire de la table
)
User.
]
) ● childColumns = ["user_id"] → Désigne la colonne de la table enfant qui
data class Order( fait référence à id de User.
@PrimaryKey(autoGenerate = true)
val id: Int = 0, ● onDelete = [Link] → Si un utilisateur (User) est
@ColumnInfo(name = "user_id") supprimé, toutes les entrées associées dans la table enfant seront également
val userId: Int,
val product: String,
supprimées automatiquement.
val amount: Int,
) 💡 Si un User est supprimé, toutes ses commandes (Order) seront supprimées
automatiquement. 18
Explication de @Embedded
@Embedded

val address: Address

Explication :

● @Embedded permet d'intégrer un objet dans une entité Room sans créer une table séparée.

● Il fonctionne comme une composition d'objets en SQL.

Exemple :

data class Address(


val street: String,
val city: String,
val zipCode: String
)

@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true) val id: Int,
val name: String,
@Embedded val address: Address // Insère directement les champs d'Address
)

💡 Dans la base de données, la table users contiendra directement les colonnes street, city et zipCode. 19
Explication de @Relation
@Relation(
parentColumn = "id",
entityColumn = "user_id"
)

Explication :
@Relation permet de récupérer les données liées entre une table parent et une table
enfant sans requête SQL manuelle.
parentColumn = "id" → Indique la clé primaire de la table parent.
entityColumn = "user_id" → Indique la clé étrangère dans la table enfant.
Exemple :
data class UserWithOrders(
@Embedded val user: User,
@Relation(
parentColumn = "id",
entityColumn = "user_id"
)
val orders: List<Order>
)
Cela permet de récupérer un User avec toutes ses commandes (orders) en une seule requête !
20
Récupérer les données avec @Transaction
Pour récupérer un utilisateur avec ses commandes :

@Dao

interface UserDao {

@Transaction

@Query("SELECT * FROM users WHERE id = :userId")

fun getUserWithOrders(userId: Int): LiveData<UserWithOrders>

💡 Grâce à @Transaction, Room s'assure que toutes les requêtes sont exécutées ensemble pour éviter des incohérences.

21
Exemple d'utilisation de @Transaction dans un contexte bancaire 🏦
Dans une application bancaire, supposons que nous avons deux entités :
1⃣ Account (compte bancaire)
2⃣ Transaction (historique des transactions effectuées sur un compte)

Nous allons utiliser @Transaction pour s'assurer qu'une transaction de virement est bien effectuée dans sa totalité (débit et crédit) ou annulée en cas d'erreur.

Définition des entités


@Entity(tableName = "accounts")
data class Account(
@PrimaryKey val accountNumber: String,
var balance: Double
)

@Entity(
tableName = "transactions",
foreignKeys = [
ForeignKey(
entity = Account::class,
parentColumns = ["accountNumber"],
childColumns = ["account_id"],
onDelete = [Link]
)
]
)
data class Transaction(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val account_id: String, // Clé étrangère vers Account
val amount: Double,
val type: String, // "DEBIT" ou "CREDIT"
val date: Long = [Link]()
)
22
Exemple d'utilisation de @Transaction dans un contexte bancaire 🏦
DAO avec @Transaction pour effectuer un virement
@Dao
interface BankDao {
// Récupérer un compte par son numéro
@Query("SELECT * FROM accounts WHERE accountNumber = :accountNumber"
)
fun getAccount(accountNumber: String): Account?
// Mettre à jour le solde du compte
@Update
fun updateAccount(account: Account)
// Insérer une transaction dans l'historique
@Insert
fun insertTransaction(transaction: Transaction)
// Effectuer un virement sécurisé entre deux comptes
@Transaction
fun transferMoney(fromAccountNumber: String, toAccountNumber: String, amount: Double) {
val fromAccount = getAccount(fromAccountNumber) ?: throw Exception("Compte source introuvable")
val toAccount = getAccount(toAccountNumber) ?: throw Exception("Compte destinataire introuvable")
if ([Link] < amount) {
throw Exception("Fonds insuffisants pour effectuer le virement"
)
}
// Débiter l'argent du compte source
[Link] -= amount
updateAccount(fromAccount)
insertTransaction(Transaction(account_id = fromAccountNumber, amount = -amount, type = "DEBIT"))
// Crédite l'argent sur le compte destinataire
[Link] += amount
updateAccount(toAccount)
insertTransaction(Transaction(
account_id = toAccountNumber, amount = amount, type = "CREDIT")) 23
}
}
💡 Pourquoi utiliser @Transaction ?
✅ Garantit que toutes les opérations sont exécutées ensemble.
✅ Si une erreur se produit (ex. fonds insuffisants, compte inexistant), aucune modification n’est appliquée.
✅ Assure l'intégrité des données bancaires.

Exécution dans ViewModel


class BankViewModel(private val bankDao: BankDao) : ViewModel() {

fun transferMoney(fromAccount: String, toAccount: String, amount: Double) {


[Link]([Link]) {
try {
[Link](fromAccount, toAccount, amount)
println("Virement réussi de $amount de $fromAccount vers $toAccount")
} catch (e: Exception) {
println("Échec du virement : ${[Link]}")
}
}
}
}

24
Relation One-to-Many (@Embedded et @Relation)
Problème : Comment récupérer une liste d’éléments liés à un objet ?
Solution : Utilisation de @Embedded et @Relation.

Exemple : Un utilisateur peut avoir plusieurs commandes.

data class UserWithOrders(


@Embedded val user: User,

@Relation(
parentColumn = "id",
entityColumn = "user_id"
)
val orders: List<Order>
)

DAO pour récupérer un utilisateur avec ses commandes :

@Dao
interface UserDao {
@Transaction
@Query("SELECT * FROM users WHERE id = :userId")
fun getUserWithOrders(userId: Int): Flow<UserWithOrders>
}

✅ Les relations sont automatiquement gérées par Room !

25
Relation Many-to-Many avec une table intermédiaire
Problème : Comment gérer une relation Many-to-Many ? 4⃣ Récupérer un étudiant avec ses cours
Solution : Création d’une table intermédiaire.

📌 Exemple : Un étudiant peut être inscrit dans plusieurs cours, et un cours data class StudentWithCourses(
peut avoir plusieurs étudiants.1⃣ Table Student @Embedded val student: Student,

@Entity(tableName = "students") @Relation(


data class Student( parentColumn = "studentId",
@PrimaryKey(autoGenerate = true) val studentId: Int, entityColumn = "courseId",
val name: String associateBy =
)2⃣ Table Course Junction(StudentCourseCrossRef::class)
@Entity (tableName = "courses" ) )
data class Course( val courses: List<Course>
@PrimaryKey (autoGenerate = true) val courseId : Int, )
val title: String
) DAO pour récupérer un étudiant et ses cours :
3⃣ Table intermédiaire StudentCourseCrossRef
@Entity( @Dao
primaryKeys = ["studentId", "courseId"],
interface StudentDao {
foreignKeys = [
@Transaction
ForeignKey(entity = Student::class, parentColumns
= ["studentId"], childColumns = ["studentId"]), @Query("SELECT * FROM students WHERE studentId =
ForeignKey(entity = Course::class, parentColumns :studentId")
= ["courseId"], childColumns = ["courseId"]) fun getStudentWithCourses(studentId: Int):
] Flow<StudentWithCourses>
) }
data class StudentCourseCrossRef(
val studentId: Int, ✅ Room gère automatiquement la relation Many-to-Many !
val courseId: Int
)
26
Requêtes avancées avec JOIN
📌 Exemple : Récupérer tous les étudiants et leurs cours associés avec une requête SQL

@Query("""

SELECT [Link], [Link]

FROM students

INNER JOIN StudentCourseCrossRef ON [Link] = [Link]

INNER JOIN courses ON [Link] = [Link]

""")

fun getStudentsWithCourses(): Flow<List<StudentCourse>>

✅ Utile si l’on veut récupérer les données d’une relation Many-to-Many sous une forme personnalisée.

27
Gestion des transactions avec @Transaction
● Pourquoi ? Pour exécuter plusieurs requêtes dans une seule transaction.

● Exemple : Ajouter un utilisateur et ses commandes en une seule transaction.

📌 DAO avec transaction :

@Dao
interface OrderDao {
@Transaction
suspend fun insertUserWithOrders(user: User, orders: List<Order>) {
insertUser(user)
[Link] { insertOrder(it) }
}

@Insert
suspend fun insertUser(user: User)

@Insert
suspend fun insertOrder(order: Order)
}

✅ Si une opération échoue, tout est annulé !


28
TP

29

Vous aimerez peut-être aussi