14.6. Lock Groups

14.6.1. Lock Groups and Subclasses
14.6.2. Lock Group Mapping

The JDO specification provides support for both optimistic and pessimistic transaction types. Typically, optimistic locking is performed at the object level of granularity. That is, concurrent changes in different transactions to any part of a common object will result in an optimistic locking exception being thrown in the transaction that commits last. In many applications, this is acceptable. However, if your application has a high likelihood of concurrent writes to different parts of the same object, then it may be advantageous to use a finer-grained optimistic lock. Additionally, certain parts of an object model may be best modelled without any locking at all, or with a last-commit-wins strategy. It is for these types of situations that Kodo offers customizable optimistic lock groups, which allow you to achieve sub-object-level locking granularity.

For example, an Employee class may have some fields configurable by the employee that the object represents (firstName, lastName, phoneNumber), some that are only modifiable by that employee's manager (salary, title), and some in which concurrent updates are acceptable (a list of projects). In such a model, you can greatly improve the success of concurrent updates in optimistic transactions by putting firstName, lastName, and phoneNumber into one lock group, salary and title into another, and excluding the projects field from optimistic lock checks altogether:

Example 14.22. Lock Group Meta-Data

public class Employee
{
    // these fields are configurable by this employee
    private String               firstName;
    private String               lastName;
    private String               phoneNumber;

    // these fields can only be set by the employee's manager
    private float                salary;
    private String               title;

    // this field might be updated concurrently by the employee,
    // other team members, or the employee's manager
    private Set                  projects;
}


<?xml version="1.0"?>
<jdo>
  <package name="">
    <class name="Employee">
      <!-- these fields are implicitly left in the default lock group -->
      <field name="firstName"/>
      <field name="lastName"/>

      <!-- this field is explicitly put into the default lock group -->
      <field name="phoneNumber">
        <extension vendor-name="kodo" key="lock-group" value="default"/>
      </field>


      <!-- these are explicitly put into a non-default lock group -->
      <field name="salary">
        <extension vendor-name="kodo" key="lock-group" value="corporate"/>
      </field>
      <field name="title">
        <extension vendor-name="kodo" key="lock-group" value="corporate"/>
      </field>


      <!-- concurrent updates to this field are ok, so it's 
        excluded from locking -->
      <field name="projects">
        <collection element-type="Project"/>
        <extension vendor-name="kodo" key="lock-group" value="none"/>
      </field>
    </class>
  </package>
</jdo>

Currently, lock groups are only supported when using a relational database, either through a direct connection to the database or through a remote persistence manager. Additionally, lock groups are only currently available when using the version-number or version-date version indicators (that is, lock groups are not supported by the state-image version indicator). Additionally, lock groups should transparently work with any custom extension of kodo.jdbc.meta.ColumnVersionIndicator.

14.6.1. Lock Groups and Subclasses

Custom lock groups are a bit limited when it comes to subclass fields. Because of the way that lock groups are parsed, subclasses cannot simply declare additional lock groups implicitly, as is done in the example shown above. Instead, the least-derived type that the subclass inherits from must list all lock groups that its children can use via the class-level lock-groups metadata extension. For example, if the Employee class in the last example extended Person, the metadata would have looked like so:

Example 14.23. Lock Group Meta-Data

public class Person
{
    // these fields are configurable by this person
    private String               firstName;
    private String               lastName;
    private String               phoneNumber;
}


public class Employee
    extends Person
{
    // these fields can only be set by the employee's manager
    private float                salary;
    private String               title;

    // this field might be updated concurrently by the employee,
    // other team members, or the employee's manager
    private Set                  projects;
}


<?xml version="1.0"?>
<jdo>
  <package name="">
    <class name="Person">
      <!-- here we list the lock groups that will be used by Employee. -->
      <extension vendor-name="kodo" key="lock-groups" value="corporate"/>

      <!-- these fields are implicitly left in the default lock group -->
      <field name="firstName"/>
      <field name="lastName"/>

      <!-- this field is explicitly put into the default lock group -->
      <field name="phoneNumber">
        <extension vendor-name="kodo" key="lock-group" value="default"/>
      </field>
    </class>

    <class name="Employee">
      <!-- these are explicitly put into a non-default lock group -->
      <field name="salary">
        <extension vendor-name="kodo" key="lock-group" value="corporate"/>
      </field>
      <field name="title">
        <extension vendor-name="kodo" key="lock-group" value="corporate"/>
      </field>

      <!-- concurrent updates to this field are ok, so it's 
        excluded from locking -->
      <field name="projects">
        <collection element-type="Project"/>
        <extension vendor-name="kodo" key="lock-group" value="none"/>
      </field>
    </class>
  </package>
</jdo>

The exceptions to this rule are the none and default built-in lock groups. They can be used at any point in the inheritance hierarchy without pre-declaration. Additionally, the lock-groups listing can contain lock groups that would otherwise be implicitly defined in the least-derived type metadata.

14.6.2. Lock Group Mapping

When using the custom lock groups with a relational database, Kodo will need a lock column for each of the groups, instead of just one lock column. (Of course, no lock column will be needed for the special none group.) Kodo currently requires that all the lock columns for a given object be in the same table. Additionally, it is only possible to use a single version indicator strategy for a given object. That is, you cannot have one numeric lock column and another date lock column.

Example 14.24. Using Lock Groups with version-number Indicator

For the example presented above, corresponding mapping information might look like so. Note that the lock group name is prepended to the attribute name / extension value when specifying the column name for use by non-default lock groups.


Schema: 

<table name="EMPLOYEE">
    ... primary key columns         ...
    <column name="VERSION1" type="bigint"/>
    <column name="VERSION2" type="bigint"/>
    ... columns for magazine fields ...
</table> 


Mapping information using the mapping XML format:

<class name="Employee">
    ... class mapping  ...
    <jdbc-version-ind type="version-number" column="VERSION1" 
        corporate-column="VERSION2"/>
    ... field mappings ...
</class>


Mapping information using JDO metadata extensions:

<class name="Employee">
    ... class extensions ...
    <extension vendor-name="kodo" key="jdbc-version-ind" value="version-number">
        <extension vendor-name="kodo" key="column" value="VERSION1"/>
        <extension vendor-name="kodo" key="corporate-column" value="VERSION2"/>
    </extension>
    ... field metadata   ...
</class>