La base de dades que utilitza Android per defecte és Sqlite.
Introducció
Projecte de suport: https://gitlab.com/xtec/android/Sqlite
Entorn de treball
Instal.la Android Studio:
> scoop install android-studio android-clt
Crea un projecte tal com s'explica a ...
Per projectes Android, el plugin SQLDelight Gradle selecciona de manera automàtica la versió del dialecte SQLite en base a la configuració minSdkVersion
del teu projecte.
Mira en aquesta llista quina versió de Sqlite porta per defecte cada nivell del Android SDK.
build.gradle.kts
Primer, aplica el plugin gradle al fitxer app/build.gradle.kts
file:
plugins {
...
id("app.cash.sqldelight") version "2.0.2"
}
dependencies {
...
implementation("app.cash.sqldelight:android-driver:2.0.2")
}
Sincronitza la configuració per tal que es descarreguin les dependències.
Ja pots configurar la base de dades en el fitxer app/build.gradle.kts
.
sqldelight {
databases {
create("Database") { // The name of the database
packageName.set("dev.xtec.data") // Package name used for the database class.
// Additional configuration options if required
// https://sqldelight.github.io/sqldelight/2.0.2/android_sqlite/gradle/
}
}
}
Aplicació
SQL
Crea els fitxers sq
corresponents a aquest model de dades en el directori app/src/main/sqldelight/...
:
classDiagram direction LR class Team { id integer primary key autoincrement name text not null unique location text not null } class Player { id integer primary key autoincrement name text not null country text not null } Player --> Team : team
Team.sq
CREATE TABLE IF NOT EXISTS Team (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
location TEXT NOT NULL
);
insert:
INSERT INTO Team(name,location) VALUES(?,?);
select:
SELECT * FROM Team LIMIT :limit OFFSET :offset;
selectCount:
SELECT count(*) FROM Team;
Player.sq
CREATE TABLE IF NOT EXISTS Player (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
country TEXT NOT NULL,
team INTEGER NOT NULL,
FOREIGN KEY (team) REFERENCES Team(id)
);
insert:
INSERT INTO Player(name,country,team) VALUES (?,?, ?);
Si no es genera de manera automàtica el codi corresponent, executa la tasca generateSqlInterface
des del terminal.
> .\gradlew generateSqlInterface
Base de dades
Modifica el contingut del fitxer MainActivity.kt
:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val databaseName = "soccer"
val production = false
val sqlDriver: SqlDriver =
AndroidSqliteDriver(Database.Schema, this.applicationContext, databaseName)
val database = Database(sqlDriver)
if (production) {
Database.Schema.create(sqlDriver)
} else {
this.applicationContext.deleteDatabase(databaseName)
Database.Schema.create(sqlDriver)
insertDevelopmentData(database)
}
val teams = database.teamQueries.select(Long.MAX_VALUE, 0).executeAsList()
setContent {
Text("TODO")
}
}
}
fun insertDevelopmentData(database: Database) {
listOf(
Team(1, "Arsenal", "London"),
Team(2, "Aston Villa", "Birmingham")
).forEach() { team ->
database.teamQueries.insert(team.name, team.location)
}
listOf(
Player(1, "David Raya", "Spain", 1),
Player(2, "William Saliba", "France", 1),
Player(3, "Kieran Tiernay", "Scotland", 1)
).forEach { player ->
database.playerQueries.insert(player.name, player.country, player.team)
}
}
Executa l'aplicació en un dispositiu.
Obre la base de dades en el Database Inspector:
- Des de la barra del menú, selecciona View > Tool Windows > App Inspection.
- Selecciona el Database Inspector tab.
- Les bases de dades de l'aplicació que s'està executant apareixen en el panell Databases
- Expandeix els nodes de la base de dades
soccer
.
Fes doble-clic a Player
per veure tots els jugadors:
Recorda que la base de dades està en el dispositiu, no en el teu Windows!
Tens més informació en aquest enllaç: Debug your database with the Database Inspector
Consultar dades
A continuació has de crear una "Screen" per mostrar tots els equips que estan a la base de dades.
1.- Crea les funcions "composables" TeamView
i TeamViewList
.
@Composable
fun TeamView(team: Team) {
// ...
}
@Composable
fun TeamListView(teams: List<Team>) {
// ...
}
2.- Modifica el setContent
de la classe MainActivity
:
class MainActivity : ComponentActivity() {
// ...
val teams = db.teamQueries.select(Long.MAX_VALUE, 0).executeAsList()
setContent {
TeamListView(teams)
}
}
3.- Crea la funció "composable" TeamListScreen
que té la base de dades com a paràmetre:
@Composable
fun TeamListScreen(database: Database) {
val teams = database.teamQueries.select(Long.MAX_VALUE, 0).executeAsList()
TeamListView(teams)
}
4.- Modifica el setContent
de la classe MainActivity
:
class MainActivity : ComponentActivity() {
// ...
setContent {
TeamListScreen(database)
}
}
Afegir dades
A continuació has de crear una "Screen" per insertar un nou equip a la base de dades
1.- Completa la funció "composable" TeamInsert
per afegir un equip nou.
Has d'afegir un TextField
per location
!
@Composable
fun TeamInsert(handle: (Team) -> Unit) {
val team = remember { mutableStateOf(Team(0,"","")) }
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxSize()
.padding(10.dp)
) {
Text(
text = "Team",
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.height(10.dp))
TextField(
value = team.value.name,
placeholder = { Text("name") },
onValueChange = { team.value = team.value.copy(name = it) })
Button(
onClick = {
message.value = handle(team.value)
},
modifier = Modifier.padding(vertical = 40.dp)
) {
Text("Insert")
}
}
}
@Preview
@Composable
fun TeamInsertPreview() {
TeamInsert {
println(it)
}
}
2.- Crea la funció "composable" TeamInsertScreen
.
La funció ha d'insertar el registre a la base de dades.
@Composable
fun TeamInsertScreen(database: Database) {
TeamInsert { team ->
database.teamQueries.insert(team.name,team.location)
}
}
3.- Verifica que pots insertar equips a la base de dades.
4.- Si insertes un equip amb un nom que ja existeix l'aplicació fa "crash".
El motiu és que el nom de l'equip ha de ser únic en la taula Team
.
Modifica les funcion TeamInsert
i TeamInsertScreen
perqué gestioni l'excepció, i mostri a l'usuari un missatge d'error dient que l'equip ja existeix.
@Composable
fun TeamInsert(handle: (Team) -> String) {
val team = remember { mutableStateOf(Team(0, "", "")) }
val message = remember { mutableStateOf("") }
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxSize()
.padding(10.dp)
) {
Text(
text = "Team",
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.height(10.dp))
TextField(
value = team.value.name,
placeholder = { Text("name") },
onValueChange = { team.value = team.value.copy(name = it) })
Spacer(modifier = Modifier.height(10.dp))
TextField(
value = team.value.location,
placeholder = { Text("location") },
onValueChange = { team.value = team.value.copy(location = it) })
Button(
onClick = {
message.value = handle(team.value)
},
modifier = Modifier.padding(vertical = 40.dp)
) {
Text("Insert")
}
Text(
text = message.value,
modifier = Modifier.padding(10.dp),
color = MaterialTheme.colorScheme.error
)
}
}
@Preview
@Composable
fun TeamInsertPreview() {
// Bournemouth, Brentford
TeamInsert {
"Error"
}
}
@Composable
fun TeamInsertScreen(database: Database) {
TeamInsert { team ->
try {
database.teamQueries.insert(team.name, team.location)
"Fet!"
} catch (e: Exception) {
e.message ?: "Error al insertar el registre"
}
}
}