Indirection (Lazy Loading)

By default, when EclipseLink retrieves a persistent object, it retrieves all of the dependent objects to which it refers. When you configure indirection (also known as lazy reading, lazy loading, and just-in-time reading) for an attribute mapped with a relationship mapping, EclipseLink uses an indirection object as a place holder for the referenced object: EclipseLink defers reading the dependent object until you access that specific attribute. This can result in a significant performance improvement, especially if the application is interested only in the contents of the retrieved object, rather than the objects to which it is related.

Oracle strongly recommends using indirection for all relationship mappings. Not only does this lets you optimize data source access, but it also allows EclipseLink to optimize the persistence unit processing, cache access, and concurrency.

Notes:

The use of indirection is especially important for providing a proper maintenance of bidirectional relationships. In this case, you must use indirection. If you are operating with collections, you must use transparent indirection (see Transparent Indirection).

The implementation of indirection (lazy loading) is vendor-specific. Serializing entities and merging those entities back into a persistence context may not be interoperable across vendors when lazy properties or fields and/or relationships are used.

Figure 6-8 shows an indirection example. Without indirection, reading the Order object also reads the dependent collection of LineItem objects. With indirection, reading the Order object does not read the dependent collection of LineItem objects: the lineItems attribute refers to an indirection object. You can access other attributes (such as customerId), but EclipseLink reads the dependent LineItem objects only if and when you access the lineItems attribute.

When using indirection with an object that your application serializes, you must consider the effect of any untriggered indirection objects at deserialization time. See Indirection, Serialization, and Detachment.

Indirection, Serialization, and Detachment

When using indirection (lazy loading), it is likely that a graph of persistent objects will contain untriggered indirection objects. Because indirection objects are transient and do not survive serialization between one JVM and another, untriggered indirection objects will trigger an error if the relationship is accessed after deserialization.

The application must ensure that any indirect relationships that will be required after deserialization have been instantiated before serialization. This can be done through accessing the get method for any relationship using ValueHolder or weaved indirection, and by calling the size method to any relationship using transparent indirection. If the application desired the relationships to be always instantiated on serialization, you could overwrite the serialization writeObject method in the persistent class to first instantiate the desired relationships. Use caution for objects with many or deep relationships to avoid serializing large object graphs: ideally, only the relationships required by the client should be instantiated.

When serializing JPA entities, any lazy relationships that have not been instantiated prior to serialization will trigger errors if they are accessed. If weaving is used on the server, and the entities are serialized to a client, the same weaved classes must exist on the client, either through static weaving of the jar, or through launching the client JVM using the EclipseLink agent.

Value Holder Indirection

Persistent classes that use indirection must replace relationship attributes with value holder attributes. A value holder is an instance of a class that implements the ValueHolderInterface interface, such as ValueHolder. This object stores the information necessary to retrieve the object it is replacing from the database. If the application does not access the value holder, the replaced object is never read from the database.

To obtain the object that the value holder replaces, use the getValue and setValue methods of the ValueHolderInterface. A convenient way of using these methods is to hide the getValue and setValue methods of the ValueHolderInterface inside get and set methods, as shown in the following illustrations.

Figure 6-9 shows the Employee object being read from the database. The Address object is not read and will not be created unless it is accessed.

If you are using method access, the get and set methods specified in the mapping must access the instance of ValueHolderInterface, rather than the object referenced by the value holder. The application should not use these getter and setter, but use the getter and setter that hide the usage of value holders.

Transparent Indirection

Transparent indirection lets you declare any relationship attribute of a persistent class that holds a collection of related objects as any of the following Java objects:

java.util.Collection

java.util.Hastable

java.util.List

java.util.Map

java.util.Set

java.util.Vector

EclipseLink will use an indirection object that implements the appropriate interface and also performs just-in-time reading of the related objects. When using transparent indirection, you do not have to declare the attributes as ValueHolderInterface.

Newly created collection mappings use transparent indirection by default if their attribute is not a ValueHolderInterface.

You can configure EclipseLink to automatically weave transparent indirect container indirection for JPA entities and Plain Old Java Object (POJO) classes. For more information, see Using Java Byte-code Weaving and About Weaving.

Proxy Indirection

The Java class Proxy lets you use dynamic proxy objects as place-holders for a defined interface. Certain EclipseLink mappings can be configured to use proxy indirection, which gives you the benefits of indirection without the need to include EclipseLink classes in your domain model. Proxy indirection is to one-to-one relationship mappings as indirect containers are to collection mappings.

To use proxy indirection, your domain model must satisfy all of the following criteria:

The target class of the one-to-one relationship must implement a public interface.

The one-to-one attribute on the source class must be of the interface type.

If you employ method accessing, then the getter and setter methods must use the interface.

To configure proxy indirection, you can use JDeveloper or Java in an amendment method.

Proxy Indirection Restrictions

Proxy objects in Java are only able to intercept messages sent. If a primitive operation such as ==, instanceof, or getClass is used on a proxy, it will not be intercepted. This limitation can require the application to be somewhat aware of the usage of proxy objects.

You cannot register the target of a proxy indirection implementation with a persistence unit. Instead, first register the source object with the persistence unit. This lets you retrieve a target object clone with a call to a getter on the source object clone.

Weaved Indirection

For JPA entities or POJO classes that you configure for weaving, EclipseLink weaves value holder indirection for one-to-one mappings. If you want EclipseLink to weave change tracking and your application includes collection mappings (one-to-many or many-to-many), then you must configure all collection mappings to use transparent indirect container indirection only (you may not configure your collection mappings to use eager loading nor value holder indirection).

