The standard EJB persistence and JDO runtime environments are local and online. They are local in that components such as EntityManagers, PersistenceManagers, and queries connect directly to the datastore and execute their actions in the same JVM as the code using them. They are online in that changes to managed objects take place in the context of an active EntityManager or PersistenceManager. These two properties, combined with the fact that managers cannot be serialized for storage or network transfer, make the standard EJB persistence and JDO runtimes difficult to incorporate into some enterprise and client/server program designs.
Kodo extends the standard runtime to add remote and offline capabilities in the form of enhanced Detach and Attach APIs, Remote Managers, and Remote Commit Events. The following sections explain these capabilities in detail.
The EJB Persistence Overview describes EJB's standard detach and attach APIs in Chapter 8, EntityManager. The JDO Overview does the same for JDO in Section 8.7, “Detach and Attach Functionality”. This section enumerates Kodo's enhancements to the standard behavior.
![]() | Warning |
|---|---|
Kodo's detach behavior is likely to change in future previews as the specification matures. | |
When a class is detachable, the Kodo enhancer adds an additional field to the enhanced version of the class. This field of type Object. Kodo 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 (in case the client tier only has the unenhanced class definition to work with). The detached state field must not be persistent. See Section 6.4.1.3, “Detached State” for details on how to declare a detached state field.
EJB persistence:
import kodo.persistence.*;
@Entity
public class Magazine
implements Serializable
{
private String name;
@DetachedState private Object state;
...
}
JDO:
public class Magazine
implements Serializable
{
private String name;
private Object detachedState;
...
}
<?xml version="1.0"?>
<jdo>
<package name="org.mag">
<class name="Magazine" detachable="true">
<extension vendor-name="kodo" key="detached-state-field"
value="detachedState"/>
</class>
</package>
</jdo>
In EJB persistence, 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 KodoEntityManager, 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, Kodo 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 Kodo from assuming the transaction will commit by invoking setRollbackOnly prior to detaching your objects. Setting the RollbackOnly flag prevents Kodo from flushing when detaching dirty objects; instead Kodo just runs its pre-flush actions (see the KodoEntityManager.preFlush or KodoPersistenceManager.preFlush Javadoc for details).
This allows you to use the same instances in multiple attach/modify/detach/rollback cycles. Alternatively, you might also prevent a flush by making your modifications outside of a transaction (with NontransactionalWrite enabled) before detaching.
inspecting instances about to be attached, Kodo uses several strategies to determine the optimal way to merge the changes made to the remote 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, Kodo will use the detached state to determine the version and primary key values. In addition, this state will tell Kodo 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 there is a application visible version field , Kodo will inspect the version and primary key field values to see if there is a corresponding instance in the datastore. If the version field is the default Java value, Kodo will assume that the instance is new.
When attaching a null field, Kodo cannot distinguish between a field that was unloaded or intentionally set to null. In this case, Kodo 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.
The final strategy is to assume that the instance is new and should be persisted.
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 an instance whose representation has changed in the datastore since detachment, Kodo 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, Kodo will throw an optimistic concurrency exception from the attach method. In these cases, Kodo sets the RollbackOnly flag on the transaction.
When detached objects lose their association with the Kodo 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, Kodo includes facilities for automatically populating objects when they detach. Kodo supports three modes for determining which fields and relations of an object get populated. All of the modes are recursive.
DETACH_LOADED: Detach all fields and relations that are already loaded, but don't include unloaded fields in the detached graph. This is the default mode.
DETACH_FGS: Detach all fields and relations in the default fetch group, and any other fetch groups that you have added to the current fetch configuration. For more information on custom fetch groups, see Section 5.6, “Fetch Groups”.
DETACH_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.
KodoEntityManagers and KodoPersistenceManagers expose the following APIs for controlling detached state:
public static final int DETACH_LOADED; public static final int DETACH_FGS; public static final int DETACH_ALL; public int getDetachState (int mode); public void setDetachState (int mode);
You can also change the default mode using the kodo.DetachState configuration property.
EJB XML format:
<!-- available settings are "loaded", "fgs", "all" --> <property name="kodo.DetachState" value="fgs"/>
JDO properties format:
# available settings are "loaded", "fgs", "all" kodo.DetachState: fgs
The EJB Persistence Overview describes EJB's automatic detach behavior in Section 7.3, “Persistence Context”. We describe Kodo's options for automatic detach in JDO below.
Detachable JDO objects automatically detach when they are serialized. Kodo expands this automatic detach behavior with optional automatic detachment on various events: close, commit, and non-transactional read. On each configured event, all managed objects in the PersistenceManager cache become detached. Non-detachable objects become transient.
close: Detach all objects when the PersistenceManager closes.
commit: Detach all objects when a transaction ends.
nontx-read: Reads outside of a transaction will automatically detach instances before returning them.
By using the proper set of these options, you can avoid unnecessary detach calls in your code. Some common use cases for this functionality are:
Client/Server applications with short-lived PersistenceManagers. Clients request objects from the server, who returns detached instances which can be modified and manipulated without a local PersistenceManager . Changes can then be re-attached later through the server.
Servlets and JSPs. A very common pattern in servlet and JSP architectures is to allocate a PersistenceManager at the beginning of a web request, then close it at the end of the request. You can store detached instances between requests in the HTTP session, to maintain changes to an object over several pages for example.
Session Beans. These beans can return a portion of the object graph which remain valid no matter what tier the bean is being called from.
No automatic detachment is performed in JDO by default. You can turn on automatic detachment at runtime through the KodoPersistenceManager.setAutoDetach methods. You can also set detach detachment triggers with the kodo.AutoDetach configuration property:
kodo.AutoDetach: close, commit, nontx-read