x::sql::transaction
x::sql::transaction
inseparably glues an SQL transaction with an application scope:
void move_money(const x::sql::connection &conn, int from_account, int to_account, double amount) { x::sql::transaction tran(conn); auto statement=conn->prepare("INSERT INTO ledger(account_id, amount) VALUES(?, ?)"); conn->execute(from_account, -amount); conn->execute(to_account, amount); tran.commit_work(); }
Instantiate an
x::sql::transaction
on a thread's stack.
x::sql::transaction
's constructor calls
begin_work
().
x::sql::transaction
's
commit_work
() and
rollback_work
() must be invoked instead of the
connection object's. If the
x::sql::transaction
goes out of scope and
gets destroyed for any reason, including an exception, without having its
commit_work
() or
rollback_work
() explicitly called,
its destructor invokes
rollback_work
() automatically.
Using an
x::sql::transaction
results in an automatic
transaction rollback when an uncaught exception gets thrown within the
transaction's scope.
The most common approach is to have
x::sql::transaction
's
commit_work
() invoked explicitly,
just before the object
goes out of scope and gets destroyed; or before leaving via some other
exit path. The destructor automatically invokes
rollback_work
() when the execution thread
leaves the scope via an exception, or any other means. It's also possible
to invoke rollback_work
() explicitly.
The only advantage to an explicit
rollback_work
() is that a database error in the
rollback throws an explicit exception. All exceptions are caught, logged,
and discarded when
rollback_work
() gets invoked from the
destructor.
However, throwing an explicit exception from
rollback_work
() is just a small consolation.
Since the database connection's state is no longer deterministic, any
database error occuring in
commit_work
() or
rollback_work
() will automatically close the
connection, making it no longer usable.