Transaccions
Introducció
Section titled “Introducció”Transaccions
Section titled “Transaccions”Si vols executar més d’una sentència dins una transacció has d’utlitzar la funció transaction()
:
db.transaction { val eva = db.ownerQueries.insert("Eva").executeAsOne() val milú = db.dogQueries.insert("Milú", null, null, eva.id).executeAsOne() println(milú)}
El resultat és:
Dog(id=7, name=Milú, number=null, breed=null, owner=3)
Per tornar el valor des de una transacció, utilitza la funció transactionWithResult
:
val dogs = db.transactionWithResult { db.dogQueries.select(5,0).executeAsList()}println(dogs)
Rollback
Section titled “Rollback”Si durant l’execució del codi es produeix una execpció, automàticamen es farà un “rollback” de la transació.
try { db.transaction { db.dogQueries.insert("Kokoro", null, null, null) throw Exception("Ups!") } } catch (e: Exception) { println("No hi a gossos: ${db.dogQueries.select(5, 0).executeAsList().isEmpty()}") }
Si vols pots fer un “rollback” de la transacció en qualsevol moment (no es produeix cap excepció):
db.transaction { db.dogQueries.insert("Kokoro", null, null, null) rollback()}println("No hi a gossos: ${db.dogQueries.select(5, 0).executeAsList().isEmpty()}"
Però si la transacció retorna un valor, has de retornar un valor amb el rollback:
val dogs: Dog? = db.transactionWithResult { db.dogQueries.insert("Kokoro", null, null, null) rollback(null)}
Callbacks
Section titled “Callbacks”Pots registrar “callbacks” que s’executarn un con la transacció ha finalitzar o s’ha fet un “rollback”:
val dog: Dog? = db.transactionWithResult {
afterRollback { println("No s'ha afegit Kokoro") } afterCommit { println("S'ha afegit Kokoro") }
db.dogQueries.insert("Kokoro", null, null, null) rollback(null)}
Grouping Statements
Section titled “Grouping Statements”TODO
You can group multiple SQL statements together to be executed at once inside a transaction:
https://sqldelight.github.io/sqldelight/2.0.2/jvm_sqlite/types/
Cursor
Section titled “Cursor”Però si la llista és molt llarga, i vols processar molts elements de la llista és millor utilitzar un cursor:
val query = db.dogQueries.select(Long.MAX_VALUE,0)query.execute { cursor -> while (cursor.next().value) { val dog = query.mapper(cursor) println(dog) } QueryResult.Unit}
La manera més fàcil d’accedir a les dades, és mitjançant la funció mapper
que forma part de las classe Query<Dog>
, i que transforma el registre al que apunta el cursor
a un objecte Dog
.
Però si vols, pots accedir als elements de registre de manera posicional:
val query = db.dogQueries.select()query.execute { cursor -> while (cursor.next().value) { println(cursor.getString(1)) } QueryResult.Unit}
No tens seguretat a nivell de tipus, però és molt més eficient si no necessites crear l’objecte!
I el que es evident, no cal que recorris tots els elements de la llista!
// Consultem la taula gossos amb màxim 2 resultatsval query = db.dogQueries.select(Long.MAX_VALUE,0)query.execute { cursor -> while (cursor.next().value) { val dog = query.mapper(cursor) println(dog) if (dog.name == "Ketzu") { break } } QueryResult.Unit}
Listener
Section titled “Listener”Quan estas processant un select
en un entorn concurrent és possible que s’afegeixin o es borrin registres.
Pots registrar un “listener”:
val query = db.dogQueries.select(Long.MAX_VALUE,0);query.addListener { app.cash.sqldelight.Query.Listener { println("queryResultsChanged") }}
però només té sentint en un entorn concurrent …
TODO primer l’alumne ha de saber com s’executa en entorn concurrent.
Activitat
Section titled “Activitat”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
{% sol %} 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 (?,?, ?);
{% endsol %}
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) }}
Consultar dades
Section titled “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
.
@Composablefun TeamView(team: Team) {
// ...}
@Composablefun 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:
@Composablefun 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
Section titled “Afegir dades”A continuació has de crear una “Screen” per insertar un nou equip a la base de dades
{% image “studio-structure.png” %}
1.- Completa la funció “composable” TeamInsert
per afegir un equip nou.
Has d’afegir un TextField
per location
!
@Composablefun 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@Composablefun TeamInsertPreview() { TeamInsert { println(it) }}
2.- Crea la funció “composable” TeamInsertScreen
.
La funció ha d’insertar el registre a la base de dades.
@Composablefun TeamInsertScreen(database: Database) { TeamInsert { team -> database.teamQueries.insert(team.name,team.location) }}
3.- Verifica que pots insertar equips a la base de dades.
{% image “insert-team.png” %}
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.
{% image “team-constraint-unique.png” %}
{% sol %}
@Composablefun 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@Composablefun TeamInsertPreview() { // Bournemouth, Brentford TeamInsert { "Error" }}
@Composablefun TeamInsertScreen(database: Database) {
TeamInsert { team -> try { database.teamQueries.insert(team.name, team.location) "Fet!" } catch (e: Exception) { e.message ?: "Error al insertar el registre" } }}
{% endsol %}
El contingut d'aquest lloc web té llicència CC BY-NC-ND 4.0.
©2022-2025 xtec.dev