Chapter 11.  Remote and Offline Operation

Table of Contents

1. Detach and Attach
1.1. Detach Behavior
1.2. Attach Behavior
1.3. Defining the Detached Object Graph
1.3.1. Detached State Field
2. Remote Event Notification Framework
2.1. Remote Commit Provider Configuration
2.1.1. JMS
2.1.2. TCP
2.1.3. Common Properties
2.2. Customization

The standard JPA runtime environment is local and online. It is local in that components such as EntityManagers and queries connect directly to the datastore and execute their actions in the same JVM as the code using them. It is online in that all changes to managed objects must be made in the context of an active EntityManager. These two properties, combined with the fact that EntityManagers cannot be serialized for storage or network transfer, make the standard JPA runtime difficult to incorporate into some enterprise and client/server program designs.

OpenJPA extends the standard runtime to add remote and offline capabilities in the form of enhanced Detach and Attach APIs and Remote Commit Events. The following sections explain these capabilities in detail.

1.  Detach and Attach

The JPA Overview describes the specification's standard detach and attach APIs in Section 2, “ Entity Lifecycle Management ”. This section enumerates OpenJPA's enhancements to the standard behavior.

1.1.  Detach Behavior

In JPA, objects detach automatically when they are serialized or when a persistence context ends. The specification does not define any way to explicitly detach objects. The extended OpenJPAEntityManager, however, allows you to explicitly detach objects at any time.

public Object detach(Object pc):
public Object[] detachAll(Object... pcs):
public Collection detachAll(Collection pcs):

Each detach method returns detached copies of the given instances. The copy mechanism is similar to serialization, except that only certain fields are traversed. We will see how to control which fields are detached in a later section.

When detaching an instance that has been modified in the current transaction (and thus made dirty), the current transaction is flushed. This means that when subsequently re-attaching the detached instances, OpenJPA assumes that the transaction from which they were originally detached was committed; if it has been rolled back, then the re-attachment process will throw an optimistic concurrency exception.

You can stop OpenJPA from assuming the transaction will commit in the following ways :

  • Invoke EntityTransaction.setRollbackOnly prior to detachingyour objects. Setting the RollbackOnly flag prevents OpenJPA from flushing when detaching dirty objects; instead OpenJPA just runs its pre-flush actions (see the OpenJPAEntityManager.preFlush Javadoc for details).

    This allows you to use the same instances in multiple attach/modify/detach/rollback cycles.

  • Make your modifications outside of a transaction (with NontransactionalWrite enabled) before detaching.

  • Set flushBeforeDetach to false (see Compatibility.setFlushBeforeDetach Javadoc ). This option is similar to the first option, but does not affect the current transaction.

1.2.  Attach Behavior

When attaching, OpenJPA uses several strategies to determine the optimal way to merge changes made to the detached instance. As you will see, these strategies can even be used to attach changes made to a transient instance which was never detached in the first place.

  • If the instance was detached and detached state is enabled, OpenJPA will use the detached state to determine the object's version and primary key values. In addition, this state will tell OpenJPA which fields were loaded at the time of detach, and in turn where to expect changes. Loaded detached fields with null values will set the attached instance's corresponding fields to null.

  • If the instance has a Version field, OpenJPA will consider the object detached if the version field has a non-default value, and new otherwise. Similarly, if the instance has GeneratedValue primary key fields, OpenJPA will consider the object detached if any of these fields have non-default values, and new otherwise.

    When attaching null fields in these cases, OpenJPA cannot distinguish between a field that was unloaded and one that was intentionally set to null. In this case, OpenJPA will use the current detach state setting to determine how to handle null fields: fields that would have been included in the detached state are treated as loaded, and will in turn set the corresponding attached field to null.

  • If neither of the above cases apply, OpenJPA will check to see if an instance with the same primary key values exists in the database. If so, the object is considered detached. Otherwise, it is considered new.

These strategies will be assigned on a per-instance basis, such that during the attachment of an object graph more than one of the above strategies may be used.

