Transactions can be complex to understand, this page will show you which parts of the code will process objects and at which stage. This can be useful to extend the current behaviour, improve your transactions performance or simply better understand the code.
We will follow the code flow at the point where the
IOWriteHandlerInterface is called (either via
a utility class like IOHelper
, DAO
or via your own custom code (custom DAO, etc).
ObjectIOStatement
s defined for the object's application and for each column of this application during the mapping phase (typically on startup).
doWrite
method of its
ObjectStorageWriter
(which defaults to an instance of
TransactionObjectStorageWriter)
doWrite
method of the TransactionObjectStorageWriter simply adds a
WriteStatement
to the current GenericTransaction<ObjectStoreStatement>
started by step 2.
nest()
call or if the nested transaction is running at a different
isolation level), all the WriteStatement
s that were added to it are executed.
WriteStatement
's execute method then starts a GenericTransactionManager for
StorageStatements.
(note: this is different from the manager which manages ObjectStoreStatement
s).
It also creates a trigger on the current ObjectIOStatement Transaction so that at the end of a successful commit of that transaction,
the newly created StorageStatement Transaction is itself committed.
doWriteTransaction
method on the TransactionObjectStorageWriter. (see
AbstractWriteImpl)
doWriteTransaction
method will then delegates to the actual storage engine's to obtain a StorageStatement.
getWriter
will return a
DBUpdate
Transaction
of StorageStatement
s.
StorageStatement
s,
the commit end of the ObjectStoreStatement will call execute
on each StorageStatement
.
Now to give a more concrete example (using just the default options and a standard database backend),
we will assume a very simple case of 2 objects A and B, where B is nested (and cascading) in A:
@Table("Table_A")
public class A {
@Sequence @PrimaryKey
public int key = 0;
@Cascade
public B b;
}
@Table("Table_B")
public class B {
@Sequence @PrimaryKey
public int key = 0;
public String name;
}
We now simply call IOHelper.save(new A());
(after mapping the object to a schema using DBDataMapper.map(A.class)
)
ObjectWriteHandler
creates an ObjectIOStatementImpl
to represent this IO operation requestTransactionObjectWriter
starts a GenericTransaction
for ObjectStoreStatement
s. to collect the store statementsGenericTransactionHandler
calls the ObjectIOStatementImpl
's execute method in this new transaction context.ObjectIOStatementImpl
's execute method calls:
A.b
).doWrite
method of the TransactionObjectStorageWriter
doWrite()
method of the TransactionObjectStorageWriter
creates a WriteStatement
and adds it to the ObjectStoreStatement
transaction.commit()
method of the GenericTransaction
executes each WriteStatement
in turn, it contains:
WriteStatement(B)
WriteStatement(A)
WriteStament(B)
creates a new GenericTransaction
for StorageStatement
s.
Then it calls doWriteTransaction
on the TransactionObjectStorageWriter
.
WriteStament(B)
is executed, the StorageStatement transaction exists and it just calls doWriteTransaction
.
doWriteTransaction
for B generates a DBInsert("Table_B")
.doWriteTransaction
for A generates a DBInsert("Table_A")
.
commit()
has successfully processed all the StorageStatement
s, the
COMMIT_END
trigger calls commit()
on the StorageStatement
transaction, which calls
execute()
on each DBInsert
statement it contains (DBInsert("Table_B")
and DBInsert("Table_A")
).
DBInsert
statements to connect to the database and execute the appropriate SQL statements.
Note: in this example, the database lock is only held during part of step 9!
Between the time the first database statement requests a connection and the sucessful commit end of the StorageStatement
transaction.
The database connection is automatically commited (or rolled back if needed) using another COMMIT_END
(or COMMIT_FAIL
) trigger on the StorageStatement
transaction.