Transactions
A transaction is a sequence of one or more database operations that are treated as a single unit of work. Transactions in a database are used to ensure data integrity, consistency, and reliability when multiple operations need to be executed as a single, atomic unit.
In rorm, a transaction can be retrieved from a Database
instance:
use std::error::Error;
use rorm::prelude::*;
async fn transaction(db: &Database) -> Result<Transaction, Box<dyn Error>> {
Ok(db.start_transaction().await?)
}
The resulting Transaction
can be used in place of the Database
instance,
with the difference that the instance must be provided as mutable reference.
There are two possible options to end a transaction:
commit()
This will end the transaction and apply all modifications to the databaserollback()
This will end the transaction and rollback all modifications
Tip
Rollback is default that gets executed if a Transaction
is dropped.
Example usage
use std::fs::File;
use std::io::Write;
use rorm::prelude::*;
#[derive(Model)]
pub struct User {
#[rorm(id)]
pub id: i64,
#[rorm(unique, max_length = 255)]
pub username: String,
}
#[derive(Patch)]
#[rorm(model = "User")]
pub struct UserInsert {
pub username: String,
}
pub async fn create_user(
db: &Database,
username: String,
profile_picture: &[u8]
) -> Result<(), Box<dyn std::error::Error>> {
// Start a transaction
let mut tx = db.start_transaction().await?;
// Check if the username is already taken
let is_taken = query!(&mut tx, (User::F.id,))
.condition(User::F.username.equals(&username))
.optional()
.await?
.is_some();
if is_taken {
return Err("Username is already taken".to_string());
}
// Insert the new user
let id = insert!(&mut tx, UserInsert)
.return_primary_key()
.single(&UserInsert { username })
.await
.unwrap();
// If any of the io tasks throw an error, the transaction is dropped when the
// function exits, and a rollback is performed.
// So the user wouldn't be created in the database in this case
let mut file = File::create(format!("profile_pictures/{id}.png"))?;
file.write_all(profile_picture)?;
// Commit the transaction
tx.commit().await?;
Ok(())
}