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.