5.8. Lock Groups

5.8.1. Lock Groups and Subclasses
5.8.2. Lock Group Mapping

Kodo supports both optimistic and datastore locking strategies, but optimistic locking is the preferred approach in most applications. Typically, optimistic locking is performed at the object level of granularity. That is, changes to any part of the same object in concurrent transactions will result in an optimistic locking exception being thrown by 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 modeled 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 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.

You specify a field's lock group in JPA metadata with the kodo.persistence.LockGroup annotation. You specify a field's lock group in JDO metadata with the lock-group metadata extension. See Section 6.4.2.6, “Lock Group” for details on lock group metadata.

Example 5.22. Lock Group Metadata

JPA:

import kodo.persistence.*;

@Entity
public class Employee
{
    // default lock group
    private String firstName;
    private String lastName;
    private String phoneNumber;

    // named lock group
    @LockGroup("corporate") private float  salary;
    @LockGroup("corporate") private String title;

    // no lock group; allow concurrent modifications
    @LockGroup(LockGroup.NONE) private Set<Project> projects;

    ...
}

JDO:

public class Employee
{
    private String firstName;
    private String lastName;
    private String phoneNumber;
    private float  salary;
    private String title;
    private Set    projects;

    ...
}


<?xml version="1.0"?>
<jdo>
  <package name="">
    <class name="Employee">
      <!-- named 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>
      <!-- no lock group; allow concurrent modifications -->
      <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 number and timestamp version strategies. They are not supported in the state-comparison strategy, though you can still exclude fields from participating in optimistic versioning under this strategy by setting the their lock group to none.

5.8.1. Lock Groups and Subclasses

Due to mapping restrictions, subclasses cannot simply declare additional lock groups implicitly, as is done in the example shown above. Instead, the least-derived mapped type in the persistent hierarchy must list all lock groups that its children can use via the kodo.persistence.LockGroups annotation in JPA metadata, or with JDO's 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 5.23. Lock Group Metadata

JPA:

import kodo.persistence.*;

@Entity
@LockGroups({"corporate"})
public class Person
{
    // default lock group
    private String firstName;
    private String lastName;
    private String phoneNumber;

    ...
}

@Entity
public class Employee
    extends Person
{
    // named lock group
    @LockGroup("corporate") private float  salary;
    @LockGroup("corporate") private String title;

    // no lock group; allow concurrent modifications
    @LockGroup(LockGroup.NONE) private Set<Project> projects;

    ...
}

JDO:

public class 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"/>
    </class>
    <class name="Employee">
      <!-- named 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>
      <!-- no lock group; allow concurrent modifications -->
      <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.

5.8.2. Lock Group Mapping

When using custom lock groups with a relational database, Kodo will need a version column for each of the groups, instead of just one version column. This means that you must use surrogate versioning; you cannot use a version field. Kodo also currently requires that all the version columns for a given object be in the same table. Finally, it is only possible to use a single version strategy for a given object. That is, you cannot have one version number column and another timestamp version column.

Use the kodo.persistence.jdbc.LockGroupVersionColumn(s) annotation to specify the version column for each lock group in JPA mapping. In your JDO mappings, use the lock-group column extension to specify the version column for each lock group.

Example 5.24. Mapping Lock Groups

JPA:

import kodo.persistence.jdbc.*;

@Entity
@Table(name="EMP")
@LockGroupVersionColumns({
    @LockGroupVersionColumn(name="VERS_CORP" lockGroup="corporate"),
    @LockGroupVersionColumn(name="VERS")
})
public class Employee
{
    ...
}

JDO:

<class name="Employee" table="EMP">
    <version strategy="version-number">
        <column name="VERS_CORP">
            <extension vendor-name="kodo" key="lock-group" value="corporate"/>
        </column>
        <!-- column for default group doesn't need an extension -->
        <column name="VERS"/>
    </version>
    ...
</class>

 

Skip navigation bar   Back to Top