pub struct Transaction<'p, DB> { /* private fields */ }Expand description
Transaction builder for composing multi-entity operations.
Use Transaction::new to create a builder, chain .with_*() methods
to declare which entities you’ll use, then call .run() to execute.
§Type Parameters
'p— Pool lifetimeDB— Database pool type (e.g.,PgPool)
§Example
Transaction::new(&pool)
.run(async |ctx| {
let user = ctx.users().find_by_id(id).await?;
ctx.orders().create(order).await?;
Ok(())
})
.await?;Implementations§
Source§impl<'p, DB> Transaction<'p, DB>
impl<'p, DB> Transaction<'p, DB>
Source§impl Transaction<'_, PgPool>
impl Transaction<'_, PgPool>
Sourcepub async fn run<F, T, E>(self, f: F) -> Result<T, E>
pub async fn run<F, T, E>(self, f: F) -> Result<T, E>
Execute a closure within a PostgreSQL transaction.
Commits the transaction explicitly when the closure returns Ok.
On Err, the transaction context is dropped and sqlx rolls back
automatically via its Drop implementation.
The closure receives &mut TransactionContext (not by value) so that
run retains ownership and can invoke commit().await on success.
§Type Parameters
F— Async closureT— Success typeE— Error type (must be convertible fromsqlx::Error)
§Example
Transaction::new(&pool)
.run(async |ctx| {
let user = ctx.users().create(dto).await?;
Ok(user)
})
.await?;§Errors
Propagates any error from the closure, from begin, or from commit.
Sourcepub async fn run_with_commit<F, Fut, T, E>(self, f: F) -> Result<T, E>
pub async fn run_with_commit<F, Fut, T, E>(self, f: F) -> Result<T, E>
Execute a closure within a transaction with explicit commit.
§⚠️ The closure MUST call ctx.commit().await on every successful path
run_with_commit hands ownership of TransactionContext to the
closure. There is no type-level guarantee that the closure commits;
if it returns Ok(...) without calling
commit, ctx is dropped and the
underlying sqlx::Transaction::Drop silently rolls back. The
caller observes Ok and assumes the writes persisted — they did
not. This is the same failure mode that affected the old run()
implementation; here it is preserved on purpose so callers can
implement conditional commit logic, at the cost of moving the
responsibility onto the closure.
Prefer run unless you genuinely need to decide
commit-or-rollback inside the closure. run performs the commit
automatically on Ok and rolls back on Err, eliminating this
footgun.
§Examples
Correct — closure commits on the success path:
Transaction::new(&pool)
.run_with_commit(|mut ctx| async move {
let user = ctx.users().create(dto).await?;
ctx.commit().await?; // <-- required
Ok(user)
})
.await?;Wrong — closure returns Ok without committing; the write is
rolled back when ctx drops, but the caller sees Ok(user):
Transaction::new(&pool)
.run_with_commit(|mut ctx| async move {
let user = ctx.users().create(dto).await?;
// BUG: forgot `ctx.commit().await?` — the row is rolled back.
Ok(user)
})
.await?;Conditional commit — the intended use case:
Transaction::new(&pool)
.run_with_commit(|mut ctx| async move {
let user = ctx.users().create(dto).await?;
if user.flagged {
ctx.rollback().await?;
return Ok(None);
}
ctx.commit().await?;
Ok(Some(user))
})
.await?;§Errors
Propagates any error from the closure or database transaction.
Auto Trait Implementations§
impl<'p, DB> Freeze for Transaction<'p, DB>
impl<'p, DB> RefUnwindSafe for Transaction<'p, DB>where
DB: RefUnwindSafe,
impl<'p, DB> Send for Transaction<'p, DB>where
DB: Sync,
impl<'p, DB> Sync for Transaction<'p, DB>where
DB: Sync,
impl<'p, DB> Unpin for Transaction<'p, DB>
impl<'p, DB> UnsafeUnpin for Transaction<'p, DB>
impl<'p, DB> UnwindSafe for Transaction<'p, DB>where
DB: RefUnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more