item1.foo = 'bar'
item2.bar = 'foo'
+.. _session_begin_nested:
+
Using SAVEPOINT
---------------
reference the full state of the :class:`~sqlalchemy.orm.session.Session` right
before :func:`~sqlalchemy.orm.session.Session.begin_nested` was called.
+.. _session_subtransactions:
+
+Using Subtransactions
+---------------------
+
+A subtransaction, as offered by the ``subtransactions=True`` flag of :meth:`.Session.begin`,
+is a non-transactional, delimiting construct that
+allows nesting of calls to :meth:`~.Session.begin` and :meth:`~.Session.commit`.
+It's purpose is to allow the construction of code that can function within a transaction
+both independently of any external code that starts a transaction,
+as well as within a block that has already demarcated a transaction. By "non-transactional", we
+mean that no actual transactional dialogue with the database is generated by this flag beyond that of
+a single call to :meth:`~.Session.begin`, regardless of how many times the method
+is called within a transaction.
+
+The subtransaction feature is in fact intrinsic to any call to :meth:`~.Session.flush`, which uses
+it internally to ensure that the series of flush steps are enclosed within a transaction,
+regardless of the setting of ``autocommit`` or the presence of an existing transactional context.
+However, explicit usage of the ``subtransactions=True`` flag is generally only useful with an
+application that uses the
+:class:`.Session` in "autocommit=True" mode, and calls :meth:`~.Session.begin` explicitly
+in order to demarcate transactions. For this reason the subtransaction feature is not
+commonly used in an explicit way, except for apps that integrate SQLAlchemy-level transaction control with
+the transaction control of another library or subsystem. For true, general purpose "nested"
+transactions, where a rollback affects only a portion of the work which has proceeded,
+savepoints should be used, documented in :ref:`session_begin_nested`.
+
+The feature is the ORM equivalent to the pattern described at :ref:`connections_nested_transactions`,
+where any number of functions can call :meth:`.Connection.begin` and :meth:`.Transaction.commit`
+as though they are the initiator of the transaction, but in fact may be participating
+in an already ongoing transaction.
+
+As is the case with the non-ORM :class:`.Transaction` object,
+calling :meth:`.Session.rollback` rolls back the **entire**
+transaction, which was initiated by the first call to
+:meth:`.Session.begin` (whether this call was explicit by the
+end user, or implicit in an ``autocommit=False`` scenario).
+However, the :class:`.Session` still considers itself to be in a
+"partially rolled back" state until :meth:`.Session.rollback` is
+called explicitly for each call that was made to
+:meth:`.Session.begin`, where "partially rolled back" means that
+no further SQL operations can proceed until each level
+of the transaction has been acounted for, unless the :meth:`~.Session.close` method
+is called which cancels all transactional markers. For a full exposition on
+the rationale for this,
+please see `But why isn't the one automatic call to ROLLBACK
+enough ? Why must I ROLLBACK again?
+<http://www.sqlalchemy.org/trac/wiki/FAQ#ButwhyisnttheoneautomaticcalltoROLLBACKenoughWhymustIROLLBACKagain>`_.
+In short, if subtransactions are used as intended, that is, as a means to nest multiple
+begin/commit pairs, the appropriate rollback calls naturally occur in any case.
+
+An example of ``subtransactions=True`` is nearly identical to that of the non-ORM method. The nesting
+of transactions, as well as the natural calling of "rollback" for all transactions, is illustrated::
+
+ # method_a starts a transaction and calls method_b
+ def method_a(session):
+ session.begin(subtransactions=True) # open a transaction. If there was
+ # no previous call to begin(), this will
+ # be a real transaction.
+ try:
+ method_b(session)
+ session.commit() # transaction is committed here
+ except:
+ session.rollback() # rolls back the transaction
+ raise
+
+ # method_b also starts a transaction
+ def method_b(connection):
+ session.begin(subtransactions=True) # open a transaction - this
+ # runs in the context of method_a()'s
+ # transaction
+ try:
+ session.add(SomeObject('bat', 'lala'))
+ session.commit() # transaction is not committed yet
+ except:
+ session.rollback() # rolls back the transaction, in this case
+ # the one that was initiated in method_a().
+ raise
+
+ # create a Session and call method_a
+ session = Session(autocommit=True)
+ method_a(session)
+ session.close()
+
+Since the :meth:`.Session.flush` method uses a subtransaction, a failed flush
+will always issue a rollback which then affects the state of the outermost transaction (unless a SAVEPOINT
+is in use). This forces the need to issue :meth:`~.Session.rollback` for the full operation
+before subsequent SQL operations can proceed.
+
Enabling Two-Phase Commit
-------------------------
transaction or nested transaction, an error is raised, unless
``subtransactions=True`` or ``nested=True`` is specified.
- The ``subtransactions=True`` flag indicates that this ``begin()`` can
- create a subtransaction if a transaction is already in progress. A
- subtransaction is a non-transactional, delimiting construct that
- allows matching begin()/commit() pairs to be nested together, with
- only the outermost begin/commit pair actually affecting transactional
- state. When a rollback is issued, the subtransaction will directly
- roll back the innermost real transaction, however each subtransaction
- still must be explicitly rolled back to maintain proper stacking of
- subtransactions.
-
- If no transaction is in progress, then a real transaction is begun.
-
+ The ``subtransactions=True`` flag indicates that this :meth:`~.Session.begin`
+ can create a subtransaction if a transaction is already in progress.
+ For documentation on subtransactions, please see :ref:`session_subtransactions`.
+
The ``nested`` flag begins a SAVEPOINT transaction and is equivalent
- to calling ``begin_nested()``.
+ to calling :meth:`~.Session.begin_nested`. For documentation on SAVEPOINT
+ transactions, please see :ref:`session_begin_nested`.
"""
if self.transaction is not None:
The target database(s) must support SQL SAVEPOINTs or a
SQLAlchemy-supported vendor implementation of the idea.
- The nested transaction is a real transation, unlike a "subtransaction"
- which corresponds to multiple ``begin()`` calls. The next
- ``rollback()`` or ``commit()`` call will operate upon this nested
- transaction.
+ For documentation on SAVEPOINT
+ transactions, please see :ref:`session_begin_nested`.
"""
return self.begin(nested=True)