For more information on these annotations, see Java Persistence API (JPA) Extensions Reference for EclipseLink.

Default Conversions and Converters

The section "Converter Annotations" in Java Persistence API (JPA) Extensions Reference for EclipseLink provides a list of the converter annotation extensions defined by EclipseLink and links to their descriptions.

See the individual converter annotations in Java Persistence API (JPA) Extensions Reference for EclipseLink for descriptions of the following:

the order in which the EclipseLink persistence provider searches the converter annotations

the types of classes for which you can specify converters (you can define converters at the class, field and property level)

the mappings with which you can use converters

Collection Mappings

You can access additional advanced mappings and mapping options through the EclipseLink descriptor and mapping API using a DescriptorCustomizer class.

One-to-Many Mapping

One-to-many mappings are used to represent the relationship between a single source object and a collection of target objects. They are a good example of something that is simple to implement in Java using a Collection (or other collection types) of target objects, but difficult to implement using relational databases.

In a Java Collection, the owner references its parts. In a relational database, the parts reference their owner. Relational databases use this implementation to make querying more efficient.

The phone attribute shown in the One-to-Many Relationships is of type Vector. You can use a Collection interface (or any class that implements the Collection interface) for declaring the collection attribute.

JPA Mapping

By default, JPA automatically defines a OneToMany mapping for a many-valued association with one-to-many multiplicity.

Use the @OneToMany annotation to do the following:

configure the fetch type to EAGER

configure the associated target entity, because the Collection used is not defined using generics

configure the operations that must be cascaded to the target of the association: for example, if the owning entity is removed, ensure that the target of the association is also removed

configure the details of the join table used by the persistence provider for unidirectional one-to-many relationships. For a one-to-many using a mappedBy or JoinColumn, the deletion of the related objects is cascaded on the database. For a one-to-many using a JoinTable, the deletion of the join table is cascaded on the database (target objects cannot be cascaded even if private because of constraint direction).

For more information, see Section 11.1.23 "JoinTable Annotation" in the JPA Specification.

Many-to-Many Mapping

Many-to-many mappings represent the relationships between a collection of source objects and a collection of target objects. They require the creation of an intermediate table for managing the associations between the source and target records.

Figure 6-13 illustrates a many-to-many mapping in Java and in relational database tables.

Using Indirection with Collections

JPA specifies that lazy loading is a hint to the persistence provider that data should be fetched lazily when it is first accessed, if possible. If you are developing your application in a Java EE environment, set fetch to javax.persistence.FetchType.LAZY, and the persistence provider supplies all the necessary functionality.

When using a one-to-one or many-to-one mapping in a Java SE environment, use either dynamic or static weaving to perform lazy loading when the fetch attribute is set to FetchType.LAZY. Also in the Java SE environment, one-to-many and many-to-many relationships are lazy by default and use transparent indirection, while one-to-one and many-to-one relationships are not lazy.

When using a one-to-one or many-to-one mapping in a Java SE environment and the environment does not permit the use of -javaagent on the JVM command line, use static weaving to perform lazy loading when the fetch attribute is set to FetchType.LAZY.

If you set one-to-one or many-to-one relationships to lazy, and you enable weaving, the EclipseLink JPA persistence provider will use weaving to enable value holder indirection for these relationships.

The collection annotations @OneToOne, @OneToMany, @ManyToMany, and @ManyToOne provide a fetch mapping attribute which can be set to lazy or eager. When you set the attribute to lazy, the EclipseLink JPA persistence provider uses indirection.

Lazy loading is performed when the fetch attribute is set to javax.persistence.FetchType.LAZY (default).

Lazy loading is performed when the fetch attribute is set to javax.persistence.FetchType.LAZY (default).

One-to-many

Lazy loading is performed when the fetch attribute is set to javax.persistence.FetchType.LAZY (default).

Lazy loading is performed when the fetch attribute is set to javax.persistence.FetchType.LAZY (default).

One-to-one

Lazy loading is performed when the fetch attribute is set to javax.persistence.FetchType.LAZY.

The fetch attribute is ignored and default javax.persistence.FetchType.EAGER applies.

Many-to-one

Lazy loading is performed when the fetch attribute is set to javax.persistence.FetchType.LAZY.

The fetch attribute is ignored and default javax.persistence.FetchType.EAGER applies

Basic

Lazy loading is performed when the fetch attribute is set to javax.persistence.FetchType.LAZY.

The fetch attribute is ignored and default javax.persistence.FetchType.EAGER applies

Using Optimistic Locking

Oracle recommends using optimistic locking. With optimistic locking, all users have read access to the data. When a user attempts to write a change, the application checks to ensure the data has not changed since the user read the data.

Optimistic Locking in a Stateless Environment

In a stateless environment, take care to avoid processing out-of-date (stale) data. A common strategy for avoiding stale data is to implement optimistic locking, and store the optimistic lock values in the object. This solution requires careful implementation if the stateless application serializes the objects, or sends the contents of the object to the client in an alternative format. In this case, transport the optimistic lock values to the client in the HTTP contents of an edit page. You must then use the returned values in any write transaction to ensure that the data did not change while the client was performing its work.

You can use optimistic version locking or optimistic field locking policies. Oracle recommends using version locking policies.

Optimistic Version Locking

Use the @Version annotation to enable the JPA-managed optimistic locking by specifying the version field or property of an entity class that serves as its optimistic lock value (recommended).

When choosing a version field or property, ensure that the following is true:

there is only one version field or property per entity

you choose a property or field persisted to the primary table

your application does not modify the version property or field

For more information, see Section 11.1.45 "Table Annotation" in the JPA Specification.