blob: 9a65547c93b5c2af6ab056093fe66d08998f92e0 [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<chapter id="ref_guide_pc">
<title>
Persistent Classes
</title>
<indexterm zone="ref_guide_pc">
<primary>
persistent classes
</primary>
</indexterm>
<para>
Persistent class basics are covered in <xref linkend="jpa_overview_pc"/>
of the JPA Overview. This chapter details the persistent class features OpenJPA
offers beyond the core JPA specification.
</para>
<section id="ref_guide_pc_pcclasses">
<title>
Persistent Class List
</title>
<indexterm zone="ref_guide_pc_pcclasses">
<primary>
persistent classes
</primary>
<secondary>
list
</secondary>
</indexterm>
<indexterm zone="ref_guide_pc_pcclasses">
<primary>
PCClasses
</primary>
</indexterm>
<para>
Unlike many ORM products, OpenJPA does not need to know about all of your
persistent classes at startup. OpenJPA discovers new persistent classes
automatically as they are loaded into the JVM; in fact you can introduce new
persistent classes into running applications under OpenJPA. However, there are
certain situations in which providing OpenJPA with a persistent class list is
helpful:
</para>
<itemizedlist>
<listitem>
<para>
OpenJPA must be able to match entity names in JPQL queries to persistent
classes. OpenJPA automatically knows the entity names of any persistent classes
already loaded into the JVM. To match entity names to classes that have not been
loaded, however, you must supply a persistent class list.
</para>
</listitem>
<listitem>
<para>
When OpenJPA manipulates classes in a persistent inheritance hierarchy, OpenJPA
must be aware of all the classes in the hierarchy. If some of the classes have
not been loaded into the JVM yet, OpenJPA may not know about them, and queries
may return incorrect results.
</para>
</listitem>
<listitem>
<para>
If you configure OpenJPA to create the needed database schema on startup (see
<xref linkend="ref_guide_mapping_synch"/>), OpenJPA must know all of your
persistent classes up-front.
</para>
</listitem>
</itemizedlist>
<para>
When any of these conditions are a factor in your JPA application, use the
<literal>class</literal>, <literal>mapping-file</literal>, and <literal>
jar-file</literal> elements of JPA's standard XML format to list your persistent
classes. See <xref linkend="jpa_overview_persistence_xml"/> for details.
</para>
<para>
Alternately, you can tell OpenJPA to search through your classpath for
persistent types. This is described in more detail in
<xref linkend="ref_guide_meta_factory"/>.
</para>
<note>
<para>
Listing persistent classes (or their metadata or jar files) is an all-or-nothing
endeavor. If your persistent class list is non-empty, OpenJPA will assume that
any unlisted class is not persistent.
</para>
</note>
</section>
<section id="ref_guide_pc_enhance">
<title>
Enhancement
</title>
<indexterm zone="ref_guide_pc_enhance">
<primary>
enhancer
</primary>
</indexterm>
<indexterm>
<primary>
openjpac
</primary>
<see>
enhancer
</see>
</indexterm>
<para>
In order to provide optimal runtime performance, flexible lazy loading, and
efficient, immediate dirty tracking, OpenJPA can use an <emphasis>enhancer
</emphasis>. An enhancer is a tool that automatically adds code to your
persistent classes after you have written them. The enhancer post-processes the
bytecode generated by your Java compiler, adding the necessary fields and
methods to implement the required persistence features. This bytecode
modification perfectly preserves the line numbers in stack traces and is
compatible with Java debuggers. In fact, the only change to debugging
is that the persistent setter and getter methods of entity classes using
property access will be prefixed with <literal>pc</literal> in stack traces and
step-throughs. For example, if your entity has a <methodname>getId</methodname>
method for persistent property <literal>id</literal>, and that method throws an
exception, the stack trace will report the exception from method <methodname>
pcgetId</methodname>. The line numbers, however, will correctly correspond to
the <methodname>getId</methodname> method in your source file.
</para>
<mediaobject>
<imageobject>
<!-- PNG image data, 509 x 133 (see README) -->
<imagedata fileref="img/enhancement.png" width="339px"/>
</imageobject>
</mediaobject>
<para>
The diagram above illustrates the compilation of a persistent class.
</para>
<para>
You can add the OpenJPA enhancer to your build process, or use Java 1.5's
instrumentation features to transparently enhance persistent classes when they
are loaded into the JVM. The following sections describe each option.
</para>
<section id="ref_guide_pc_enhance_build">
<title>
Enhancing at Build Time
</title>
<indexterm zone="ref_guide_pc_enhance_build">
<primary>
enhancer
</primary>
<secondary>
build time
</secondary>
</indexterm>
<para>
The enhancer can be invoked at build time
via its Java class, <classname>
org.apache.openjpa.enhance.PCEnhancer</classname>.
</para>
<note>
<para>
You can also enhance via Ant; see
<xref linkend="ref_guide_integration_enhance"/>.
</para>
</note>
<example id="ref_guide_pc_enhance_enhancer">
<title>
Using the OpenJPA Enhancer
</title>
<programlisting>
java org.apache.openjpa.enhance.PCEnhancer Magazine.java
</programlisting>
</example>
<para>
The enhancer accepts the standard set of command-line arguments defined by the
configuration framework (see <xref linkend="ref_guide_conf_devtools"/> ),
along with the following flags:
</para>
<itemizedlist>
<listitem>
<para>
<literal>-directory/-d &lt;output directory&gt;</literal>: Path to the output
directory. If the directory does not match the enhanced class' package, the
package structure will be created beneath the directory. By default, the
enhancer overwrites the original <filename>.class</filename> file.
</para>
</listitem>
<listitem>
<para>
<literal>-enforcePropertyRestrictions/-epr &lt;true/t | false/f&gt;</literal>:
Whether to throw an exception when it appears that a property access entity is
not obeying the restrictions placed on property access. Defaults to false.
</para>
</listitem>
<listitem>
<para>
<literal>-addDefaultConstructor/-adc &lt;true/t | false/f&gt;</literal>: The
spec requires that all persistent classes define a no-arg constructor. This flag
tells the enhancer whether to add a protected no-arg constructor to any
persistent classes that don't already have one. Defaults to <literal>
true</literal>.
</para>
</listitem>
<listitem>
<para>
<literal>-tmpClassLoader/-tcl &lt;true/t | false/f&gt;</literal>: Whether to
load persistent classes with a temporary class loader. This allows other code to
then load the enhanced version of the class within the same JVM. Defaults to
<literal>true</literal>. Try setting this flag to <literal>false</literal> as a
debugging step if you run into class loading problems when running the enhancer.
</para>
</listitem>
</itemizedlist>
<para>
Each additional argument to the enhancer must be one of the following:
</para>
<itemizedlist>
<listitem>
<para>
The full name of a class.
</para>
</listitem>
<listitem>
<para>
The .java file for a class.
</para>
</listitem>
<listitem>
<para>
The <filename>.class</filename> file of a class.
</para>
</listitem>
</itemizedlist>
<para>
If you do not supply any arguments to the enhancer, it will run on the classes
in your persistent class list (see <xref linkend="ref_guide_pc_pcclasses"/>).
You must, however, supply the classpath you wish the enhancer to run with. This
classpath must include, at minimum, the openjpa jar(s), persistence.xml and
the target classes.
</para>
<para>
You can run the enhancer over classes that have already been enhanced, in which
case it will not further modify the class. You can also run it over classes that
are not persistence-capable, in which case it will treat the class as
persistence-aware. Persistence-aware classes can directly manipulate the
persistent fields of persistence-capable classes.
</para>
<para>
Note that the enhancement process for subclasses introduces dependencies on the
persistent parent class being enhanced. This is normally not problematic;
however, when running the enhancer multiple times over a subclass whose parent
class is not yet enhanced, class loading errors can occur. In the event of a
class load error, simply re-compile and re-enhance the offending classes.
</para>
</section>
<section id="ref_guide_pc_enhance_runtime_container">
<title>
Enhancing JPA Entities on Deployment
</title>
<indexterm zone="ref_guide_pc_enhance_runtime_container">
<primary>
enhancer
</primary>
<secondary>
runtime
</secondary>
<tertiary>
in an EJB container
</tertiary>
</indexterm>
<para>
The Java EE specification includes hooks to automatically enhance JPA entities
when they are deployed into a container. Thus, if you are using a Java EE-compliant application server,
OpenJPA will enhance your entities automatically
at runtime. Note that if you prefer build-time enhancement, OpenJPA's runtime
enhancer will correctly recognize and skip pre-enhanced classes.
</para>
<para>
If your application server does not support the Java EE enhancement hooks,
consider using the build-time enhancement described above, or the more general
runtime enhancement described in the next section.
</para>
</section>
<section id="ref_guide_pc_enhance_runtime">
<title>
Enhancing at Runtime
</title>
<indexterm zone="ref_guide_pc_enhance_runtime">
<primary>
enhancer
</primary>
<secondary>
runtime
</secondary>
<tertiary>
outside a container
</tertiary>
</indexterm>
<para>
OpenJPA includes a <emphasis>Java agent</emphasis> for automatically enhancing
persistent classes as they are loaded into the JVM. Java agents are classes that
are invoked prior to your application's <methodname>main</methodname> method.
OpenJPA's agent uses JVM hooks to intercept all class loading to enhance classes
that have persistence metadata before the JVM loads them.
</para>
<para>
Searching for metadata for every class loaded by the JVM can slow application
initialization. One way to speed things up is to take advantage of the optional
persistent class list described in <xref linkend="ref_guide_pc_pcclasses"/>. If
you declare a persistent class list, OpenJPA will only search for
metadata for classes in that list.
</para>
<para>
To employ the OpenJPA agent, invoke <literal>java</literal> with the <literal>
-javaagent</literal> set to the path to your OpenJPA jar file.
</para>
<example id="ref_guide_pc_enhance_runtime_ex">
<title>
Using the OpenJPA Agent for Runtime Enhancement
</title>
<programlisting>
java -javaagent:/home/dev/openjpa/lib/openjpa.jar com.xyz.Main
</programlisting>
</example>
<para>
You can pass settings to the agent using OpenJPA's plugin syntax (see
<xref linkend="ref_guide_conf_plugins"/>). The agent accepts the long
form of any of the standard configuration options
(<xref linkend="ref_guide_conf_devtools"/> ). It also accepts the following
options, the first three of which correspond exactly to the same-named
options of the enhancer tool described in
<xref linkend="ref_guide_pc_enhance_build"/>:
</para>
<itemizedlist>
<listitem>
<para>
<literal>addDefaultConstructor</literal>
</para>
</listitem>
<listitem>
<para>
<literal>enforcePropertyRestrictions</literal>
</para>
</listitem>
<listitem>
<para>
<literal>scanDevPath</literal>: Boolean indicating whether to scan the
classpath for persistent types if none have been configured. If you do not
specify a persistent types list and do not set this option to true, OpenJPA will
check whether each class loaded into the JVM is persistent, and enhance it
accordingly. This may slow down class load times significantly.
</para>
</listitem>
<listitem>
<para>
<literal>classLoadEnhancement</literal>: Boolean controlling whether OpenJPA
load-time class enhancement should be available in this JVM execution. Default:
<literal>true</literal>
</para>
</listitem>
<listitem>
<para>
<literal>runtimeRedefinition</literal>: Boolean controlling whether OpenJPA
class redefinition should be available in this JVM execution. Default:
<literal>true</literal>
</para>
</listitem>
</itemizedlist>
<example id="ref_guide_pc_enhance_runtime_opt_ex">
<title>
Passing Options to the OpenJPA Agent
</title>
<programlisting>
java -javaagent:/home/dev/openjpa/lib/openjpa.jar=addDefaultConstructor=false com.xyz.Main
</programlisting>
</example>
</section>
<section id="ref_guide_pc_enhance_dynamic">
<title>
Enhancing Dynamically at Runtime
</title>
<para>
If a javaagent is not provided via the command line and
OpenJPA is running on the Oracle 1.6 SDK or IBM 1.6 JDK (SR8+), OpenJPA
will attempt to dynamically load the Enhancer that was
mentioned in the previous section. This support is
provided as an ease of use feature and it is not recommended
for use in a production system. Using this method of
enhancement has the following caveats:
</para>
<itemizedlist>
<listitem>
<para>
The dynamic runtime enhancer is plugged into
the JVM during creation of the
EntityManagerFactory. Any Entity classes that
are loaded before the EntityManagerFactory is
created will not be enhanced.
</para>
</listitem>
<listitem>
<para>
The command line javaagent settings are not
configurable when using this method of
enhancement.
</para>
</listitem>
<listitem>
<para>
Just as with the Javaagent approach, if you
declare a persistent class list, then OpenJPA
will only search for metadata and try to
enhance the listed classes.
</para>
</listitem>
</itemizedlist>
<para>
When then dynamic enhancer is loaded, the following
informational message is logged:
<programlisting>
[java] jpa.enhancement INFO [main] openjpa.Runtime - OpenJPA dynamically loaded the class enhancer. Any classes that were not enhanced at build time will be enhanced when they are loaded by the JVM.
</programlisting>
</para>
<para>
Setting the property openjpa.DynamicEnhancementAgent to false
will disable this function.
</para>
</section>
<section id="ref_guide_pc_enhance_unenhanced_types">
<title>
Omitting the OpenJPA enhancer
</title>
<indexterm zone="ref_guide_pc_enhance_unenhanced_types">
<primary>
enhancer
</primary>
<secondary>
omitting
</secondary>
<tertiary>
outside a container
</tertiary>
</indexterm>
<para>
OpenJPA does not require that the enhancer be run. If you do not run the
enhancer, OpenJPA will fall back to one of several possible alternatives for
state tracking, depending on the execution environment.
</para>
<itemizedlist>
<listitem><para>
<emphasis>Deploy-time enhancement</emphasis>: if you are running your
application inside a Java EE container, or another environment that supports
the JPA container contract, then OpenJPA will automatically perform class
transformation at deploy time.
</para></listitem>
<listitem><para>
<emphasis>Java 6 class retransformation</emphasis>: if you are running your
application in a Java 6 environment, OpenJPA will attempt to dynamically
register a <literal>ClassTransformer</literal> that will redefine your
persistent classes on the fly to track access to persistent data. Additionally,
OpenJPA will create a subclass for each of your persistent classes. When
you execute a query or traverse a relation, OpenJPA will return an instance
of the subclass. This means that the <literal>instanceof</literal> operator
will work as expected, but <literal>o.getClass()</literal> will return the
subclass instead of the class that you wrote.
</para>
<para>
You do not need to do anything at all to get this behavior. OpenJPA will
automatically detect whether or not the execution environment is capable of
Java 6 class retransformation.
</para></listitem>
<listitem><para>
<emphasis>Java 5 class redefinition</emphasis>: if you are running your
application in a Java 5 environment, and you specify the OpenJPA javaagent,
OpenJPA will use Java 5 class redefinition to redefine any persistent classes
that are not enhanced by the OpenJPA javaagent. Aside from the requirement
that you specify a javaagent on the command line, this behavior is exactly the
same as the Java 6 class retransformation behavior. Of course, since the
OpenJPA javaagent performs enhancement by default, this will only be available
if you set the <literal>classLoadEnhancement</literal> javaagent flag to
<literal>false</literal>, or on any classes that are skipped by the OpenJPA
runtime enhancement process for some reason.
</para></listitem>
<listitem><para>
<emphasis>Runtime Unenhanced Classes</emphasis>: AKA state comparison and
subclassing. If you are running
in a Java 5 environment without a javaagent, or in a Java 6 environment that
does not support class retransformation, OpenJPA will still create subclasses
as outlined above. However, in some cases, OpenJPA may not be able to receive
notifications when you read or write persistent data.
</para>
<para>
<note>
Runtime Unenhanced Classes has some known limitations which are discussed below
and documented in JIRA issue tracker on the OpenJPA website. As a result this option is
disabled by default. Support for this method of automatic enhancement may be
enabled via the <xref linkend="openjpa.RuntimeUnenhancedClasses">
openjpa.RuntimeUnenhancedClasses configuration </xref> option.
</note>
</para>
<para>
To enable Runtime Unenhanced Classes for a specific persistence unit, add the following property to persistence.xml:
<programlisting>
<![CDATA[<properties>
. . .
<property name="openjpa.RuntimeUnenhancedClasses" value="supported"/>
. . .
<properties>]]>
</programlisting>
</para>
<para>
If you are using <emphasis>property access</emphasis> for your persistent data,
then OpenJPA will be able to track all accesses for instances that you load
from the database, but not for instances that you create. This is because
OpenJPA will create new instances of its dynamically-generated subclass when
it loads data from the database. The dynamically-generated subclass has
code in the setters and getters that notify OpenJPA about persistent data
access. This means that new instances that you create will be subject to
state-comparison checks (see discussion below) to compute which fields to
write to the database, and that OpenJPA will ignore requests to evict
persistent data from such instances. In practice, this is not a particularly
bad limitation, since OpenJPA already knows that it must insert all field
values for new instances. So, this is only really an issue if you flush
changes to the database while inserting new records; after such a flush,
OpenJPA will need to hold potentially-unneeded hard references to the
new-flushed instances.
</para>
<para>
If you are using <emphasis>field access</emphasis> for your persistent data,
then OpenJPA will not be able to track accesses for any instances, including
ones that you load from the database. So, OpenJPA will perform state-comparison
checks to determine which fields are dirty. These state comparison checks are
costly in two ways. First, there is a performance penalty at flush / commit
time, since OpenJPA must walk through every field of every instance to determine
which fields of which records are dirty. Second, there is a memory penalty,
since OpenJPA must hold hard references to all instances that were loaded at
any time in a given transaction, and since OpenJPA must keep a copy of all
the initial values of the loaded data for later comparison. Additionally,
OpenJPA will ignore requests to evict persistent state for these types of
instances. Finally, the default lazy loading configuration will be ignored for
single-valued fields (one-to-one, many-to-one, and any other non-collection
or non-map field that has a lazy loading configuration). If you use fetch
groups or programmatically configure your fetch plan, OpenJPA will obey these
directives, but will be unable to lazily load any data that you exclude from
loading. As a result of these limitations, it is not recommended that you use
field access if you are not either running the enhancer or using OpenJPA with
a javaagent or in a Java 6 environment.
</para></listitem>
</itemizedlist>
</section>
</section>
<section id="ref_guide_pc_interfaces">
<title>Managed Interfaces</title>
<indexterm zone="ref_guide_pc_interfaces">
<primary>interfaces</primary>
<secondary>managed</secondary>
</indexterm>
<para>
OpenJPA's managed interface feature allows you to define your object model
entirely in terms of interfaces, instead of concrete classes. To use this
feature, you must annotate your managed interfaces with the
<classname>ManagedInterface</classname> annotation, and use the
<literal>OpenJPAEntityManager.createInstance(Class)</literal> method to
create new records. Note that <literal>createInstance()</literal> returns
unmanaged instances; you must pass them to
<literal>EntityManager.persist()</literal> to store them in the database.
</para>
<programlisting>
@ManagedInterface
public interface PersonIface {
@Id @GeneratedValue
int getId();
void setId(int id);
// implicitly persistent per JPA property rules
String getName();
void setName(String name);
}
</programlisting>
<programlisting>
OpenJPAEntityManager em = ...;
PersonIface person = em.createInstance(PersonIface.class);
person.setName("Homer Simpson");
em.getTransaction().begin();
em.persist(person);
em.getTransaction().commit();
</programlisting>
</section>
<section id="ref_guide_pc_oid">
<title>
Object Identity
</title>
<indexterm zone="ref_guide_pc_oid">
<primary>
identity
</primary>
</indexterm>
<para>
OpenJPA makes several enhancements to JPA's standard entity identity.
</para>
<section id="ref_guide_pc_oid_datastore">
<title>
Datastore Identity
</title>
<indexterm zone="ref_guide_pc_oid_datastore">
<primary>
identity
</primary>
<secondary>
datastore
</secondary>
</indexterm>
<para>
The JPA specification requires you to declare one or more identity fields in
your persistent classes. OpenJPA fully supports this form of object identity,
called <emphasis>application</emphasis> identity. OpenJPA, however, also
supports <emphasis>datastore</emphasis> identity. In datastore identity, you do
not declare any primary key fields. OpenJPA manages the identity of your
persistent objects for you through a surrogate key in the database.
</para>
<para>
You can control how your JPA datastore identity value is generated through
OpenJPA's
<ulink url="../javadoc/org/apache/openjpa/persistence/DataStoreId.html">
<classname>org.apache.openjpa.persistence.DataStoreId</classname></ulink> class
annotation. This annotation has <literal>strategy</literal> and <literal>
generator</literal> properties that mirror the same-named properties on the
standard <classname>javax.persistence.GeneratedValue</classname> annotation
described in <xref linkend="jpa_overview_meta_id"/> of the JPA Overview.
</para>
<para>
To retrieve the identity value of a datastore identity entity, use the
<methodname>OpenJPAEntityManager.getObjectId(Object entity)</methodname>
method. See <xref linkend="ref_guide_runtime_em"/> for more information on
the <classname>OpenJPAEntityManager</classname>.
</para>
<example id="ref_guide_pc_oid_datastoreentityex">
<title>
JPA Datastore Identity Metadata
</title>
<programlisting>
import org.apache.openjpa.persistence.*;
@Entity
@DataStoreId
public class LineItem {
... no @Id fields declared ...
}
</programlisting>
</example>
<para>
Internally, OpenJPA uses the public
<ulink url="../javadoc/org/apache/openjpa/util/Id.html"><classname>
org.apache.openjpa.util.Id</classname></ulink> class for datastore identity
objects. When writing OpenJPA plugins, you can manipulate datastore identity
objects by casting them to this class. You can also create your own <classname>
Id</classname> instances and pass them to any internal OpenJPA method that
expects an identity object.
</para>
<para>
In JPA, you will never see <classname>Id</classname> instances directly.
Instead, calling <classname>OpenJPAEntityManager.getObjectId</classname> on a
datastore identity object will return the <classname>Long</classname> surrogate
primary key value for that object. You can then use this value in calls to
<classname>EntityManager.find</classname> for subsequent lookups of the same
record.
</para>
</section>
<section id="ref_guide_pc_oid_entitypk">
<title>
Entities as Identity Fields
</title>
<indexterm zone="ref_guide_pc_oid_entitypk">
<primary>
identity
</primary>
<secondary>
application
</secondary>
<tertiary>
entity id fields
</tertiary>
</indexterm>
<para>
OpenJPA allows <literal>ManyToOne</literal> and <literal>OneToOne</literal>
relations to be identity fields. To identify a relation field as an identity
field, simply annotate it with both the <literal>@ManyToOne</literal> or
<literal>@OneToOne</literal> relation annotation and the <literal>@Id</literal>
identity annotation.
</para>
<para>
When finding an entity identified by a relation, pass the id of the
<emphasis>relation</emphasis> to the <methodname>EntityManager.find</methodname>
method:
</para>
<example id="ref_guide_pc_oid_entitypk_simplefind">
<title>
Finding an Entity with an Entity Identity Field
</title>
<programlisting>
public Delivery createDelivery(Order order) {
Delivery delivery = new Delivery();
delivery.setId(order);
delivery.setDelivered(new Date());
return delivery;
}
public Delivery findDelivery(EntityManager em, Order order) {
// use the identity of the related instance
return em.find(Delivery.class, order.getId());
}
</programlisting>
</example>
<para>
When your entity has multiple identity fields, at least one of which is a
relation to another entity, you can use an identity class (see
<xref linkend="jpa_overview_pc_identitycls"/> in the JPA Overview), or
an embedded identity object. Identity class fields corresponding to
entity identity fields should be of the same type as the related entity's
identity. If an embedded identity object is used, you must annotate the
relation field with both the <literal>@ManyToOne</literal> or
<literal>@OneToOne</literal> relation annotation and the
<literal>@MapsId</literal> annotation.
</para>
<example id="ref_guide_pc_oid_entitypk_idcls">
<title>
Id Class for Entity Identity Fields
</title>
<programlisting>
@Entity
public class Order {
@Id
private long id;
...
}
/**
* LineItem uses a compound primary key. Part of the compound key
* LineItemId is relation or reference to Order instance.
**/
@Entity
@IdClass(LineItemId.class)
public class LineItem {
@Id
private int index;
@Id
@ManyToOne
private Order order;
...
}
public class LineItemId {
public int index;
public long order; // same type as identity of Order i.e Order.id
// also the variable name must match the name of the
// variable in LineItem that refers to Order.
}
</programlisting>
</example>
<para>
In the example above, if <classname>Order</classname> had used an identity
class <classname>OrderId</classname> in place of a simple <classname>long
</classname> value, then the <literal>LineItemId.order</literal> field would
have been of type <classname>OrderId</classname>.
</para>
<example id="ref_guide_pc_oid_entitypk_embeded_id">
<title>
Embedded Id for Entity Identity Fields
</title>
<programlisting>
@Entity
public class Order {
@Id
private long id;
...
}
/**
* LineItem uses a compound primary key. Part of the compound key
* LineItemId is relation or reference to Order instance.
**/
@Entity
public class LineItem {
@EmbeddedId LineItemId id;
@ManyToOne
@MapsId("orderId") // The value element of the MapsId annotation
// must be used to specify the name of the primary
// key attribute to which the relationship
// corresponds. If the primary key referenced by
// the relationship attribute is of the same Java
// type as the dependent's primary key, then the
// value element is not specified.
private Order order;
...
}
@Embeddable
public class LineItemId {
public int index;
public long orderId;
}
</programlisting>
</example>
<para>
In the example above, the <classname>LineItem</classname> uses an embedded id to
represent its primary key. The primary key attribute corresponding to the
relationship in the <classname>LineItemId</classname> must be of the same
type as the primary key of the <classname>Order</classname>. The
<literal>MapsId</literal> annotation must be applied to the relationship
field <literal>LineItem.order</literal>.
</para>
</section>
<section id="ref_guide_pc_oid_application">
<title>
Application Identity Tool
</title>
<indexterm zone="ref_guide_pc_oid_application">
<primary>
identity
</primary>
<secondary>
application
</secondary>
<tertiary>
application identity tool
</tertiary>
</indexterm>
<indexterm zone="ref_guide_pc_oid_application">
<primary>
application identity tool
</primary>
</indexterm>
<para>
If you choose to use application identity, you may want to take advantage of
OpenJPA's application identity tool. The application
identity tool generates Java code implementing the identity class for any
persistent type using application identity. The code satisfies all the
requirements the specification places on identity classes. You can use it as-is,
or simply use it as a starting point, editing it to meet your needs.
</para>
<para>
Before you can run the application identity tool on a persistent class, the
class must be compiled and must have complete metadata. All primary key fields
must be marked as such in the metadata.
</para>
<para>
In JPA metadata, do not attempt to specify the <literal>@IdClass</literal>
annotation unless you are using the application identity tool to overwrite an
existing identity class. Attempting to set the value of the <literal>@IdClass
</literal> to a non-existent class will prevent your persistent class from
compiling. Instead, use the <literal>-name</literal> or <literal>-suffix
</literal> options described below to tell OpenJPA what name to give your
generated identity class. Once the application identity tool has generated the
class code, you can set the <literal>@IdClass</literal> annotation.
</para>
<para>
The application identity tool can be invoked via its Java class,
<ulink url="../javadoc/org/apache/openjpa/enhance/ApplicationIdTool">
<classname>org.apache.openjpa.enhance.ApplicationIdTool</classname></ulink>.
</para>
<note>
<para>
<xref linkend="ref_guide_integration_appidtool"/> describes the
application identity tool's Ant task.
</para>
</note>
<example id="ref_guide_pc_appid_appidtool">
<title>
Using the Application Identity Tool
</title>
<programlisting>
java org.apache.openjpa.enhance.ApplicationIdTool -s Id Magazine.java
</programlisting>
</example>
<para>
The application identity tool accepts the standard set of command-line arguments
defined by the configuration framework (see
<xref linkend="ref_guide_conf_devtools"/>), including code formatting
flags described in <xref linkend="ref_guide_conf_devtools_format"/>. It
also accepts the following arguments:
</para>
<itemizedlist>
<listitem>
<para>
<literal>-directory/-d &lt;output directory&gt;</literal>: Path to the output
directory. If the directory does not match the generated oid class' package, the
package structure will be created beneath the directory. If not specified, the
tool will first try to find the directory of the <filename>.java</filename> file
for the persistence-capable class, and failing that will use the current
directory.
</para>
</listitem>
<listitem>
<para>
<literal>-ignoreErrors/-i &lt;true/t | false/f&gt;</literal>: If <literal>false
</literal>, an exception will be thrown if the tool is run on any class that
does not use application identity, or is not the base class in the inheritance
hierarchy (recall that subclasses never define the application identity class;
they inherit it from their persistent superclass).
</para>
</listitem>
<listitem>
<para>
<literal>-token/-t &lt;token&gt;</literal>: The token to use to separate
stringified primary key values in the string form of the object id. This option
is only used if you have multiple primary key fields. It defaults to "::".
</para>
</listitem>
<listitem>
<para>
<literal>-name/-n &lt;id class name&gt;</literal>: The name of the identity
class to generate. If this option is specified, you must run the tool on exactly
one class. If the class metadata already names an object id class, this option
is ignored. If the name is not fully qualified, the persistent class' package is
prepended to form the qualified name.
</para>
</listitem>
<listitem>
<para>
<literal>-suffix/-s &lt;id class suffix&gt;</literal>: A string to suffix each
persistent class name with to form the identity class name. This option is
overridden by <literal>-name</literal> or by any object id class specified in
metadata.
</para>
</listitem>
</itemizedlist>
<para>
Each additional argument to the tool must be one of the following:
</para>
<itemizedlist>
<listitem>
<para>
The full name of a persistent class.
</para>
</listitem>
<listitem>
<para>
The .java file for a persistent class.
</para>
</listitem>
<listitem>
<para>
The <filename>.class</filename> file of a persistent class.
</para>
</listitem>
</itemizedlist>
<para>
If you do not supply any arguments to the tool, it will act on the classes in
your persistent classes list (see <xref linkend="ref_guide_pc_pcclasses"/>).
</para>
</section>
<section id="ref_guide_pc_oid_pkgen_autoinc">
<title>
Autoassign / Identity Strategy Caveats
</title>
<indexterm zone="ref_guide_pc_oid_pkgen_autoinc">
<primary>
datastore identity
</primary>
<secondary>
autoassign strategy
</secondary>
</indexterm>
<indexterm zone="ref_guide_pc_oid_pkgen_autoinc">
<primary>
datastore identity
</primary>
<secondary>
autoassign strategy
</secondary>
</indexterm>
<indexterm zone="ref_guide_pc_oid_pkgen_autoinc">
<primary>
persistent fields
</primary>
<secondary>
autoassign strategy
</secondary>
</indexterm>
<para>
<xref linkend="jpa_overview_meta_gen"/> explains how to use JPA's
<literal>IDENTITY</literal> generation type to automatically assign field
values. However, here are some additional caveats you should be aware of when
using <literal>IDENTITY</literal> generation:
</para>
<orderedlist>
<listitem>
<para>
Your database must support auto-increment / identity columns, or some equivalent
(see <xref linkend="ref_guide_dbsetup_dbsupport_oracle"/> for how to
configure a combination of triggers and sequences to fake auto-increment support
in Oracle database).
</para>
</listitem>
<listitem>
<para>
Auto-increment / identity columns must be an integer or long integer type.
</para>
</listitem>
<listitem>
<para>
Databases support auto-increment / identity columns to varying degrees. Some do
not support them at all. Others only allow a single such column per table, and
require that it be the primary key column. More lenient databases may allow
non-primary key auto-increment columns, and may allow more than one per table.
See your database documentation for details.
</para>
</listitem>
</orderedlist>
</section>
</section>
<section id="ref_guide_inverses">
<title>
Managed Inverses
</title>
<indexterm zone="ref_guide_inverses">
<primary>
bidirectional relations
</primary>
<secondary>
automatic management
</secondary>
</indexterm>
<para>
Bidirectional relations are an essential part of data modeling.
<xref linkend="jpa_overview_mapping"/> in the JPA Overview explains how to
use the <literal>mappedBy</literal> annotation attribute to form bidirectional
relations that also share datastore storage in JPA.
</para>
<para>
OpenJPA also allows you to define purely logical bidirectional relations. The
<ulink url="../javadoc/org/apache/openjpa/persistence/InverseLogical.html">
<classname>org.apache.openjpa.persistence.InverseLogical</classname></ulink>
annotation names a logical inverse in JPA metadata.
</para>
<example id="ref_guide_inverses_logicalex">
<title>
Specifying Logical Inverses
</title>
<para>
<literal>Magazine.coverPhoto</literal> and <literal>Photograph.mag</literal> are
each mapped to different foreign keys in their respective tables, but form a
logical bidirectional relation. Only one of the fields needs to declare the
other as its logical inverse, though it is not an error to set the logical
inverse of both fields.
</para>
<programlisting>
import org.apache.openjpa.persistence.*;
@Entity
public class Magazine {
@OneToOne
private Photograph coverPhoto;
...
}
@Entity
public class Photograph {
@OneToOne
@InverseLogical("coverPhoto")
private Magazine mag;
...
}
</programlisting>
</example>
<para>
Java does not provide any native facilities to ensure that both sides of a
bidirectional relation remain consistent. Whenever you set one side of the
relation, you must manually set the other side as well.
</para>
<para>
By default, OpenJPA behaves the same way. OpenJPA does not automatically
propagate changes from one field in bidirectional relation to the other field.
This is in keeping with the philosophy of transparency, and also provides higher
performance, as OpenJPA does not need to analyze your object graph to correct
inconsistent relations.
</para>
<para>
<indexterm>
<primary>
InverseManager
</primary>
</indexterm>
If convenience is more important to you than strict transparency, however, you
can enable inverse relation management in OpenJPA. Set the
<link linkend="openjpa.InverseManager"><classname>openjpa.InverseManager
</classname></link> plugin property to <literal>true</literal> for standard
management. Under this setting, OpenJPA detects changes to either side of a
bidirectional relation (logical or physical), and automatically sets the other
side appropriately on flush.
</para>
<example id="ref_guide_inversesex">
<title>
Enabling Managed Inverses
</title>
<programlisting>
&lt;property name="openjpa.InverseManager" value="true"/&gt;
</programlisting>
</example>
<para>
The inverse manager has options to log a warning or throw an exception when it
detects an inconsistent bidirectional relation, rather than correcting it. To
use these modes, set the manager's <literal>Action</literal> property to
<literal>warn</literal> or <literal>exception</literal>, respectively.
</para>
<para>
By default, OpenJPA excludes <link linkend="ref_guide_pc_scos_proxy_lrs"> large
result set fields</link> from management. You can force large result set fields
to be included by setting the <literal>ManageLRS</literal> plugin property to
<literal>true</literal>.
</para>
<example id="ref_guide_inverses_logex">
<title>
Log Inconsistencies
</title>
<programlisting>
&lt;property name="openjpa.InverseManager" value="true(Action=warn)"/&gt;
</programlisting>
</example>
</section>
<section id="ref_guide_pc_scos">
<title>
Persistent Fields
</title>
<indexterm zone="ref_guide_pc_scos">
<primary>
persistent fields
</primary>
</indexterm>
<para>
OpenJPA enhances the specification's support for persistent fields in many ways.
This section documents aspects of OpenJPA's persistent field handling that may
affect the way you design your persistent classes.
</para>
<section id="ref_guide_pc_scos_restore">
<title>
Restoring State
</title>
<indexterm zone="ref_guide_pc_scos">
<primary>
persistent fields
</primary>
<secondary>
field rollback
</secondary>
</indexterm>
<indexterm zone="ref_guide_pc_scos_restore">
<primary>
RestoreState
</primary>
</indexterm>
<para>
While the JPA specification says that you should not use rolled back objects,
such objects are perfectly valid in OpenJPA. You can control whether the
objects' managed state is rolled back to its pre-transaction values with the
<link linkend="openjpa.RestoreState"><literal>openjpa.RestoreState</literal>
</link> configuration property. <literal>none</literal> does not roll back state
(the object becomes hollow, and will re-load its state the next time it is
accessed), <literal>immutable</literal> restores immutable values (primitives,
primitive wrappers, strings) and clears mutable values so that they are reloaded
on next access, and <literal>all</literal> restores all managed values to their
pre-transaction state.
</para>
</section>
<section id="ref_guide_pc_scos_order">
<title>
Typing and Ordering
</title>
<indexterm zone="ref_guide_pc_scos_order">
<primary>
persistent fields
</primary>
<secondary>
comparators
</secondary>
</indexterm>
<para>
When loading data into a field, OpenJPA examines the value you assign the field
in your declaration code or in your no-args constructor. If the field value's
type is more specific than the field's declared type, OpenJPA uses the value
type to hold the loaded data. OpenJPA also uses the comparator you've
initialized the field with, if any. Therefore, you can use custom comparators on
your persistent field simply by setting up the comparator and using it in your
field's initial value.
</para>
<example id="ref_guide_pc_scos_order_initialvals">
<title>
Using Initial Field Values
</title>
<para>
Though the annotations are left out for simplicity, assume <literal>
employeesBySal</literal> and <literal>departments</literal> are persistent
fields in the class below.
</para>
<programlisting>
public class Company {
// OpenJPA will detect the custom comparator in the initial field value
// and use it whenever loading data from the database into this field
private Collection employeesBySal = new TreeSet(new SalaryComparator());
private Map departments;
public Company {
// or we can initialize fields in our no-args constructor; even though
// this field is declared type Map, OpenJPA will detect that it's
// actually a TreeMap and use natural ordering for loaded data
departments = new TreeMap();
}
// rest of class definition...
}
</programlisting>
</example>
</section>
<section id="ref_guide_pc_calendar_timezone">
<title>
Calendar Fields and TimeZones
</title>
<indexterm zone="ref_guide_pc_calendar_timezone">
<primary>
persistent fields
</primary>
<secondary>
calendar
</secondary>
</indexterm>
<para>
OpenJPA's support for the <classname>java.util.Calendar</classname> type will
store only the <classname>Date</classname> part of the field, not the
<classname>TimeZone</classname> associated with the field. When loading the date
into the <classname>Calendar</classname> field, OpenJPA will use the <classname>
TimeZone</classname> that was used to initialize the field.
</para>
</section>
<section id="ref_guide_pc_scos_proxy">
<title>
Proxies
</title>
<indexterm zone="ref_guide_pc_scos_proxy">
<primary>
proxies
</primary>
</indexterm>
<indexterm>
<primary>
persistent fields
</primary>
<secondary>
proxies
</secondary>
<see>
proxies
</see>
</indexterm>
<para>
At runtime, the values of all mutable second class object fields in persistent
and transactional objects are replaced with implementation-specific proxies. On
modification, these proxies notify their owning instance that they have been
changed, so that the appropriate updates can be made on the datastore.
</para>
<section id="ref_guide_pc_scos_proxy_smart">
<title>
Smart Proxies
</title>
<indexterm zone="ref_guide_pc_scos_proxy_smart">
<primary>
proxies
</primary>
<secondary>
smart
</secondary>
</indexterm>
<para>
Most proxies only track whether or not they have been modified. Smart proxies
for collection and map fields, however, keep a record of which elements have
been added, removed, and changed. This record enables the OpenJPA runtime to
make more efficient database updates on these fields.
</para>
<para>
When designing your persistent classes, keep in mind that you can optimize for
OpenJPA smart proxies by using fields of type <classname>java.util.Set
</classname>, <classname>java.util.TreeSet</classname>, and <classname>
java.util.HashSet</classname> for your collections whenever possible. Smart
proxies for these types are more efficient than proxies for <classname>
List</classname>s. You can also design your own smart proxies to further
optimize OpenJPA for your usage patterns. See the section on
<link linkend="ref_guide_pc_scos_proxy_custom">custom proxies</link> for
details.
</para>
</section>
<section id="ref_guide_pc_scos_proxy_lrs">
<title>
Large Result Set Proxies
</title>
<indexterm zone="ref_guide_pc_scos_proxy_lrs">
<primary>
proxies
</primary>
<secondary>
large result set
</secondary>
</indexterm>
<indexterm zone="ref_guide_pc_scos_proxy_lrs">
<primary>
large result sets
</primary>
<secondary>
fields
</secondary>
</indexterm>
<para>
Under standard ORM behavior, traversing a persistent collection or map field
brings the entire contents of that field into memory. Some persistent fields,
however, might represent huge amounts of data, to the point that attempting to
fully instantiate them can overwhelm the JVM or seriously degrade performance.
</para>
<para>
OpenJPA uses special proxy types to represent these "large result set" fields.
OpenJPA's large result set proxies do not cache any data in memory. Instead,
each operation on the proxy offloads the work to the database and returns the
proper result. For example, the <methodname>contains</methodname> method of a
large result set collection will perform a <literal> SELECT COUNT(*)</literal>
query with the proper <literal>WHERE</literal> conditions to find out if the
given element exists in the database's record of the collection. Similarly, each
time you obtain an iterator OpenJPA performs the proper query using the current
<link linkend="ref_guide_dbsetup_lrs">large result set settings</link>, as
discussed in the <link linkend="ref_guide_dbsetup">JDBC</link> chapter. As you
invoke <methodname>Iterator.next</methodname>, OpenJPA instantiates the result
objects on-demand.
</para>
<para>
You can free the resources used by a large result set iterator by passing it to
the static <link linkend="ref_guide_runtime_openjpapersistence"><methodname>
OpenJPAPersistence.close</methodname></link> method.
</para>
<example id="ref_guide_pc_scos_proxy_lrs_itr">
<title>
Using a Large Result Set Iterator
</title>
<programlisting>
import org.apache.openjpa.persistence.*;
...
Collection employees = company.getEmployees(); // employees is a lrs collection
Iterator itr = employees.iterator();
while (itr.hasNext())
process((Employee) itr.next());
OpenJPAPersistence.close(itr);
</programlisting>
</example>
<para>
You can also add and remove from large result set proxies, just as with standard
fields. OpenJPA keeps a record of all changes to the elements of the proxy,
which it uses to make sure the proper results are always returned from
collection and map methods, and to update the field's database record on commit.
</para>
<para>
In order to use large result set proxies in JPA, add the
<ulink url="../javadoc/org/apache/openjpa/persistence/LRS.html"><classname>
org.apache.openjpa.persistence.LRS</classname></ulink> annotation to the
persistent field.
</para>
<para>
The following restrictions apply to large result set fields:
</para>
<itemizedlist>
<listitem>
<para>
The field must be declared as either a <classname>java.util.Collection
</classname> or <classname>java.util.Map</classname>. It cannot be declared as
any other type, including any sub-interface of collection or map, or any
concrete collection or map class.
</para>
</listitem>
<listitem>
<para>
The field cannot have an externalizer (see
<xref linkend="ref_guide_pc_extern"/>).
</para>
</listitem>
<listitem>
<para>
Because they rely on their owning object for context, large result set proxies
cannot be transferred from one persistent field to another. The following code
would result in an error on commit:
</para>
<programlisting>
Collection employees = company.getEmployees() // employees is a lrs collection
company.setEmployees(null);
anotherCompany.setEmployees(employees);
</programlisting>
</listitem>
</itemizedlist>
<example id="ref_guide_pc_scos_proxy_lrs_extension">
<title>
Marking a Large Result Set Field
</title>
<programlisting>
import org.apache.openjpa.persistence.*;
@Entity
public class Company {
@ManyToMany
@LRS private Collection&lt;Employee&gt; employees;
...
}
</programlisting>
</example>
</section>
<section id="ref_guide_pc_scos_proxy_custom">
<title>
Custom Proxies
</title>
<indexterm zone="ref_guide_pc_scos_proxy_custom">
<primary>
proxies
</primary>
<secondary>
custom
</secondary>
</indexterm>
<indexterm zone="ref_guide_pc_scos_proxy_custom">
<primary>
proxies
</primary>
<secondary>
ProxyManager
</secondary>
</indexterm>
<para>
OpenJPA manages proxies through the
<ulink url="../javadoc/org/apache/openjpa/util/ProxyManager.html"><classname>
org.apache.openjpa.util.ProxyManager</classname></ulink> interface. OpenJPA
includes a default proxy manager, the <classname>
org.apache.openjpa.util.ProxyManagerImpl</classname> (with a plugin alias name
of <literal>default</literal>), that will meet the needs of most users. The
default proxy manager understands the following configuration properties:
</para>
<itemizedlist>
<listitem>
<para>
<literal>TrackChanges</literal>: Whether to use
<link linkend="ref_guide_pc_scos_proxy_smart">smart proxies</link>. Defaults to
<literal>true</literal>.
</para>
</listitem>
<listitem>
<para>
<literal>AssertAllowedType</literal>: Whether to immediately throw an exception
if you attempt to add an element to a collection or map that is not assignable
to the element type declared in metadata. Defaults to <literal>false</literal>.
</para>
</listitem>
<listitem>
<para>
<literal>DelayCollectionLoading</literal>: Whether to delay loading elements of a
lazily loaded collection. Delay loading allows non-indexed add and remove
operations to occur without prior loading of the collection from the data store. This can
improve performance of some applications by allowing them to perform simple add or remove
operations on collections without requiring them to be loaded. Delayed proxies are
loaded when an operation is performed that requires loading, such
as iteration, size, serialization, and indexOf. They can also be loaded by casting the
proxy to a <ulink url="../javadoc/org/apache/openjpa/util/DelayedProxy.html"><classname>
org.apache.openjpa.util.DelayedProxy</classname></ulink> and invoking the
<methodname>load</methodname> method. If a broker factory is available after detaching the owning
entity, a collection may be available for delayed loading after the persistence context has been
cleared. In post-detachment, entities that are loaded are not associated with a persistence context.
Defaults to <literal>false</literal>.
</para>
</listitem>
</itemizedlist>
<para>
The default proxy manager can proxy the standard methods of any
<classname>Collection</classname>, <classname>List</classname>,
<classname>Map</classname>, <classname>Queue</classname>,
<classname>Date</classname>, or <classname>Calendar</classname> class,
including custom implementations. It can also proxy custom classes whose
accessor and mutator methods follow JavaBean naming conventions. Your custom
types must, however, meet the following criteria:
</para>
<itemizedlist>
<listitem>
<para>
Custom container types must have a public no-arg constructor or a public
constructor that takes a single <classname>Comparator</classname> parameter.
</para>
</listitem>
<listitem>
<para>
Custom date types must have a public no-arg constructor or a public
constructor that takes a single <classname>long</classname> parameter
representing the current time.
</para>
</listitem>
<listitem>
<para>
Other custom types must have a public no-arg constructor or a public copy
constructor. If a custom types does not have a copy constructor, it must be
possible to fully copy an instance A by creating a new instance B and calling
each of B's setters with the value from the corresponding getter on A.
</para>
</listitem>
</itemizedlist>
<para>
If you have custom classes that must be proxied and do not meet these
requirements, OpenJPA allows you to define your own proxy classes and
your own proxy manager. See the <literal>openjpa.util</literal> package
<ulink url="../javadoc/">Javadoc</ulink> for details on the interfaces involved,
and the utility classes OpenJPA provides to assist you.
</para>
<para>
You can plug your custom proxy manager into the OpenJPA runtime through the
<link linkend="openjpa.ProxyManager"><literal> openjpa.ProxyManager</literal>
</link> configuration property.
</para>
<example id="ref_guide_pc_scos_proxy_custom_ex">
<title>
Configuring the Proxy Manager
</title>
<programlisting>
&lt;property name="openjpa.ProxyManager" value="TrackChanges=false"/&gt;
</programlisting>
</example>
</section>
<section id="ref_guide_pc_scos_proxy_serial">
<title>
Serialization
</title>
<indexterm zone="ref_guide_pc_scos_proxy_serial">
<primary>
proxies
</primary>
<secondary>
serialization
</secondary>
</indexterm>
<indexterm zone="ref_guide_pc_scos_proxy_serial">
<primary>
proxies
</primary>
<secondary>
DetachedStateField
</secondary>
</indexterm>
<para>
When objects are serialized, the <literal>DetachedStateField</literal> in
section <xref linkend="ref_guide_detach_state"/>
will be used to help determine when build time proxies will be removed.
If runtime created proxies are being used (proxies not supplied by OpenJPA)
or if an entity has already been detached, then any found proxies will be
removed during serialization.
</para>
<itemizedlist>
<listitem>
<para>
<literal>transient</literal>: 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 or the OpenJPA runtime.
All proxies will be removed during serialization. This is the default.
</para>
</listitem>
<listitem>
<para>
<literal>true</literal>: 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 runtime.
No OpenJPA provided proxies will be removed during serialization.
</para>
</listitem>
<listitem>
<para>
<literal>false</literal>: Do not use a detached state field.
All proxies will be removed during serialization.
</para>
</listitem>
</itemizedlist>
</section>
</section>
<section id="ref_guide_pc_extern">
<title>
Externalization
</title>
<indexterm zone="ref_guide_pc_extern">
<primary>
externalization
</primary>
</indexterm>
<indexterm>
<primary>
persistent fields
</primary>
<secondary>
externalization
</secondary>
<see>
externalization
</see>
</indexterm>
<para>
OpenJPA offers the ability to write
<link linkend="ref_guide_mapping_custom_field">custom field mappings</link> in
order to have complete control over the mechanism with which fields are stored,
queried, and loaded from the datastore. Often, however, a custom mapping is
overkill. There is often a simple transformation from a Java field value to its
database representation. Thus, OpenJPA provides the externalization service.
Externalization allows you to specify methods that will externalize a field
value to its database equivalent on store and then rebuild the value from its
externalized form on load.
</para>
<note>
<para>
Fields of embeddable classes used for <literal>@EmbeddedId</literal> values in
JPA cannot have externalizers.
</para>
</note>
<para>
The OpenJPA
<ulink url="../javadoc/org/apache/openjpa/persistence/Externalizer.html">
<classname>org.apache.openjpa.persistence.Externalizer</classname></ulink>
annotation sets the name of a method that will be invoked to convert
the field into its external form for database storage. You can specify either
the name of a non-static method, which will be invoked on the field value, or a
static method, which will be invoked with the field value as a parameter. Each
method can also take an optional
<ulink url="../javadoc/org/apache/openjpa/kernel/StoreContext.html"><classname>
StoreContext</classname></ulink> parameter for access to a persistence context.
The return value of the method is the field's external form. By default, OpenJPA
assumes that all named methods belong to the field value's class (or its
superclasses). You can, however, specify static methods of other classes using
the format <literal>&lt;class-name&gt;.&lt;method-name&gt;</literal>.
</para>
<para>
Given a field of type <classname>CustomType</classname> that externalizes to a
string, the table below demonstrates several possible externalizer methods and
their corresponding metadata extensions.
</para>
<table tocentry="1">
<title>
Externalizer Options
</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<colspec colname="method"/>
<colspec colname="extension"/>
<thead>
<row>
<entry colname="method">
Method
</entry>
<entry colname="extension">
Extension
</entry>
</row>
</thead>
<tbody>
<row>
<entry colname="method">
<literal>
public String CustomType.toString()
</literal>
</entry>
<entry colname="extension">
<literal>
@Externalizer("toString")
</literal>
</entry>
</row>
<row>
<entry colname="method">
<literal>
public String CustomType.toString(StoreContext ctx)
</literal>
</entry>
<entry colname="extension">
<literal>
@Externalizer("toString")
</literal>
</entry>
</row>
<row>
<entry colname="method">
<literal>
public static String AnyClass.toString(CustomType ct)
</literal>
</entry>
<entry colname="extension">
<literal>
@Externalizer("AnyClass.toString")
</literal>
</entry>
</row>
<row>
<entry colname="method">
<literal>
public static String AnyClass.toString(CustomType ct, StoreContext ctx)
</literal>
</entry>
<entry colname="extension">
<literal>
@Externalizer("AnyClass.toString")
</literal>
</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
The OpenJPA
<ulink url="../javadoc/org/apache/openjpa/persistence/Factory.html"><classname>
org.apache.openjpa.persistence.Factory</classname></ulink> annotation
contains the name of a method that will be invoked to instantiate the field from
the external form stored in the database. Specify a static method name. The
method will be invoked with the externalized value and must return an
instance of the field type. The method can also take an optional
<ulink url="../javadoc/org/apache/openjpa/kernel/StoreContext.html"><classname>
StoreContext</classname></ulink> parameter for access to a persistence context.
If a factory is not specified, OpenJPA will use the constructor of the field
type that takes a single argument of the external type, or will throw an
exception if no constructor with that signature exists.
</para>
<para>
Given a field of type <classname>CustomType</classname> that externalizes to a
string, the table below demonstrates several possible factory methods and their
corresponding metadata extensions.
</para>
<table tocentry="1">
<title>
Factory Options
</title>
<tgroup cols="2" align="left" colsep="1" rowsep="1">
<colspec colname="method"/>
<colspec colname="extension"/>
<thead>
<row>
<entry colname="method">
Method
</entry>
<entry colname="extension">
Extension
</entry>
</row>
</thead>
<tbody>
<row>
<entry colname="method">
<literal>
public CustomType(String str)
</literal>
</entry>
<entry colname="extension">
none
</entry>
</row>
<row>
<entry colname="method">
<literal>
public static CustomType CustomType.fromString(String str)
</literal>
</entry>
<entry colname="extension">
<literal>
@Factory("fromString")
</literal>
</entry>
</row>
<row>
<entry colname="method">
<literal>
public static CustomType CustomType.fromString(String str, StoreContext ctx)
</literal>
</entry>
<entry colname="extension">
<literal>
@Factory("fromString")
</literal>
</entry>
</row>
<row>
<entry colname="method">
<literal>
public static CustomType AnyClass.fromString(String str)
</literal>
</entry>
<entry colname="extension">
<literal>
@Factory("AnyClass.fromString")
</literal>
</entry>
</row>
<row>
<entry colname="method">
<literal>
public static CustomType AnyClass.fromString(String str, StoreContext ctx)
</literal>
</entry>
<entry colname="extension">
<literal>
@Factory("AnyClass.fromString")
</literal>
</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
If your externalized field is not a standard persistent type, you must
explicitly mark it persistent. In OpenJPA, you can force a persistent field
by annotating it with <link linkend="ref_guide_meta_jpa_persistent"><classname>
org.apache.openjpa.persistence.Persistent</classname></link> annotation.
</para>
<note>
<para>
If your custom field type is mutable and is not a standard collection, map, or
date class, OpenJPA will not be able to detect changes to the field. You must
mark the field dirty manually, or create a custom field proxy.
See
<ulink url="../javadoc/org/apache/openjpa/persistence/OpenJPAEntityManager.html">
<methodname>OpenJPAEntityManager.dirty</methodname></ulink> for how to mark a
field dirty manually in JPA.
See <xref linkend="ref_guide_pc_scos_proxy"/> for a discussion of proxies.
</para>
</note>
<para>
You can externalize a field to virtually any value that is supported by
OpenJPA's field mappings (embedded relations are the exception; you must declare
your field to be a persistence-capable type in order to embed it). This means
that a field can externalize to something as simple as a primitive, something as
complex as a collection or map of entities, or anything in
between. If you do choose to externalize to a collection or map, OpenJPA
recognizes a family of metadata extensions for specifying type information for the
externalized form of your fields - see <xref linkend="type"/>. If the
external form of your field is an entity object or contains entities, OpenJPA
will correctly include the objects in its persistence-by-reachability
algorithms and its delete-dependent algorithms.
</para>
<para>
The example below demonstrates a few forms of externalization.
</para>
<example id="ref_guide_pc_externex">
<title>
Using Externalization
</title>
<programlisting>
import org.apache.openjpa.persistence.*;
@Entity
public class Magazine {
// use Class.getName and Class.forName to go to/from strings
@Persistent
@Externalizer("getName")
@Factory("forName")
private Class cls;
// use URL.getExternalForm for externalization. no factory;
// we can rely on the URL string constructor
@Persistent
@Externalizer("toExternalForm")
private URL url;
// use our custom methods
@Persistent
@Externalizer("Magazine.authorsFromCustomType")
@Factory("Magazine.authorsToCustomType")
@ElementType(Author.class)
private CustomType customType;
public static Collection authorsFromCustomType(CustomType customType) {
... logic to pack custom type into a list of authors ...
}
public static CustomType authorsToCustomType(Collection authors) {
... logic to create custom type from a collection of authors ...
}
...
}
</programlisting>
</example>
<para>
<indexterm>
<primary>
externalization
</primary>
<secondary>
queries
</secondary>
</indexterm>
You can query externalized fields using parameters. Pass in a value of the field
type when executing the query. OpenJPA will externalize the parameter using the
externalizer method named in your metadata, and compare the externalized
parameter with the value stored in the database. As a shortcut, OpenJPA also
allows you to use parameters or literals of the field's externalized type in
queries, as demonstrated in the example below.
</para>
<note>
<para>
Currently, queries are limited to fields that externalize to a primitive,
primitive wrapper, string, or date types, due to constraints on query syntax.
</para>
</note>
<example id="ref_guide_pc_extern_queryex">
<title>
Querying Externalization Fields
</title>
<para>
Assume the <classname>Magazine</classname> class has the same fields as in the
previous example.
</para>
<programlisting>
// you can query using parameters
Query q = em.createQuery("select m from Magazine m where m.url = :u");
q.setParameter("u", new URL("http://www.solarmetric.com"));
List results = q.getResultList();
// or as a shortcut, you can use the externalized form directly
q = em.createQuery("select m from Magazine m where m.url = 'http://www.solarmetric.com'");
results = q.getResultList();
</programlisting>
</example>
<section id="ref_guide_pc_extern_values">
<title>
External Values
</title>
<indexterm zone="ref_guide_pc_extern_values">
<primary>
externalization
</primary>
<secondary>
external values
</secondary>
</indexterm>
<para>
Externalization often takes simple constant values and transforms them to
constant values of a different type. An example would be storing a <classname>
boolean</classname> field as a <classname>char</classname>, where <literal>true
</literal> and <literal>false</literal> would be stored in the database as
<literal>'T'</literal> and <literal>'F'</literal> respectively.
</para>
<para>
OpenJPA allows you to define these simple translations in metadata, so that the
field behaves as in <link linkend="ref_guide_pc_extern">full-fledged
externalization</link> without requiring externalizer and factory methods.
External values supports translation of pre-defined simple types (primitives,
primitive wrappers, and Strings), to other pre-defined simple values.
</para>
<para>
Use the OpenJPA
<ulink url="../javadoc/org/apache/openjpa/persistence/ExternalValues.html">
<classname>org.apache.openjpa.persistence.ExternalValues</classname></ulink>
annotation to define external value translations. The values are
defined in a format similar to that of <link linkend="ref_guide_conf_plugins">
configuration plugins</link>, except that the value pairs represent Java and
datastore values. To convert the Java boolean values of <literal>true</literal>
and <literal>false</literal> to the character values <literal>T</literal> and
<literal>F</literal>, for example, you would use the extension value: <literal>
true=T,false=F</literal>.
</para>
<para>
If the type of the datastore value is different from the field's type, use the
<ulink url="../javadoc/org/apache/openjpa/persistence/Type.html">
<classname>org.apache.openjpa.persistence.Type</classname></ulink> annotation
to define the datastore type.
</para>
<example id="externvalues_ex">
<title>
Using External Values
</title>
<para>
This example uses external value translation to transform a string field to an
integer in the database.
</para>
<programlisting>
public class Magazine {
@ExternalValues({"SMALL=5", "MEDIUM=8", "LARGE=10"})
@Type(int.class)
private String sizeWidth;
...
}
</programlisting>
</example>
</section>
</section>
</section>
<section id="ref_guide_fetch">
<title>
Fetch Groups
</title>
<indexterm zone="ref_guide_fetch">
<primary>
fetch groups
</primary>
</indexterm>
<para>
Fetch groups are sets of fields that load together. They can be used to pool
together associated fields in order to provide performance improvements over
standard data fetching. Specifying fetch groups allows for tuning of lazy
loading and eager fetching behavior.
</para>
<para>
The JPA Overview's <xref linkend="jpa_overview_meta_fetch"/> describes how
to use JPA metadata annotations to control whether a field is fetched eagerly or
lazily. Fetch groups add a dynamic aspect to this standard ability. As you will
see, OpenJPA's JPA extensions allow you can add and remove fetch groups at
runtime to vary the sets of fields that are eagerly loaded.
</para>
<section id="ref_guide_fetch_custom">
<title>
Custom Fetch Groups
</title>
<para>
OpenJPA places any field that is eagerly loaded according to the JPA metadata
rules into the built-in <emphasis>default</emphasis> fetch group. As its name
implies, the default fetch group is active by default. You may also
define your own named fetch groups and activate or deactivate them at runtime,
as described later in this chapter. OpenJPA will eagerly-load the fields in all
active fetch groups when loading objects from the datastore.
</para>
<para>
You create fetch groups with the
<ulink url="../javadoc/org/apache/openjpa/persistence/FetchGroup.html">
<classname>org.apache.openjpa.persistence.FetchGroup</classname></ulink>
annotation. If your class only has one custom fetch group, you can place this
annotation directly on the class declaration. Otherwise, use the
<ulink url="../javadoc/org/apache/openjpa/persistence/FetchGroups.html">
<classname>org.apache.openjpa.persistence.FetchGroups</classname></ulink>
annotation to declare an array of individual <classname>FetchGroup</classname>
values. The <classname>FetchGroup</classname> annotation has the following
properties:
</para>
<itemizedlist>
<listitem>
<para>
<literal>String name</literal>: The name of the fetch group. Fetch group names
are global, and are expected to be shared among classes. For example, a shopping
website may use a <emphasis>detail</emphasis> fetch group in each product class
to efficiently load all the data needed to display a product's "detail" page.
The website might also define a sparse <emphasis>list</emphasis> fetch group
containing only the fields needed to display a table of products, as in a search
result.
</para>
<para>
The following names are reserved for use by OpenJPA: <literal>default</literal>
, <literal>values</literal>, <literal>all</literal>, <literal>none</literal>,
and any name beginning with <literal>jdo</literal>, <literal>jpa</literal>, or
<literal>openjpa</literal>.
</para>
</listitem>
<listitem>
<para>
<literal>FetchAttribute[] attributes</literal>: The set of persistent fields or
properties in the fetch group.
</para>
</listitem>
<listitem>
<para>
<literal>String[] fetchGroups</literal>: Other fetch groups whose fields to
include in this group.
</para>
</listitem>
</itemizedlist>
<para>
As you might expect, listing a
<ulink url="../javadoc/org/apache/openjpa/persistence/FetchAttribute.html">
<classname>org.apache.openjpa.persistence.FetchAttribute</classname></ulink>
within a <classname>FetchGroup</classname> includes the corresponding persistent
field or property in the fetch group. Each <classname>FetchAttribute</classname>
has the following properties:
</para>
<itemizedlist>
<listitem>
<para>
<literal>String name</literal>: The name of the persistent field or property to
include in the fetch group.
</para>
</listitem>
<listitem>
<para>
<literal>recursionDepth</literal>: If the attribute represents a relation, the
maximum number of same-typed relations to eager-fetch from this field. Defaults
to 1. For example, consider an <classname>Employee</classname> class with a
<literal>manager</literal> field, also of type <classname>Employee</classname>.
When we load an <classname>Employee</classname> and the <literal>
manager</literal> field is in an active fetch group, the recursion depth (along
with the max fetch depth setting, described below) determines whether we only
retrieve the target <classname>Employee</classname> and his manager (depth 1),
or whether we also retrieve the manager's manager (depth 2), or the manager's
manager's manager (depth 3), etc. Use -1 for unlimited depth.
</para>
</listitem>
</itemizedlist>
<example id="ref_guide_fetch_customgroups">
<title>
Custom Fetch Group Metadata
</title>
<para>
Creates a <emphasis>detail</emphasis> fetch group consisting of the
<literal>publisher</literal> and <literal>articles</literal> relations.
</para>
<programlisting>
import org.apache.openjpa.persistence.*;
@Entity
@FetchGroups({
@FetchGroup(name="detail", attributes={
@FetchAttribute(name="publisher"),
@FetchAttribute(name="articles")
}),
...
})
public class Magazine {
...
}
</programlisting>
</example>
<para>
A field can be a member of any number of fetch groups. A field can also
declare a <emphasis>load fetch group</emphasis>.
When you access a lazy-loaded field for the first time, OpenJPA makes a
datastore trip to fetch that field's data. Sometimes, however, you know
that whenever you access a lazy field A, you're likely to access lazy fields B
and C as well. Therefore, it would be more efficient to fetch the data for A,
B, and C in the same datastore trip. By setting A's load fetch group to the
name of a <link linkend="ref_guide_fetch">fetch group</link> containing B and
C, you can tell OpenJPA to load all of these fields together when A is first
accessed.
</para>
<para>
Use OpenJPA's
<ulink url="../javadoc/org/apache/openjpa/persistence/LoadFetchGroup.html">
<classname>org.apache.openjpa.persistence.LoadFetchGroup</classname></ulink>
annotation to specify the load fetch group of any persistent field. The value of
the annotation is the name of a declared fetch group whose members should be
loaded along with the annotated field.
</para>
<example id="ref_guide_fetch_loadgroup">
<title>
Load Fetch Group Metadata
</title>
<programlisting>
import org.apache.openjpa.persistence.*;
@Entity
@FetchGroups({
@FetchGroup(name="detail", attributes={
@FetchAttribute(name="publisher"),
@FetchAttribute(name="articles")
}),
...
})
public class Magazine {
@ManyToOne(fetch=FetchType.LAZY)
@LoadFetchGroup("detail")
private Publisher publisher;
...
}
</programlisting>
</example>
</section>
<section id="ref_guide_fetch_conf">
<title>
Custom Fetch Group Configuration
</title>
<indexterm zone="ref_guide_fetch_conf">
<primary>
fetch groups
</primary>
<secondary>
custom configuration
</secondary>
</indexterm>
<para>
<indexterm>
<primary>
fetch groups
</primary>
<secondary>
FetchGroups
</secondary>
</indexterm>
You can control the default set of fetch groups with the
<link linkend="openjpa.FetchGroups"><literal>openjpa.FetchGroups</literal>
</link> configuration property. Set this property to a comma-separated list of
fetch group names.
</para>
<para>
You can also set the system's default maximum fetch depth with the
<link linkend="openjpa.MaxFetchDepth"><literal>openjpa.MaxFetchDepth</literal>
</link> configuration property. The maximum fetch depth determines how "deep"
into the object graph to traverse when loading an instance. For example, with
a <literal>MaxFetchDepth</literal> of 1, OpenJPA will load at most the target
instance and its immediate relations. With a <literal>MaxFetchDepth</literal>
of 2, OpenJPA may load the target instance, its immediate relations, and
the relations of those relations. This works to arbitrary depth. In fact,
the default <literal>MaxFetchDepth</literal> value is -1, which symbolizes
infinite depth. Under this setting, OpenJPA will fetch configured relations
until it reaches the edges of the object graph. Of course, which relation
fields are loaded depends on whether the fields are eager or lazy, and on the
active fetch groups. A fetch group member's recursion depth may also limit
the fetch depth to something less than the configured maximum.
</para>
<para>
OpenJPA's <classname>OpenJPAEntityManager</classname> and <classname>
OpenJPAQuery</classname> extensions to the standard <classname>EntityManager
</classname> and <classname>Query</classname> interfaces provide access to a
<ulink url="../javadoc/org/apache/openjpa/persistence/FetchPlan.html">
<classname>org.apache.openjpa.persistence.FetchPlan</classname></ulink> object.
The <classname>FetchPlan</classname> maintains the set of active fetch groups
and the maximum fetch depth. It begins with the groups and depth defined in the
<literal>openjpa.FetchGroups</literal> and <literal>openjpa.MaxFetchDepth
</literal> properties, but allows you to add or remove groups and change the
maximum fetch depth for an individual <classname>EntityManager</classname> or
<classname>Query</classname> through the methods below.
</para>
<programlisting>
public FetchPlan addFetchGroup(String group);
public FetchPlan addFetchGroups(String... groups);
public FetchPlan addFetchGroups(Collection groups);
public FetchPlan removeFetchGrop(String group);
public FetchPlan removeFetchGroups(String... groups);
public FetchPlan removeFetchGroups(Collection groups);
public FetchPlan resetFetchGroups();
public Collection&lt;String&gt; getFetchGroups();
public void clearFetchGroups();
public FetchPlan setMaxFetchDepth(int depth);
public int getMaxFetchDepth();
</programlisting>
<para>
<xref linkend="ref_guide_runtime"/> details the <classname>
OpenJPAEntityManager</classname>, <classname>OpenJPAQuery</classname>, and
<classname>FetchPlan</classname> interfaces.
</para>
<example id="ref_guide_fetch_conf_query">
<title>
Using the FetchPlan
</title>
<programlisting>
import org.apache.openjpa.persistence.*;
...
OpenJPAQuery kq = OpenJPAPersistence.cast(em.createQuery(...));
kq.getFetchPlan().setMaxFetchDepth(3).addFetchGroup("detail");
List results = kq.getResultList();
</programlisting>
</example>
</section>
<section id="ref_guide_fetch_single_field">
<title>
Per-field Fetch Configuration
</title>
<indexterm zone="ref_guide_fetch_single_field">
<primary>
fetch groups
</primary>
<secondary>
single fields
</secondary>
</indexterm>
<para>
In addition to controlling fetch configuration on a per-fetch-group basis, you
can configure OpenJPA to include particular fields in the current fetch
plan. This allows you to add individual fields that are not in the
default fetch group or in any other active fetch groups to the set of
fields that will be eagerly loaded from the database.
</para>
<para>
OpenJPA <classname>FetchPlan</classname> methods:
</para>
<programlisting>
public FetchPlan addField(String field);
public FetchPlan addFields(String... fields);
public FetchPlan addFields(Class cls, String... fields);
public FetchPlan addFields(Collection fields);
public FetchPlan addFields(Class cls, Collection fields);
public FetchPlan removeField(String field);
public FetchPlan removeFields(String... fields);
public FetchPlan removeFields(Class cls, String... fields);
public FetchPlan removeFields(Collection fields);
public FetchPlan removeFields(Class cls, Collection fields);
public Collection&lt;String&gt; getFields();
public void clearFields();
</programlisting>
<para>
The methods that take only string arguments use the fully-qualified field name,
such as <literal>org.mag.Magazine.publisher</literal>. Similarly, <methodname>
getFields</methodname> returns the set of fully-qualified field names. In all
methods, the named field must be defined in the class specified in the
invocation, not a superclass. So, if the field <literal>publisher</literal> is
defined in base class <classname>Publication</classname> rather than subclass
<classname>Magazine</classname>, you must invoke <literal>addField
(Publication.class, "publisher")</literal> and not <literal>addField
(Magazine.class, "publisher")</literal>. This is stricter than Java's default
field-masking algorithms, which would allow the latter method behavior if
<literal>Magazine</literal> did not also define a field called <literal>
publisher</literal>.
</para>
<para>
To include the fields defined in a super class by the subclass or to distinguish
between fields that are defined in <emphasis>both</emphasis> super- and subclass,
set <literal>setExtendedPathLookup(boolean)</literal> on <literal>FetchPlan
</literal> to <literal>true</literal>. By default, this option is set to
<literal>false</literal>, to reduce more extensive lookups for predominant use
cases.
</para>
<para>
In order to avoid the cost of reflection, OpenJPA does not perform any
validation of the field name / class name pairs that you put into the fetch
configuration. If you specify non-existent class / field pairs, nothing adverse
will happen, but you will receive no notification of the fact that the specified
configuration is not being used.
</para>
<example id="ref_guide_fetch-conf_per_field">
<title>
Adding an Eager Field
</title>
<programlisting>
import org.apache.openjpa.persistence.*;
...
OpenJPAEntityManager kem = OpenJPAPersistence.cast(em);
kem.getFetchPlan().addField(Magazine.class, "publisher");
Magazine mag = em.find(Magazine.class, magId);
</programlisting>
</example>
</section>
<section id="ref_guide_fetch_impl">
<title>
Implementation Notes
</title>
<itemizedlist>
<listitem>
<para>
Even when a direct relation is not eagerly fetched, OpenJPA selects the foreign
key columns and caches the values. This way when you do traverse the relation,
OpenJPA can often find the related object in its cache, or at least avoid joins
when loading the related object from the database.
</para>
</listitem>
<listitem>
<para>
The above implicit foreign key-selecting behavior does not always apply when the
relation is in a subclass table. If the subclass table would not otherwise be
joined into the select, OpenJPA avoids the extra join just to select the foreign
key values.
</para>
</listitem>
</itemizedlist>
</section>
</section>
<section id="ref_guide_perfpack_eager">
<title>
Eager Fetching
</title>
<indexterm zone="ref_guide_perfpack_eager">
<primary>
eager fetching
</primary>
</indexterm>
<indexterm>
<primary>
persistent fields
</primary>
<see>
eager fetching
</see>
</indexterm>
<indexterm zone="ref_guide_perfpack_eager">
<primary>
fetch groups
</primary>
<secondary>
eager fetching
</secondary>
<seealso>
eager fetching
</seealso>
</indexterm>
<indexterm>
<primary>
lazy loading
</primary>
<seealso>
eager fetching
</seealso>
<seealso>
fetch groups
</seealso>
</indexterm>
<para>
Eager fetching is the ability to efficiently load subclass data and related
objects along with the base instances being queried. Typically, OpenJPA has to
make a trip to the database whenever a relation is loaded, or when you first
access data that is mapped to a table other than the least-derived superclass
table. If you perform a query that returns 100 <classname>Person</classname>
objects, and then you have to retrieve the <classname>Address</classname> for
each person, OpenJPA may make as many as 101 queries (the initial query, plus
one for the address of each person returned). Or if some of the <classname>
Person</classname> instances turn out to be <classname>Employee</classname>s,
where <classname>Employee</classname> has additional data in its own joined
table, OpenJPA once again might need to make extra database trips to access the
additional employee data. With eager fetching, OpenJPA can reduce these cases to
a single query.
</para>
<para>
Eager fetching only affects relations in the active fetch groups, and is limited
by the declared maximum fetch depth and field recursion depth (see
<xref linkend="ref_guide_fetch"/>). In other words, relations that would
not normally be loaded immediately when retrieving an object or accessing a
field are not affected by eager fetching. In our example above, the address of
each person would only be eagerly fetched if the query were configured to
include the address field or its fetch group, or if the address were in the
default fetch group. This allows you to control exactly which fields are eagerly
fetched in different situations. Similarly, queries that exclude subclasses
aren't affected by eager subclass fetching, described below.
</para>
<para>
Eager fetching has three modes:
</para>
<itemizedlist>
<listitem>
<para>
<literal>none</literal>: No eager fetching is performed. Related objects are
always loaded in an independent select statement. No joined subclass data is
loaded unless it is in the table(s) for the base type being queried. Unjoined
subclass data is loaded using separate select statements rather than a SQL UNION
operation.
</para>
</listitem>
<listitem>
<para>
<indexterm>
<primary>
eager fetching
</primary>
<secondary>
join mode
</secondary>
</indexterm>
<literal>join</literal>: In this mode, OpenJPA joins to to-one relations in the
configured fetch groups. If OpenJPA is loading data for a single instance, then
OpenJPA will also join to any collection field in the configured fetch groups.
When loading data for multiple instances, though, (such as when executing a
<classname>Query</classname>) OpenJPA will not join to collections by default.
Instead, OpenJPA defaults to <literal>parallel</literal> mode for collections,
as described below. You can force OpenJPA use a join rather than parallel mode
for a collection field using the metadata extension described in
<xref linkend="eager-fetch-mode"/>.
</para>
<para>
<indexterm>
<primary>
outer joins
</primary>
</indexterm>
Under <literal>join</literal> mode, OpenJPA uses a left outer join (or inner
join, if the relations' field metadata declares the relation non-nullable) to
select the related data along with the data for the target objects. This process
works recursively for to-one joins, so that if <classname>Person</classname> has
an <classname>Address</classname>, and <classname>Address</classname> has a
<classname>TelephoneNumber</classname>, and the fetch groups are configured
correctly, OpenJPA might issue a single select that joins across the tables for
all three classes. To-many joins can not recursively spawn other to-many joins,
but they can spawn recursive to-one joins.
</para>
<para>
Under the <literal>join</literal> subclass fetch mode, subclass data in joined
tables is selected by outer joining to all possible subclass tables of the type
being queried. As you'll see below, subclass data fetching is configured
separately from relation fetching, and can be disabled for specific classes.
</para>
<note>
<para>
Some databases may not support outer joins. Also, OpenJPA can not use
outer joins if you have set the <link linkend="openjpa.jdbc.DBDictionary">
<literal> DBDictionary</literal></link>'s <literal>JoinSyntax</literal> to
<literal>traditional</literal>. See <xref linkend="ref_guide_dbsetup_sql92"/>.
</para>
</note>
</listitem>
<listitem>
<para>
<indexterm>
<primary>
eager fetching
</primary>
<secondary>
parallel mode
</secondary>
</indexterm>
<literal>parallel</literal>: Under this mode, OpenJPA selects to-one relations
and joined collections as outlined in the <literal>join</literal> mode
description above. Unjoined collection fields, however, are eagerly fetched
using a separate select statement for each collection, executed in parallel with
the select statement for the target objects. The parallel selects use the
<literal>WHERE</literal> conditions from the primary select, but add their own
joins to reach the related data. Thus, if you perform a query that returns 100
<classname>Company</classname> objects, where each company has a list of
<classname>Employee</classname> objects and <classname>Department</classname>
objects, OpenJPA will make 3 queries. The first will select the company objects,
the second will select the employees for those companies, and the third will
select the departments for the same companies. Just as for joins, this process
can be recursively applied to the objects in the relations being eagerly
fetched. Continuing our example, if the <classname>Employee</classname> class
had a list of <classname>Projects</classname> in one of the fetch groups being
loaded, OpenJPA would execute a single additional select in parallel to load the
projects of all employees of the matching companies.
</para>
<para>
Using an additional select to load each collection avoids transferring more data
than necessary from the database to the application. If eager joins were used
instead of parallel select statements, each collection added to the configured
fetch groups would cause the amount of data being transferred to rise
dangerously, to the point that you could easily overwhelm the network.
</para>
<para>
Polymorphic to-one relations to table-per-class mappings use parallel eager
fetching because proper joins are impossible. You can force other to-one
relations to use parallel rather than join mode eager fetching using the
metadata extension described in <xref linkend="eager-fetch-mode"/>.
</para>
<para>
Parallel subclass fetch mode only applies to queries on joined inheritance
hierarchies. Rather than outer-joining to
subclass tables, OpenJPA will issue the query separately for each subclass. In
all other situations, parallel subclass fetch mode acts just like join mode in
regards to vertically-mapped subclasses.
</para>
<para>
When OpenJPA knows that it is selecting for a single object only, it never uses
<literal>parallel</literal> mode, because the additional selects can be made
lazily just as efficiently. This mode only increases efficiency over <literal>
join</literal> mode when multiple objects with eager relations are being loaded,
or when multiple selects might be faster than joining to all possible
subclasses.
</para>
</listitem>
</itemizedlist>
<section id="ref_guide_perfpack_eager_conf">
<title>
Configuring Eager Fetching
</title>
<indexterm zone="ref_guide_perfpack_eager_conf">
<primary>
eager fetching
</primary>
<secondary>
configuration
</secondary>
</indexterm>
<para>
<indexterm>
<primary>
EagerFetchMode
</primary>
</indexterm>
<indexterm>
<primary>
SubclassFetchMode
</primary>
</indexterm>
<indexterm>
<primary>
eager fetching
</primary>
<secondary>
EagerFetchMode
</secondary>
</indexterm>
<indexterm>
<primary>
eager fetching
</primary>
<secondary>
SubclassFetchMode
</secondary>
</indexterm>
You can control OpenJPA's default eager fetch mode through the
<link linkend="openjpa.jdbc.EagerFetchMode"><literal>
openjpa.jdbc.EagerFetchMode</literal></link> and
<link linkend="openjpa.jdbc.SubclassFetchMode"><literal>
openjpa.jdbc.SubclassFetchMode</literal></link> configuration properties. Set
each of these properties to one of the mode names described in the previous
section: <literal>none, join, parallel</literal>. If left unset, the eager
fetch mode defaults to <literal>parallel</literal> and the subclass fetch mode
defaults to <literal>join</literal> These are generally the most robust and
performant strategies.
</para>
<para>
You can easily override the default fetch modes at runtime for any lookup or
query through OpenJPA's fetch configuration APIs. See
<xref linkend="ref_guide_runtime"/> for details.
</para>
<example id="ref_guide_perfpack_eager_def">
<title>
Setting the Default Eager Fetch Mode
</title>
<programlisting>
&lt;property name="openjpa.jdbc.EagerFetchMode" value="parallel"/&gt;
&lt;property name="openjpa.jdbc.SubclassFetchMode" value="join"/&gt;
</programlisting>
</example>
<example id="ref_guide_perfpack_eager_runtime">
<title>
Setting the Eager Fetch Mode at Runtime
</title>
<programlisting>
import org.apache.openjpa.persistence.*;
import org.apache.openjpa.persistence.jdbc.*;
...
Query q = em.createQuery("select p from Person p where p.address.state = 'TX'");
OpenJPAQuery kq = OpenJPAPersistence.cast(q);
JDBCFetchPlan fetch = (JDBCFetchPlan) kq.getFetchPlan();
fetch.setEagerFetchMode(FetchMode.PARALLEL);
fetch.setSubclassFetchMode(FetchMode.JOIN);
List results = q.getResultList();
</programlisting>
</example>
<para>
You can specify a default subclass fetch mode for an individual class with the
metadata extension described in <xref linkend="subclass-fetch-mode"/>.
Note, however, that you cannot "upgrade" the runtime fetch mode with your class
setting. If the runtime fetch mode is <literal>none</literal>, no eager
subclass data fetching will take place, regardless of your metadata setting.
</para>
<para>
This applies to the eager fetch mode metadata extension as well (see
<xref linkend="eager-fetch-mode"/>). You can use this extension to
disable eager fetching on a field or to declare that a collection would rather
use joins than parallel selects or vice versa. But an extension value of
<literal>join</literal> won't cause any eager joining if the fetch
configuration's setting is <literal>none</literal>.
</para>
</section>
<section id="ref_guide_perfpack_eager_consider">
<title>
Eager Fetching Considerations and Limitations
</title>
<para>
There are several important points that you should consider when using eager
fetching:
</para>
<itemizedlist>
<listitem>
<para>
<indexterm>
<primary>
eager fetching
</primary>
<secondary>
with large result sets
</secondary>
</indexterm>
<indexterm>
<primary>
large result sets
</primary>
<secondary>
interaction with eager fetching
</secondary>
</indexterm>
When you are using <literal>parallel</literal> eager fetch mode and you have
large result sets enabled (see <xref linkend="ref_guide_dbsetup_lrs"/>)
or you place a range on a query, OpenJPA performs the needed parallel selects on
one page of results at a time. For example, suppose your <literal>
FetchBatchSize</literal> is set to 20, and you perform a large result set query
on a class that has collection fields in the configured fetch groups. OpenJPA
will immediately cache the first <literal>20</literal> results of the query
using <literal>join</literal> mode eager fetching only. Then, it will issue the
extra selects needed to eager fetch your collection fields according to
<literal>parallel</literal> mode. Each select will use a SQL <literal>IN
</literal> clause (or multiple <literal>OR</literal> clauses if your class has a
compound primary key) to limit the selected collection elements to those owned
by the 20 cached results.
</para>
<para>
Once you iterate past the first 20 results, OpenJPA will cache the next 20 and
again issue any needed extra selects for collection fields, and so on. This
pattern ensures that you get the benefits of eager fetching without bringing
more data into memory than anticipated.
</para>
</listitem>
<listitem>
<para>
Once OpenJPA eager-joins into a class, it cannot issue any further eager to-many
joins or parallel selects from that class in the same query. To-one joins,
however, can recurse to any level.
</para>
</listitem>
<listitem>
<para>
Using a to-many join makes it impossible to determine the number of instances
the result set contains without traversing the entire set. This is because each
result object might be represented by multiple rows. Thus, queries with a range
specification or queries configured for lazy result set traversal automatically
turn off eager to-many joining.
</para>
</listitem>
<listitem>
<para>
OpenJPA cannot eagerly join to polymorphic relations to non-leaf classes in a
table-per-class inheritance hierarchy. You can work around this restriction
using the mapping extensions described in <xref linkend="nonpolymorphic"/>.
</para>
</listitem>
</itemizedlist>
</section>
</section>
</chapter>