15.10. Version
Prev | Chapter 15. Mapping Metadata | Next

## 15.10. Version

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 `version` element dictates what form this versioning takes.

The `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 attributes:

- `strategy`: The version strategy. We review the standard JDO strategies in the following sections. Vendors may define additional non-standard strategies.

  | Note
  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.

- `column`: The column that holds the version value. Not all version strategies require a column.

  As with other elements, the `column` attribute can be replaced with a nested `column` element.

### 15.10.1. none

As its name implies, the `none` version strategy performs no versioning at all. Using concurrent optimistic transactions under this strategy is dangerous.

### 15.10.2. version-number

The `version-number` strategy stores a monotonically increasing version number in the column indicated by the `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 current `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.

The `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.

### 15.10.3. date-time

The `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. The `version-number` strategy is a better choice. In addition to being slightly more efficient, it does not suffer from these sorts of timing failures.

### 15.10.4. state-comparison

The `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.

Unfortunately, the `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 limitations:

- Only simple, exact field values can be used in state comparisons. If a commit only changes a `float` or `Collection` 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 modifies a `TrialSubscription` 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.

### 15.10.5. Putting it All Together

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 `subclass-table` type, `Contract` does not declare a version. Its direct subclasses `Subscription` and `LineItem`, however, each define version mappings. `TrialSubscription` does not declare an additional version on its `CNTRCT.TRIAL_SUB` 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>

Prev | Up | Next
15.9. Discriminator | Home | 15.11. Field Mapping