If you attempt to attach a versioned instance whose representation has changed in the datastore since detachment, OpenJPA will throw an optimistic concurrency exception upon commit or flush, just as if a normal optimistic conflict was detected. When attaching an instance whose database record has been deleted since detaching, or when attaching a detached instance into a manager that has a stale version of the object, OpenJPA will throw an optimistic concurrency exception from the attach method. In these cases, OpenJPA sets the RollbackOnly flag on the transaction.

1.3.  Defining the Detached Object Graph

When detached objects lose their association with the OpenJPA runtime, they also lose the ability to load additional state from the datastore. It is important, therefore, to populate objects with all the persistent state you will need before detaching them. While you are free to do this manually, OpenJPA includes facilities for automatically populating objects when they detach. The openjpa.DetachState configuration property determines which fields and relations are detached by default. All settings are recursive. They are:

  1. loaded: Detach all fields and relations that are already loaded, but don't include unloaded fields in the detached graph. This is the default.

  2. fetch-groups: Detach all fields and relations in the current fetch configuration. For more information on custom fetch groups, see Section 7, “ Fetch Groups ”.

  3. all: Detach all fields and relations. Be very careful when using this mode; if you have a highly-connected domain model, you could end up bringing every object in the database into memory!

Any field that is not included in the set determined by the detach mode is set to its Java default value in the detached instance.

The openjpa.DetachState option is actually a plugin string (see Section 4, “ Plugin Configuration ”) that allows you to also configure the following options related to detached state:

  • DetachedStateField: As described in Section 1.2, “ Attach Behavior ” above, OpenJPA can take advantage of a detached state field to make the attach process more efficient. This field is added by the enhancer and is not visible to your application. Set this property to one of the following values:

    • transient: Use a transient detached state field. This gives the benefits of a detached state field to local objects that are never serialized, but retains serialization compatibility for client tiers without access to the enhanced versions of your classes. This is the default.

    • true: Use a non-transient detached state field so that objects crossing serialization barriers can still be attached efficiently. This requires, however, that your client tier have the enhanced versions of your classes and the OpenJPA libraries.

    • false: Do not use a detached state field.

    You can override the setting of this property or declare your own detached state field on individual classes using OpenJPA's metadata extensions. See Section 1.3.1, “ Detached State Field ” below.

  • DetachedStateManager: Whether to use a detached state manager. A detached state manager makes attachment much more efficient. Like a detached state field, however, it breaks serialization compatibility with the unenhanced class if it isn't transient.

    This setting piggybacks on the DetachedStateField setting above. If your detached state field is transient, the detached state manager will also be transient. If the detached state field is disabled, the detached state manager will also be disabled. This is typically what you'll want. By setting DetachedStateField to true (or transient) and setting this property to false, however, you can use a detached state field without using a detached state manager. This may be useful for debugging or for legacy OpenJPA users who find differences between OpenJPA's behavior with a detached state manager and OpenJPA's older behavior without one.

  • AccessUnloaded: Whether to allow access to unloaded fields of detached objects. Defaults to true. Set to false to throw an exception whenever an unloaded field is accessed. This option is only available when you use detached state managers, as determined by the settings above.

Example 11.1.  Configuring Detached State

<property name="openjpa.DetachState" value="fetch-groups(DetachedStateField=true)"/>

You can also alter the set of fields that will be included in the detached graph at runtime. OpenJPAEntityManagers expose the following APIs for controlling detached state:

public DetachStateType getDetachState();
public void setDetachState(DetachStateType type);

The DetachStateType enum contains the following values:

enum DetachStateType {
    FETCH_GROUPS,
    LOADED,
    ALL
}

1.3.1.  Detached State Field

When the detached state field is enabled, the OpenJPA enhancer adds an additional field to the enhanced version of your class. This field of type Object. OpenJPA uses this field for bookkeeping information, such as the versioning data needed to detect optimistic concurrency violations when the object is re-attached.

It is possible to define this detached state field yourself. Declaring this field in your class metadata prevents the enhancer from adding any extra fields to the class, and keeps the enhanced class serialization-compatible with the unenhanced version. The detached state field must not be persistent. See Section 3.1.3, “ Detached State ” for details on how to declare a detached state field.

import org.apache.openjpa.persistence.*;

@Entity
public class Magazine
    implements Serializable {

    private String name;
    @DetachedState private Object state;
    ...
}