Section 9.1, “Transaction Types” introduced optimistic
transactions. In order to prevent one optimistic transaction from
blindly overwriting the changes made by a concurrent transaction,
JDOR versions your objects. The
dictates what form this versioning takes.
version element appears just after the
inheritance element in a mapping document. Only
the least-derived mapped classes in an inheritance hierarchy use the
version element. The element has the following
Kodo does not define any non-standard version strategies, but does allow you to plug in custom strategies of your own. The Reference Guide's Section 7.10, “Custom Mappings” documents version customization in detail.
Kodo also allows you to define multiple lock groups for fine-grained control over optimistic versioning. See Section 5.8, “Lock Groups” in the Reference Guide for more information on lock groups.
As with other elements, the
can be replaced with a nested
As its name implies, the
none version strategy
performs no versioning at all. Using concurrent optimistic
transactions under this strategy is dangerous.
version-number strategy stores a
monotonically increasing version number in the column indicated by
column attribute/element. When a dirty
object's database record is being updated, the JDOR implementation
checks its in-memory version number against the database version
number. If the database version is higher, the implementation
knows that another transaction has modified the object since the
PersistenceManager last read its
state, and it aborts the transaction to preserve data integrity.
If the version number is the same, on the other hand, the
implementation increments the number and proceeds with the flush.
version-number strategy is the fastest,
most efficient, and most accurate version strategy. Its only
drawback is that it requires a dedicated database column. If you
are mapping to legacy tables you might not be able to use it.
date-time strategy is exactly like the
version-number strategy, but it timestamps
each update rather than storing an increasing integer. This
strategy is mainly present to support existing tables that use
time-based versioning. We do not recommend it for new schemas,
since it is theoretically possible for two updates to happen so
close together that they have the same timestamp.
version-number strategy is a
better choice. In addition to being slightly more efficient,
it does not suffer from these sorts of timing failures.
state-comparison version strategy does not
require a database column. It works by comparing the last-read
values of your object with the database on flush. If any values
in the database don't match the recorded last-read value, then
some other transaction must have concurrently modified the data,
and the flush is aborted.
state-image strategy is much
more memory-hungry than other strategies, because the JDOR runtime
has to store the last-read values of all modified fields. It also
results in more complex
UPDATE SQL statements as
the JDOR implementation verifies that no column has been changed by
a concurrent transaction. Finally, it suffers from the following
Only simple, exact field values can be used in state
comparisons. If a commit only changes a
field, subsequent commits will not detect any difference
in the object's version, because these fields are not
compared. Thus it is possible for one transaction to
unknowingly overwrite another.
If two concurrent transactions make changes to fields that
reside in a disjoint set of tables, the second transaction
may overwrite the first. For example, if one transaction
instance by only changing fields mapped to
CNTRCT.SUB, and a concurrent transaction modifies
the same instance but only changes fields mapped to
CNTRCT.TRIAL_SUB, one transaction might
overwrite the other.
Due to these shortcomings, we only recommend using the
state-comparison strategy when performing optimistic
transactions on legacy tables without a version column.
Here is our model with version columns added:
And here is our updated mapping data. We made sure that all
version strategies are represented for illustrative purposes. Note
that as an unmapped
Contract does not declare a version. Its
LineItem, however, each define version
TrialSubscription does not declare
an additional version on its
table, because it joins down to its superclass table. We have
also continued to consolidate our mappings; this document leaves out
default inheritance and discriminator mappings.
Example 15.10. Version Mapping
<?xml version="1.0"?> <orm> <package name="org.mag"> <sequence name="ArticleSeq" datastore-sequence="ART_SEQ"/> <class name="Magazine" table="MAG"> <version column="VERS" strategy="version-number"/> <field name="isbn"> <column name="ISBN" jdbc-type="char" length="15"/> </field> <field name="title" column="TITLE"/> ... </class> <class name="Article" table="ART"> <version column="UPD" strategy="date-time"/> <field name="id" column="ID"/> ... </class> </package> <package name="org.mag.pub"> <sequence name="AuthorSeq" factory-class="Author$SequenceFactory"/> <class name="Company" table="COMP"> <datastore-identity column="CID" strategy="autoassign"/> <version strategy="none"/> ... </class> <class name="Author" table="AUTH"> <datastore-identity sequence="AuthorSeq"> <column name="AID" sql-type="INTEGER64"/> </datastore-identity> <inheritance> <discriminator column="CLS" strategy="class-name"/> </inheritance> <version strategy="state-comparison"/> ... </class> </package> <package name="org.mag.subscribe"> <sequence name="ContractSeq" strategy="transactional"/> <class name="Contract"> <inheritance strategy="subclass-table"/> ... </class> <class name="Subscription" table="CNTRCT.SUB"> <inheritance> <discriminator value="1"> <column name="TYPE" jdbc-type="tinyint"/> </discriminator> </inheritance> <version column="VERS" strategy="version-number"/> <field name="Contract.id" column="ID"/> ... </class> <class name="LifetimeSubscription"> <inheritance> <discriminator value="2"/> </inheritance> ... </class> <class name="TrialSubscription" table="CNTRCT.TRIAL_SUB"> <inheritance strategy="new-table"> <join column="ID"/> <discriminator value="3"/> </inheritance> ... </class> <class name="Subscription$LineItem" table="CNTRCT.LINE_ITEM"> <version column="VERS" strategy="version-number"/> <field name="Contract.id" column="ID"/> ... </class> </package> </orm>