Table of Contents
List of Tables
List of Examples
Table of Contents
Table of Contents
OpenJPA is Apache's implementation of Sun's Java Persistence API (JPA) specification for the transparent persistence of Java objects. This document provides an overview of the JPA standard and technical details on the use of OpenJPA.
This document is intended for OpenJPA users. It is divided into several parts:
The JPA Overview describes the fundamentals of the JPA specification.
The OpenJPA Reference Guide contains detailed documentation on all aspects of OpenJPA. Browse through this guide to familiarize yourself with the many advanced features and customization opportunities OpenJPA provides. Later, you can use the guide when you need details on a specific aspect of OpenJPA.
Table of Contents
Table of Contents
The Java Persistence API (JPA) is a specification from Sun Microsystems for the persistence of Java objects to any relational datastore. JPA requires J2SE 1.5 (also referred to as "Java 5") or higher, as it makes heavy use of new Java language features such as annotations and generics. This document provides an overview of JPA. Unless otherwise noted, the information presented applies to all JPA implementations.
For coverage of OpenJPA's many extensions to the JPA specification, see the Reference Guide.
This document is intended for developers who want to learn about JPA in order to use it in their applications. It assumes that you have a strong knowledge of object-oriented concepts and Java, including Java 5 annotations and generics. It also assumes some experience with relational databases and the Structured Query Language (SQL).
Persistent data is information that can outlive the program that creates it. The majority of complex programs use persistent data: GUI applications need to store user preferences across program invocations, web applications track user movements and orders over long periods of time, etc.
Lightweight persistence is the storage and retrieval of persistent data with little or no work from you, the developer. For example, Java serialization is a form of lightweight persistence because it can be used to persist Java objects directly to a file with very little effort. Serialization's capabilities as a lightweight persistence mechanism pale in comparison to those provided by JPA, however. The next chapter compares JPA to serialization and other available persistence mechanisms.
Java developers who need to store and retrieve persistent data already have several options available to them: serialization, JDBC, JDO, proprietary object-relational mapping tools, object databases, and EJB 2 entity beans. Why introduce yet another persistence framework? The answer to this question is that with the exception of JDO, each of the aforementioned persistence solutions has severe limitations. JPA attempts to overcome these limitations, as illustrated by the table below.
Table 2.1. Persistence Mechanisms
| Supports: | Serialization | JDBC | ORM | ODB | EJB 2 | JDO | JPA |
|---|---|---|---|---|---|---|---|
| Java Objects | Yes | No | Yes | Yes | Yes | Yes | Yes |
| Advanced OO Concepts | Yes | No | Yes | Yes | No | Yes | Yes |
| Transactional Integrity | No | Yes | Yes | Yes | Yes | Yes | Yes |
| Concurrency | No | Yes | Yes | Yes | Yes | Yes | Yes |
| Large Data Sets | No | Yes | Yes | Yes | Yes | Yes | Yes |
| Existing Schema | No | Yes | Yes | No | Yes | Yes | Yes |
| Relational and Non-Relational Stores | No | No | No | No | Yes | Yes | No |
| Queries | No | Yes | Yes | Yes | Yes | Yes | Yes |
| Strict Standards / Portability | Yes | No | No | No | Yes | Yes | Yes |
| Simplicity | Yes | Yes | Yes | Yes | No | Yes | Yes |
Serialization is Java's built-in mechanism for transforming an object graph into a series of bytes, which can then be sent over the network or stored in a file. Serialization is very easy to use, but it is also very limited. It must store and retrieve the entire object graph at once, making it unsuitable for dealing with large amounts of data. It cannot undo changes that are made to objects if an error occurs while updating information, making it unsuitable for applications that require strict data integrity. Multiple threads or programs cannot read and write the same serialized data concurrently without conflicting with each other. It provides no query capabilities. All these factors make serialization useless for all but the most trivial persistence needs.
Many developers use the Java Database Connectivity (JDBC) APIs to manipulate persistent data in relational databases. JDBC overcomes most of the shortcomings of serialization: it can handle large amounts of data, has mechanisms to ensure data integrity, supports concurrent access to information, and has a sophisticated query language in SQL. Unfortunately, JDBC does not duplicate serialization's ease of use. The relational paradigm used by JDBC was not designed for storing objects, and therefore forces you to either abandon object-oriented programming for the portions of your code that deal with persistent data, or to find a way of mapping object-oriented concepts like inheritance to relational databases yourself.
There are many proprietary software products that can perform the mapping between objects and relational database tables for you. These object-relational mapping (ORM) frameworks allow you to focus on the object model and not concern yourself with the mismatch between the object-oriented and relational paradigms. Unfortunately, each of these product has its own set of APIs. Your code becomes tied to the proprietary interfaces of a single vendor. If the vendor raises prices, fails to fix show-stopping bugs, or falls behind in features, you cannot switch to another product without rewriting all of your persistence code. This is referred to as vendor lock-in.
Rather than map objects to relational databases, some software companies have developed a form of database designed specifically to store objects. These object databases (ODBs) are often much easier to use than object-relational mapping software. The Object Database Management Group (ODMG) was formed to create a standard API for accessing object databases; few object database vendors, however, comply with the ODMG's recommendations. Thus, vendor lock-in plagues object databases as well. Many companies are also hesitant to switch from tried-and-true relational systems to the relatively unknown object database technology. Fewer data-analysis tools are available for object database systems, and there are vast quantities of data already stored in older relational databases. For all of these reasons and more, object databases have not caught on as well as their creators hoped.
The Enterprise Edition of the Java platform introduced entity Enterprise Java Beans (EJBs). EJB 2.x entities are components that represent persistent information in a datastore. Like object-relational mapping solutions, EJB 2.x entities provide an object-oriented view of persistent data. Unlike object-relational software, however, EJB 2.x entities are not limited to relational databases; the persistent information they represent may come from an Enterprise Information System (EIS) or other storage device. Also, EJB 2.x entities use a strict standard, making them portable across vendors. Unfortunately, the EJB 2.x standard is somewhat limited in the object-oriented concepts it can represent. Advanced features like inheritance, polymorphism, and complex relations are absent. Additionally, EBJ 2.x entities are difficult to code, and they require heavyweight and often expensive application servers to run.
The JDO specification uses an API that is strikingly similar to JPA. JDO, however, supports non-relational databases, a feature that some argue dilutes the specification.
JPA combines the best features from each of the persistence mechanisms listed above. Creating entities under JPA is as simple as creating serializable classes. JPA supports the large data sets, data consistency, concurrent use, and query capabilities of JDBC. Like object-relational software and object databases, JPA allows the use of advanced object-oriented concepts such as inheritance. JPA avoids vendor lock-in by relying on a strict specification like JDO and EJB 2.x entities. JPA focuses on relational databases. And like JDO, JPA is extremely easy to use.
OpenJPA typically stores data in relational databases, but can be customized for use with non-relational datastores as well.
JPA is not ideal for every application. For many applications, though, it provides an exciting alternative to other persistence mechanisms.
Table of Contents
The diagram below illustrates the relationships between the primary components of the JPA architecture.
![]() |
A number of the depicted interfaces are only required outside of an
EJB3-compliant application server. In an application server,
EntityManager instances are typically injected, rendering the
EntityManagerFactory unnecessary. Also, transactions
within an application server are handled using standard application server
transaction controls. Thus, the EntityTransaction also
goes unused.
Persistence: The
javax.persistence.Persistence class contains static helper methods
to obtain EntityManagerFactory instances in a
vendor-neutral fashion.
EntityManagerFactory: The
javax.persistence.EntityManagerFactory class is a factory for
EntityManager s.
EntityManager
: The javax.persistence.EntityManager
is the primary JPA interface used by applications. Each
EntityManager manages a set of persistent objects, and
has APIs to insert new objects and delete existing ones. When used outside the
container, there is a one-to-one relationship between an
EntityManager and an EntityTransaction.
EntityManagers also act as factories for
Query instances.
Entity
: Entites are persistent objects that represent
datastore records.
EntityTransaction: Each EntityManager
has a one-to-one relation with a single
javax.persistence.EntityTransaction.
EntityTransactions allow operations on persistent data to be
grouped into units of work that either completely succeed or completely fail,
leaving the datastore in its original state. These all-or-nothing operations
are important for maintaining data integrity.
Query
: The javax.persistence.Query
interface is implemented by each JPA vendor to find persistent
objects that meet certain criteria. JPA standardizes support for queries using
both the Java Persistence Query Language (JPQL) and the Structured Query
Language (SQL). You obtain Query instances from an
EntityManager.
The example below illustrates how the JPA interfaces interact to execute a JPQL query and update persistent objects. The example assumes execution outside a container.
Example 3.1. Interaction of Interfaces Outside Container
// get an EntityManagerFactory using the Persistence class; typically
// the factory is cached for easy repeated use
EntityManagerFactory factory = Persistence.createEntityManagerFactory(null);
// get an EntityManager from the factory
EntityManager em = factory.createEntityManager(PersistenceContextType.EXTENDED);
// updates take place within transactions
EntityTransaction tx = em.getTransaction();
tx.begin();
// query for all employees who work in our research division
// and put in over 40 hours a week average
Query query = em.createQuery("select e from Employee e where "
+ "e.division.name = 'Research' AND e.avgHours > 40");
List results = query.getResultList ();
// give all those hard-working employees a raise
for (Object res : results) {
Employee emp = (Employee) res;
emp.setSalary(emp.getSalary() * 1.1);
}
// commit the updates and free resources
tx.commit();
em.close();
factory.close();
Within a container, the EntityManager will be injected
and transactions will be handled declaratively. Thus, the in-container version
of the example consists entirely of business logic:
Example 3.2. Interaction of Interfaces Inside Container
// query for all employees who work in our research division
// and put in over 40 hours a week average - note that the EntityManager em
// is injected using a @Resource annotation
Query query = em.createQuery("select e from Employee e where "
+ "e.division.name = 'Research' and e.avgHours > 40");
List results = query.getResultList();
// give all those hard-working employees a raise
for (Object res : results) {
emp = (Employee) res;
emp.setSalary(emp.getSalary() * 1.1);
}
The remainder of this document explores the JPA interfaces in detail. We present them in roughly the order that you will use them as you develop your application.
![]() |
The diagram above depicts the JPA exception architecture. All
exceptions are unchecked. JPA uses standard exceptions where
appropriate, most notably IllegalArgumentExceptions and
IllegalStateExceptions. The specification also provides
a few JPA-specific exceptions in the javax.persistence
package. These exceptions should be self-explanatory. See the
Javadoc for
additional details on JPA exceptions.
All exceptions thrown by OpenJPA implement
org.apache.openjpa.util.ExceptionInfo to provide you with
additional error information.
Table of Contents
JPA recognizes two types of persistent classes: entity
classes and embeddable classes. Each persistent instance of
an entity class - each entity - represents a unique
datastore record. You can use the EntityManager to find
an entity by its persistent identity (covered later in this chapter), or use a
Query to find entities matching certain criteria.
An instance of an embeddable class, on the other hand, is only stored as part of
a separate entity. Embeddable instances have no persistent identity, and are
never returned directly from the EntityManager or from a
Query.
Despite these differences, there are few distinctions between entity classes and embeddable classes. In fact, writing either type of persistent class is a lot like writing any other class. There are no special parent classes to extend from, field types to use, or methods to write. This is one important way in which JPA makes persistence transparent to you, the developer.
JPA supports both fields and JavaBean properties as persistent state. For simplicity, however, we will refer to all persistent state as persistent fields, unless we want to note a unique aspect of persistent properties.
Example 4.1. Persistent Class
package org.mag;
/**
* Example persistent class. Notice that it looks exactly like any other
* class. JPA makes writing persistent classes completely transparent.
*/
public class Magazine {
private String isbn;
private String title;
private Set articles = new HashSet();
private Article coverArticle;
private int copiesSold;
private double price;
private Company publisher;
private int version;
protected Magazine() {
}
public Magazine(String title, String isbn) {
this.title = title;
this.isbn = isbn;
}
public void publish(Company publisher, double price) {
this.publisher = publisher;
publisher.addMagazine(this);
this.price = price;
}
public void sell() {
copiesSold++;
publisher.addRevenue(price);
}
public void addArticle(Article article) {
articles.add(article);
}
// rest of methods omitted
}
There are very few restrictions placed on persistent classes. Still, it never hurts to familiarize yourself with exactly what JPA does and does not support.
The JPA specification requires that all persistent classes have a no-arg constructor. This constructor may be public or protected. Because the compiler automatically creates a default no-arg constructor when no other constructor is defined, only classes that define constructors must also include a no-arg constructor.
OpenJPA's enhancer will automatically add a protected no-arg constructor to your class when required. Therefore, this restriction does not apply when using OpenJPA. See Section 2, “ Enhancement ” of the Reference Guide for details.
Entity classes may not be final. No method of an entity class can be final.
OpenJPA supports final classes and final methods.
All entity classes must declare one or more fields which together form the
persistent identity of an instance. These are called identity
or primary key fields. In our
Magazine class, isbn and title
are identity fields, because no two magazine records in the datastore can have
the same isbn and title values.
Section 2.2, “
Id
” will show you how to denote your
identity fields in JPA metadata. Section 2, “
Entity Identity
”
below examines persistent identity.
OpenJPA fully supports identity fields, but does not require them. See Section 3, “ Object Identity ” of the Reference Guide for details.
The version field in our Magazine
class may seem out of place. JPA uses a version field in your entities to detect
concurrent modifications to the same datastore record. When the JPA runtime
detects an attempt to concurrently modify the same record, it throws an
exception to the transaction attempting to commit last. This prevents
overwriting the previous commit with stale data.
A version field is not required, but without one concurrent threads or processes might succeed in making conflicting changes to the same record at the same time. This is unacceptable to most applications. Section 2.5, “ Version ” shows you how to designate a version field in JPA metadata.
The version field must be an integral type ( int,
Long, etc) or a
java.sql.Timestamp. You should consider version fields immutable.
Changing the field value has undefined results.
OpenJPA fully supports version fields, but does not require them for concurrency detection. OpenJPA can maintain surrogate version values or use state comparisons to detect concurrent modifications. See Section 7, “ Additional JPA Mappings ” in the Reference Guide.
JPA fully supports inheritance in persistent classes. It allows persistent classes to inherit from non-persistent classes, persistent classes to inherit from other persistent classes, and non-persistent classes to inherit from persistent classes. It is even possible to form inheritance hierarchies in which persistence skips generations. There are, however, a few important limitations:
Persistent classes cannot inherit from certain natively-implemented system
classes such as java.net.Socket and
java.lang.Thread.
If a persistent class inherits from a non-persistent class, the fields of the non-persistent superclass cannot be persisted.
All classes in an inheritance tree must use the same identity type. We cover entity identity in Section 2, “ Entity Identity ”.
JPA manages the state of all persistent fields. Before you access persistent state, the JPA runtime makes sure that it has been loaded from the datastore. When you set a field, the runtime records that it has changed so that the new value will be persisted. This allows you to treat the field in exactly the same way you treat any other field - another aspect of JPA's transparency.
JPA does not support static or final fields. It does, however, include built-in support for most common field types. These types can be roughly divided into three categories: immutable types, mutable types, and relations.
Immutable types, once created, cannot be changed. The only way to alter a persistent field of an immutable type is to assign a new value to the field. JPA supports the following immutable types:
All primitives (int, float, byte, etc)
All primitive wrappers (java.lang.Integer, java.lang.Float,
java.lang.Byte, etc)
java.lang.String
java.math.BigInteger
java.math.BigDecimal
JPA also supports byte[], Byte[],
char[], and Character[] as
immutable types. That is, you can persist fields of these types,
but you should not manipulate individual array indexes without resetting the
array into the persistent field.
Persistent fields of mutable types can be altered without assigning the field a new value. Mutable types can be modified directly through their own methods. The JPA specification requires that implementations support the following mutable field types:
java.util.Date
java.util.Calendar
java.sql.Date
java.sql.Timestamp
Enums
Entity types (relations between entities)
Embeddable types
java.util.Collections of entities
java.util.Sets of entities
java.util.Lists of entities
java.util.Maps in which each entry maps the value of one
of a related entity's fields to that entity.
Collection and map types may be parameterized.
Most JPA implementations also have support for persisting serializable values as binary data in the datastore. Chapter 5, Metadata has more information on persisting serializable types.
OpenJPA also supports arrays, java.lang.Number,
java.util.Locale, all JDK 1.2 Set,
List, and Map types,
and many other mutable and immutable field types. OpenJPA also allows you to
plug in support for custom types.
This section detailed all of the restrictions JPA places on persistent classes. While it may seem like we presented a lot of information, you will seldom find yourself hindered by these restrictions in practice. Additionally, there are often ways of using JPA's other features to circumvent any limitations you run into.
Java recognizes two forms of object identity: numeric identity and qualitative
identity. If two references are numerically identical, then
they refer to the same JVM instance in memory. You can test for this using the
== operator. Qualitative identity, on
the other hand, relies on some user-defined criteria to determine whether two
objects are "equal". You test for qualitative identity using the
equals method. By default, this method simply relies on numeric
identity.
JPA introduces another form of object identity, called entity identity or persistent identity. Entity identity tests whether two persistent objects represent the same state in the datastore.
The entity identity of each persistent instance is encapsulated in its identity field(s). If two entities of the same type have the same identity field values, then the two entities represent the same state in the datastore. Each entity's identity field values must be unique among all other entites of the same type.
Identity fields must be primitives, primitive wrappers,
Strings, Dates,
Timestamps, or embeddable types. Notably, other entities instances
can not be used as identity fields.
For legacy schemas with binary primary key columns, OpenJPA also supports using
identity fields of type byte[]. When you use a
byte[] identity field, you must create an identity class.
Identity classes are covered below.
Changing the fields of an embeddable instance while it is assigned to an identity field has undefined results. Always treat embeddable identity instances as immutable objects in your applications.
If you are dealing with a single persistence context (see
Section 3, “
Persistence Context
”), then you do not
have to compare identity fields to test whether two entity references represent
the same state in the datastore. There is a much easier way: the ==
operator. JPA requires that each persistence context maintain only
one JVM object to represent each unique datastore record. Thus, entity identity
is equivalent to numeric identity within a persistence context. This is referred
to as the uniqueness requirement.
The uniqueness requirement is extremely important - without it, it would be impossible to maintain data integrity. Think of what could happen if two different objects in the same transaction were allowed to represent the same persistent data. If you made different modifications to each of these objects, which set of changes should be written to the datastore? How would your application logic handle seeing two different "versions" of the same data? Thanks to the uniqueness requirement, these questions do not have to be answered.
If your entity has only one identity field, you can use the value of that field
as the entity's identity object in all
EntityManager APIs. Otherwise, you must supply an
identity class to use for identity objects. Your identity class must meet the
following criteria:
The class must be public.
The class must be serializable.
The class must have a public no-args constructor.
The names of the non-static fields or properties of the class must be the same as the names of the identity fields or properties of the corresponding entity class, and the types must be identical.
The equals and hashCode
methods of the class must use the values of all fields or properties
corresponding to identity fields or properties in the entity class.
If the class is an inner class, it must be static.
All entity classes related by inheritance must use the same identity class, or else each entity class must have its own identity class whose inheritance hierarchy mirrors the inheritance hierarchy of the owning entity classes (see Section 2.1.1, “ Identity Hierarchies ”).
Though you may still create identity classes by hand, OpenJPA provides the
appidtool to automatically generate proper identity
classes based on your identity fields. See
Section 3.2, “
Application Identity Tool
” of the Reference Guide.
Example 4.2. Identity Class
This example illustrates a proper identity class for an entity with multiple identity fields.
/**
* Persistent class using application identity.
*/
public class Magazine {
private String isbn; // identity field
private String title; // identity field
// rest of fields and methods omitted
/**
* Application identity class for Magazine.
*/
public static class MagazineId {
// each identity field in the Magazine class must have a
// corresponding field in the identity class
public String isbn;
public String title;
/**
* Equality must be implemented in terms of identity field
* equality, and must use instanceof rather than comparing
* classes directly (some JPA implementations may subclass the
* identity class).
*/
public boolean equals(Object other) {
if (other == this)
return true;
if (!(other instanceof MagazineId))
return false;
MagazineId mi = (MagazineId) other;
return (isbn == mi.isbn
|| (isbn != null && isbn.equals(mi.isbn)))
&& (title == mi.title
|| (title != null && title.equals(mi.title)));
}
/**
* Hashcode must also depend on identity values.
*/
public int hashCode() {
return ((isbn == null) ? 0 : isbn.hashCode())
^ ((title == null) ? 0 : title.hashCode());
}
public String toString() {
return isbn + ":" + title;
}
}
}
![]() |
An alternative to having a single identity class for an entire inheritance hierarchy is to have one identity class per level in the inheritance hierarchy. The requirements for using a hierarchy of identity classes are as follows:
The inheritance hierarchy of identity classes must exactly mirror the hierarchy
of the persistent classes that they identify. In the example pictured above,
abstract class Person is extended by abstract class
Employee, which is extended by non-abstract class
FullTimeEmployee, which is extended by non-abstract
class Manager. The corresponding identity classes, then,
are an abstract PersonId class, extended by an abstract
EmployeeId class, extended by a non-abstract
FullTimeEmployeeId class, extended by a non-abstract
ManagerId class.
Subclasses in the identity hierarchy may define additional identity fields until
the hierarchy becomes non-abstract. In the aforementioned example,
Person defines an identity field ssn,
Employee defines additional identity field userName
, and FullTimeEmployee adds a final identity
field, empId. However, Manager may not
define any additional identity fields, since it is a subclass of a non-abstract
class. The hierarchy of identity classes, of course, must match the identity
field definitions of the persistent class hierarchy.
It is not necessary for each abstract class to declare identity fields. In the
previous example, the abstract Person and
Employee classes could declare no identity fields, and the first
concrete subclass FullTimeEmployee could define one or
more identity fields.
All subclasses of a concrete identity class must be equals
and hashCode-compatible with the
concrete superclass. This means that in our example, a ManagerId
instance and a FullTimeEmployeeId instance
with the same identity field values should have the same hash code, and should
compare equal to each other using the equals method of
either one. In practice, this requirement reduces to the following coding
practices:
Use instanceof instead of comparing Class
objects in the equals methods of your
identity classes.
An identity class that extends another non-abstract identity class should not
override equals or hashCode.
It is often necessary to perform various actions at different stages of a persistent object's lifecycle. JPA includes a variety of callbacks methods for monitoring changes in the lifecycle of your persistent objects. These callbacks can be defined on the persistent classes themselves and on non-persistent listener classes.
Every persistence event has a corresponding callback method marker. These markers are shared between persistent classes and their listeners. You can use these markers to designate a method for callback either by annotating that method or by listing the method in the XML mapping file for a given class. The lifecycle events and their corresponding method markers are:
PrePersist: Methods marked with this annotation
will be invoked before an object is persisted. This could be used for assigning
primary key values to persistent objects. This is equivalent to the XML element
tag pre-persist.
PostPersist: Methods marked with this annotation
will be invoked after an object has transitioned to the persistent state. You
might want to use such methods to update a screen after a new row is added. This
is equivalent to the XML element tag post-persist.
PostLoad: Methods marked with this annotation
will be invoked after all eagerly fetched fields of your class have been loaded
from the datastore. No other persistent fields can be accessed in this method.
This is equivalent to the XML element tag post-load.
PostLoad is often used to initialize non-persistent
fields whose values depend on the values of persistent fields, such as a complex
datastructure.
PreUpdate: Methods marked with this annotation
will be invoked just the persistent values in your objects are flushed to the
datastore. This is equivalent to the XML element tag
pre-update.
PreUpdate is the complement to PostLoad
. While methods marked with PostLoad are most
often used to initialize non-persistent values from persistent data, methods
annotated with PreUpdate is normally used to set
persistent fields with information cached in non-persistent data.
PostUpdate: Methods marked with this annotation
will be invoked after changes to a given instance have been stored to the
datastore. This is useful for clearing stale data cached at the application
layer. This is equivalent to the XML element tag post-update.
PreRemove: Methods marked with this annotation
will be invoked before an object transactions to the deleted state. Access to
persistent fields is valid within this method. You might use this method to
cascade the deletion to related objects based on complex criteria, or to perform
other cleanup. This is equivalent to the XML element tag
pre-remove.
PostRemove: Methods marked with this annotation
will be invoked after an object has been marked as to be deleted. This is
equivalent to the XML element tag post-remove.
When declaring callback methods on a persistent class, any method may be used which takes no arguments and is not shared with any property access fields. Multiple events can be assigned to a single method as well.
Below is an example of how to declare callback methods on persistent classes:
/**
* Example persistent class declaring our entity listener.
*/
@Entity
public class Magazine {
@Transient
private byte[][] data;
@ManyToMany
private List<Photo> photos;
@PostLoad
public void convertPhotos() {
data = new byte[photos.size()][];
for (int i = 0; i < photos.size(); i++)
data[i] = photos.get(i).toByteArray();
}
@PreDelete
public void logMagazineDeletion() {
getLog().debug("deleting magazine containing" + photos.size()
+ " photos.");
}
}
In an XML mapping file, we can define the same methods without annotations:
<entity class="Magazine">
<pre-remove>logMagazineDeletion</pre-remove>
<post-load>convertPhotos</post-load>
</entity>
We fully explore persistence metadata annotations and XML in Chapter 5, Metadata .
Mixing lifecycle event code into your persistent classes is not always ideal. It
is often more elegant to handle cross-cutting lifecycle events in a
non-persistent listener class. JPA allows for this, requiring only that listener
classes have a public no-arg constructor. Like persistent classes, your listener
classes can consume any number of callbacks. The callback methods must take in a
single java.lang.Object argument which represents the
persistent object that triggered the event.
Entities can enumerate listeners using the EntityListeners
annotation. This annotation takes an array of listener classes as
its value.
Below is an example of how to declare an entity and its corresponding listener classes.
/**
* Example persistent class declaring our entity listener.
*/
@Entity
@EntityListeners({ MagazineLogger.class, ... })
public class Magazine {
// ... //
}
/**
* Example entity listener.
*/
public class MagazineLogger {
@PostPersist
public void logAddition(Object pc) {
getLog ().debug ("Added new magazine:" + ((Magazine) pc).getTitle ());
}
@PreRemove
public void logDeletion(Object pc) {
getLog().debug("Removing from circulation:" +
((Magazine) pc).getTitle());
}
}
In XML, we define both the listeners and their callback methods as so:
<entity class="Magazine">
<entity-listeners>
<entity-listener class="MagazineLogger">
<post-persist>logAddition</post-persist>
<pre-remove>logDeletion</pre-remove>
</entity-listener>
</entity-listeners>
</entity>
Entity listener methods are invoked in a specific order when a given event is fired. So-called default listeners are invoked first: these are listeners which have been defined in a package annotation or in the root element of XML mapping files. Next, entity listeners are invoked in the order of the inheritance hierarchy, with superclass listeners being invoked before subclass listeners. Finally, if an entity has multiple listeners for the same event, the listeners are invoked in declaration order.
You can exclude default listeners and listeners defined in superclasses from the invocation chain through the use of two class-level annotations:
ExcludeDefaultListeners: This annotation indicates that
no default listeners will be invoked for this class, or any of its subclasses.
The XML equivalent is the empty exclude-default-listeners
element.
ExcludeSuperclassListeners: This annotation will cause
OpenJPA to skip invoking any listeners declared in superclasses. The XML
equivalent is empty the exclude-superclass-listeners element.
Table of Contents
JPA requires that you accompany each persistent class with persistence metadata. This metadata serves three primary purposes:
To identify persistent classes.
To override default JPA behavior.
To provide the JPA implementation with information that it cannot glean from simply reflecting on the persistent class.
Persistence metadata is specified using either the Java 5 annotations defined in
the javax.persistence package, XML mapping files, or a
mixture of both. In the latter case, XML declarations override conflicting
annotations. If you choose to use XML metadata, the XML files must be available
at development and runtime, and must be discoverable via either of two
strategies:
In a resource named orm.xml placed in a
META-INF directory within a directory in your classpath or within a
jar archive containing your persistent classes.
Declared in your
persistence.xml configuration file. In this case, each XML
metadata file must be listed in a mapping-file element whose
content is either a path to the given file or a resource location available to
the class' class loader.
We describe the standard metadata annotations and XML equivalents throughout this chapter. The full schema for XML mapping files is available in Section 3, “ XML Schema ”. JPA also standardizes relational mapping metadata and named query metadata, which we discuss in Chapter 12, Mapping Metadata and Section 1.9, “ Named Queries ” respectively.
OpenJPA defines many useful annotations beyond the standard set. See Section 2, “ Additional JPA Metadata ” and Section 3, “ Metadata Extensions ” in the Reference Guide for details. There are currently no XML equivalents for these extension annotations.
![]() |
Through the course of this chapter, we will create the persistent object model above.
The following metadata annotations and XML elements apply to persistent class declarations.
The Entity annotation denotes an entity class. All entity
classes must have this annotation. The Entity annotation
takes one optional property:
String name: Name used to refer to the entity in queries.
Must not be a reserved literal in JPQL. Defaults to the unqualified name of the
entity class.
The equivalent XML element is entity. It has the following
attributes:
class: The entity class. This attribute is required.
name: Named used to refer to the class in queries. See the
name property above.
access: The access type to use for the class. Must either be
FIELD or PROPERTY. For details on access
types, see Section 2, “
Field and Property Metadata
”.
OpenJPA uses a process called enhancement to modify the bytecode of entities for transparent lazy loading and immediate dirty tracking. See Section 2, “ Enhancement ” in the Reference Guide for details on enhancement.
As we discussed in Section 2.1, “
Identity Class
”,
entities with multiple identity fields must use an identity class
to encapsulate their persistent identity. The IdClass
annotation specifies this class. It accepts a single
java.lang.Class value.
The equivalent XML element is id-class, which has a single
attribute:
class: Set this required attribute to the name of the
identity class.
A mapped superclass is a non-entity class that can define
persistent state and mapping information for entity subclasses. Mapped
superclasses are usually abstract. Unlike true entities, you cannot query a
mapped superclass, pass a mapped superclass instance to any
EntityManager or Query methods, or declare a
persistent relation with a mapped superclass target. You denote a mapped
superclass with the MappedSuperclass marker annotation.
The equivalent XML element is mapped-superclass. It expects
the following attributes:
class: The entity class. This attribute is required.
access: The access type to use for the class. Must either be
FIELD or PROPERTY. For details on access
types, see Section 2, “
Field and Property Metadata
”.
OpenJPA allows you to query on mapped superclasses. A query on a mapped superclass will return all matching subclass instances. OpenJPA also allows you to declare relations to mapped superclass types; however, you cannot query across these relations.
The Embeddable annotation designates an embeddable
persistent class. Embeddable instances are stored as part of the record of their
owning instance. All embeddable classes must have this annotation.
A persistent class can either be an entity or an embeddable class, but not both.
The equivalent XML element is embeddable. It understands the
following attributes:
class: The entity class. This attribute is required.
access: The access type to use for the class. Must either be
FIELD or PROPERTY. For details on access
types, see Section 2, “
Field and Property Metadata
”.
OpenJPA allows a persistent class to be both an entity and an embeddable class. Instances of the class will act as entites when persisted explicitly or assigned to non-embedded fields of entities. Instances will act as embedded values when assigned to embedded fields of entities.
To signal that a class is both an entity and an embeddable class in OpenJPA,
simply add both the @Entity and the @Embeddable
annotations to the class.
An entity may list its lifecycle event listeners in the
EntityListeners annotation. This value of this annotation is an
array of the listener Class es for the entity. The
equivalent XML element is entity-listeners. For more details
on entity listeners, see Section 3, “
Lifecycle Callbacks
”.
Here are the class declarations for our persistent object model, annotated with
the appropriate persistence metadata. Note that Magazine
declares an identity class, and that Document and
Address are a mapped superclass and an embeddable class,
respectively. LifetimeSubscription and
TrialSubscription override the default entity name to supply a
shorter alias for use in queries.
Example 5.1. Class Metadata
package org.mag;
@Entity
@IdClass(Magazine.MagazineId.class)
public class Magazine {
...
public static class MagazineId {
...
}
}
@Entity
public class Article {
...
}
package org.mag.pub;
@Entity
public class Company {
...
}
@Entity
public class Author {
...
}
@Embeddable
public class Address {
...
}
package org.mag.subscribe;
@MappedSuperclass
public abstract class Document {
...
}
@Entity
public class Contract
extends Document {
...
}
@Entity
public class Subscription {
...
@Entity
public static class LineItem
extends Contract {
...
}
}
@Entity(name="Lifetime")
public class LifetimeSubscription
extends Subscription {
...
}
@Entity(name="Trial")
public class TrialSubscription
extends Subscription {
...
}
The equivalent declarations in XML:
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
version="1.0">
<mapped-superclass class="org.mag.subscribe.Document">
...
</mapped-superclass>
<entity class="org.mag.Magazine">
<id-class class="org.mag.Magazine$MagazineId"/>
...
</entity>
<entity class="org.mag.Article">
...
</entity>
<entity class="org.mag.pub.Company">
...
</entity>
<entity class="org.mag.pub.Author">
...
</entity>
<entity class="org.mag.subscribe.Contract">
...
</entity>
<entity class="org.mag.subscribe.LineItem">
...
</entity>
<entity class="org.mag.subscribe.LifetimeSubscription" name="Lifetime">
...
</entity>
<entity class="org.mag.subscribe.TrialSubscription" name="Trial">
...
</entity>
<embeddable class="org.mag.pub.Address">
...
</embeddable>
</entity-mappings>
The persistence implementation must be able to retrieve and set the persistent
state of your entities, mapped superclasses, and embeddable types. JPA offers
two modes of persistent state access: field access, and
property access. Under field access, the implementation
injects state directly into your persistent fields, and retrieves changed state
from your fields as well. To declare field access on an entity with XML
metadata, set the access attribute of your entity
XML element to FIELD. To use field access for an
entity using annotation metadata, simply place your metadata and mapping
annotations on your field declarations:
@ManyToOne private Company publisher;
Property access, on the other hand, retrieves and loads state through JavaBean
"getter" and "setter" methods. For a property p of type
T, you must define the following getter method:
T getP();
For boolean properties, this is also acceptable:
boolean isP();
You must also define the following setter method:
void setP(T value);
To use property access, set your entity element's
access attribute to PROPERTY, or place your
metadata and mapping annotations on the getter method:
@ManyToOne
private Company getPublisher() { ... }
private void setPublisher(Company publisher) { ... }
When using property access, only the getter and setter method for a property should ever access the underlying persistent field directly. Other methods, including internal business methods in the persistent class, should go through the getter and setter methods when manipulating persistent state.
Also, take care when adding business logic to your getter and setter methods. Consider that they are invoked by the persistence implementation to load and retrieve all persistent state; other side effects might not be desirable.
Each class must use either field access or property access for all state; you cannot use both access types within the same class. Additionally, a subclass must use the same access type as its superclass.
The remainder of this document uses the term "persistent field" to refer to either a persistent field or a persistent property.
The Transient annotation specifies that a field is
non-persistent. Use it to exclude fields from management that would otherwise be
persistent. Transient is a marker annotation only; it
has no properties.
The equivalent XML element is transient. It has a single
attribute:
name: The transient field or property name. This attribute
is required.
Annotate your simple identity fields with Id. This
annotation has no properties. We explore entity identity and identity fields in
Section 1.3, “
Identity Fields
”.
The equivalent XML element is id. It has one required
attribute:
name: The name of the identity field or property.
The previous section showed you how to declare your identity fields with the
Id annotation. It is often convenient to allow the
persistence implementation to assign a unique value to your identity fields
automatically. JPA includes the the GeneratedValue
annotation for this purpose. It has the following properties:
GenerationType strategy: Enum value specifying how to
auto-generate the field value. The GenerationType enum
has the following values:
GeneratorType.AUTO: The default. Assign the field a
generated value, leaving the details to the JPA vendor.
GenerationType.IDENTITY: The database will assign an
identity value on insert.
GenerationType.SEQUENCE: Use a datastore sequence to
generate a field value.
GenerationType.TABLE: Use a sequence table to generate a
field value.
String generator: The name of a generator defined in mapping
metadata. We show you how to define named generators in
Section 5, “
Generators
”. If the
GenerationType is set but this property is unset, the JPA
implementation uses appropriate defaults for the selected generation type.
The equivalent XML element is generated-value, which
includes the following attributes:
strategy: One of TABLE,
SEQUENCE, IDENTITY, or AUTO,
defaulting to AUTO.
generator: Equivalent to the generator property listed
above.
OpenJPA allows you to use the GeneratedValue annotation
on any field, not just identity fields. Before using the IDENTITY
generation strategy, however, read
Section 3.3, “
Autoassign / Identity Strategy Caveats
” in the Reference Guide.
OpenJPA also offers two additional generator strategies for non-numeric fields,
which you can access by setting strategy to AUTO
(the default), and setting the generator string
to:
uuid-string: OpenJPA will generate a 128-bit UUID unique
within the network, represented as a 16-character string. For more information
on UUIDs, see the IETF UUID draft specification at:
http://www1.ics.uci.edu/~ejw/authoring/uuid-guid/
uuid-hex: Same as uuid-string, but
represents the UUID as a 32-character hexadecimal string.
These string constants are defined in
org.apache.openjpa.persistence.Generator.
If your entity has multiple identity values, you may declare multiple
@Id fields, or you may declare a single @EmbeddedId
field. The type of a field annotated with EmbeddedId must
be an embeddable entity class. The fields of this embeddable class are
considered the identity values of the owning entity. We explore entity identity
and identity fields in Section 1.3, “
Identity Fields
”.
The EmbeddedId annotation has no properties.
The equivalent XML element is embedded-id. It has one
required attribute:
name: The name of the identity field or property.
Use the Version annotation to designate a version field.
Section 1.4, “
Version Field
” explained the importance of
version fields to JPA. This is a marker annotation; it has no properties.
The equivalent XML element is version, which has a single
attribute:
name: The name of the version field or property. This
attribute is required.
Basic signifies a standard value persisted as-is to the
datastore. You can use the Basic annotation on persistent
fields of the following types: primitives, primitive wrappers,
java.lang.String, byte[],
Byte[], char[],
Character[], java.math.BigDecimal,
java.math.BigInteger,
java.util.Date, java.util.Calendar,
java.sql.Date, java.sql.Timestamp,
Enums, and Serializable types.
Basic declares these properties:
FetchType fetch: Whether to load the field eagerly
(FetchType.EAGER) or lazily (
FetchType.LAZY). Defaults to FetchType.EAGER.
boolean optional: Whether the datastore allows null values.
Defaults to true.
The equivalent XML element is basic. It has the following
attributes:
name: The name of the field or property. This attribute is
required.
fetch: One of EAGER or LAZY
.
optional: Boolean indicating whether the field value may be
null.
Many metadata annotations in JPA have a fetch property. This
property can take on one of two values: FetchType.EAGER or
FetchType.LAZY. FetchType.EAGER means that
the field is loaded by the JPA implementation before it returns the persistent
object to you. Whenever you retrieve an entity from a query or from the
EntityManager, you are guaranteed that all of its eager
fields are populated with datastore data.
FetchType.LAZY is a hint to the JPA runtime that you want to
defer loading of the field until you access it. This is called lazy
loading. Lazy loading is completely transparent; when you attempt to
read the field for the first time, the JPA runtime will load the value from the
datastore and populate the field automatically. Lazy loading is only a hint and
not a directive because some JPA implementations cannot lazy-load certain field
types.
With a mix of eager and lazily-loaded fields, you can ensure that commonly-used fields load efficiently, and that other state loads transparently when accessed. As you will see in Section 3, “ Persistence Context ”, you can also use eager fetching to ensure that entites have all needed data loaded before they become detached at the end of a persistence context.
OpenJPA can lazy-load any field type. OpenJPA also allows you to dynamically change which fields are eagerly or lazily loaded at runtime. See Section 6, “ Fetch Groups ” in the Reference Guide for details.
The Reference Guide details OpenJPA's eager fetching behavior in Section 7, “ Eager Fetching ”.
Use the Embedded marker annotation on embeddable field
types. Embedded fields are mapped as part of the datastore record of the
declaring entity. In our sample model, Author and
Company each embed their Address,
rather than forming a relation to an Address as a
separate entity.
The equivalent XML element is embedded, which expects a
single attribute:
name: The name of the field or property. This attribute is
required.
When an entity A references a single entity
B, and other As might also reference the same
B, we say there is a many to one
relation from A to B. In our sample
model, for example, each magazine has a reference to its publisher. Multiple
magazines might have the same publisher. We say, then, that the
Magazine.publisher field is a many to one relation from magazines to
publishers.
JPA indicates many to one relations between entities with the
ManyToOne annotation. This annotation has the following properties:
Class targetEntity: The class of the related entity type.
CascadeType[] cascade: Array of enum values defining cascade
behavior for this field. We explore cascades below. Defaults to an empty array.
FetchType fetch: Whether to load the field eagerly
(FetchType.EAGER) or lazily
(FetchType.LAZY). Defaults to
FetchType.EAGER. See Section 2.6.1, “
Fetch Type
” above
for details on fetch types.
boolean optional: Whether the related object must exist. If
false, this field cannot be null. Defaults to
true.
The equivalent XML element is many-to-one. It accepts the
following attributes:
name: The name of the field or property. This attribute is
required.
target-entity: The class of the related type.
fetch: One of EAGER or
LAZY.
optional: Boolean indicating whether the field value may be
null.
We introduce the JPA EntityManager in
Chapter 8,
EntityManager
. The EntityManager
has APIs to persist new entities, remove (delete) existing
entities, refresh entity state from the datastore, and merge detached
entity state back into the persistence context. We explore all of
these APIs in detail later in the overview.
When the EntityManager is performing the above
operations, you can instruct it to automatically cascade the operation to the
entities held in a persistent field with the cascade property
of your metadata annotation. This process is recursive. The cascade
property accepts an array of CascadeType enum
values.
CascadeType.PERSIST: When persisting an entity, also persist
the entities held in this field. We suggest liberal application of this cascade
rule, because if the EntityManager finds a field that
references a new entity during flush, and the field does not use
CascadeType.PERSIST, it is an error.
CascadeType.REMOVE: When deleting an entity, also delete the
entities held in this field.
CascadeType.REFRESH: When refreshing an entity, also refresh
the entities held in this field.
CascadeType.MERGE: When merging entity state, also merge the
entities held in this field.
CascadeType defines one additional value,
CascadeType.ALL, that acts as a shortcut for all of the values above.
The following annotations are equivalent:
@ManyToOne(cascade={CascadeType.PERSIST,CascadeType.REMOVE,
CascadeType.REFRESH,CascadeType.MERGE})
private Company publisher;
@ManyToOne(cascade=CascadeType.ALL) private Company publisher;
In XML, these enumeration constants are available as child elements of the
cascade element. The cascade element is
itself a child of many-to-one. The following examples are
equivalent:
<many-to-one name="publisher">
<cascade>
<cascade-persist/>
<cascade-merge/>
<cascade-remove/>
<cascade-refresh/>
</cascade>
</many-to-one>
<many-to-one name="publisher">
<cascade>
<cascade-all/>
</cascade>
</many-to-one>
When an entity A references multiple B
entities, and no two As reference the same
B, we say there is a one to many relation from
A to B.
One to many relations are the exact inverse of the many to one relations we
detailed in the preceding section. In that section, we said that the
Magazine.publisher field is a many to one relation from magazines to
publishers. Now, we see that the Company.mags field is the
inverse - a one to many relation from publishers to magazines. Each company may
publish multiple magazines, but each magazine can have only one publisher.
JPA indicates one to many relations between entities with the
OneToMany annotation. This annotation has the following properties:
Class targetEntity: The class of the related entity type.
This information is usually taken from the parameterized collection or map
element type. You must supply it explicitly, however, if your field isn't a
parameterized type.
String mappedBy: Names the many to one field in the related
entity that maps this bidirectional relation. We explain bidirectional relations
below. Leaving this property unset signals that this is a standard
unidirectional relation.
CascadeType[] cascade: Array of enum values defining cascade
behavior for the collection elements. We explore cascades above in
Section 2.8.1, “
Cascade Type
”. Defaults to an empty array.
FetchType fetch: Whether to load the field eagerly
(FetchType.EAGER) or lazily
(FetchType.LAZY). Defaults to
FetchType.LAZY. See Section 2.6.1, “
Fetch Type
” above
for details on fetch types.
The equivalent XML element is one-to-many, which includes
the following attributes:
name: The name of the field or property. This attribute is
required.
target-entity: The class of the related type.
fetch: One of EAGER or
LAZY.
mapped-by: The name of the field or property that owns the
relation. See Section 2, “
Field and Property Metadata
”.
You may also nest the cascade element within a
one-to-many element.
When two fields are logical inverses of each other, they form a
bidirectional relation. Our model contains two bidirectional
relations: Magazine.publisher and Company.mags
form one bidirectional relation, and Article.authors
and Author.articles form the other. In both cases,
there is a clear link between the two fields that form the relationship. A
magazine refers to its publisher while the publisher refers to all its published
magazines. An article refers to its authors while each author refers to her
written articles.
When the two fields of a bidirectional relation share the same datastore
mapping, JPA formalizes the connection with the mappedBy
property. Marking Company.mags as mappedBy
Magazine.publisher means two things:
Company.mags uses the datastore mapping for
Magazine.publisher, but inverses it. In fact, it is illegal to
specify any additional mapping information when you use the mappedBy
property. All mapping information is read from the referenced field.
We explore mapping in depth in Chapter 12,
Mapping Metadata
.
Magazine.publisher is the "owner" of the relation. The field
that specifies the mapping data is always the owner. This means that changes to
the Magazine.publisher field are reflected in the datastore,
while changes to the Company.mags field alone are not.
Changes to Company.mags may still affect the JPA
implementation's cache, however. Thus, it is very important that you keep your
object model consistent by properly maintaining both sides of your bidirectional
relations at all times.
You should always take advantage of the mappedBy property
rather than mapping each field of a bidirectional relation independently.
Failing to do so may result in the JPA implementation trying to update the
database with conflicting data. Be careful to only mark one side of the relation
as mappedBy, however. One side has to actually do the
mapping!
You can configure OpenJPA to automatically synchronize both sides of a bidirectional relation, or to perform various actions when it detects inconsistent relations. See Section 4, “ Managed Inverses ” in the Reference Guide for details.
When an entity A references a single entity
B, and no other As can reference the same
B, we say there is a one to one relation between
A and B. In our sample model,
Magazine has a one to one relation to Article
through the Magazine.coverArticle field. No two magazines can
have the same cover article.
JPA indicates one to one relations between entities with the
OneToOne annotation. This annotation has the following properties:
Class targetEntity: The class of the related entity type.
This information is usually taken from the field type.
String mappedBy: Names the field in the related entity that
maps this bidirectional relation. We explain bidirectional relations in
Section 2.9.1, “
Bidirectional Relations
” above. Leaving this property
unset signals that this is a standard unidirectional relation.
CascadeType[] cascade: Array of enum values defining cascade
behavior for this field. We explore cascades in
Section 2.8.1, “
Cascade Type
” above. Defaults to an empty
array.
FetchType fetch: Whether to load the field eagerly
(FetchType.EAGER) or lazily
(FetchType.LAZY). Defaults to
FetchType.EAGER. See Section 2.6.1, “
Fetch Type
” above
for details on fetch types.
boolean optional: Whether the related object must exist. If
false, this field cannot be null. Defaults to
true.
The equivalent XML element is one-to-one which understands
the following attributes:
name: The name of the field or property. This attribute is
required.
target-entity: The class of the related type.
fetch: One of EAGER or
LAZY.
mapped-by: The field that owns the relation. See
Section 2, “
Field and Property Metadata
”.
You may also nest the cascade element within a
one-to-one element.
When an entity A references multiple B
entities, and other As might reference some of the same
Bs, we say there is a many to many
relation between A and B. In our sample
model, for example, each article has a reference to all the authors that
contributed to the article. Other articles might have some of the same authors.
We say, then, that Article and Author
have a many to many relation through the Article.authors
field.
JPA indicates many to many relations between entities with the
ManyToMany annotation. This annotation has the following properties:
Class targetEntity: The class of the related entity type.
This information is usually taken from the parameterized collection or map
element type. You must supply it explicitly, however, if your field isn't a
parameterized type.
String mappedBy: Names the many to many field in the related
entity that maps this bidirectional relation. We explain bidirectional relations
in Section 2.9.1, “
Bidirectional Relations
” above. Leaving this
property unset signals that this is a standard unidirectional relation.
CascadeType[] cascade: Array of enum values defining cascade
behavior for the collection elements. We explore cascades above in
Section 2.8.1, “
Cascade Type
”. Defaults to an empty array.
FetchType fetch: Whether to load the field eagerly
(FetchType.EAGER) or lazily
(FetchType.LAZY). Defaults to
FetchType.LAZY. See Section 2.6.1, “
Fetch Type
” above
for details on fetch types.
The equivalent XML element is many-to-many. It accepts the
following attributes:
name: The name of the field or property. This attribute is
required.
target-entity: The class of the related type.
fetch: One of EAGER or
LAZY.
mapped-by: The field that owns the relation. See
Section 2, “
Field and Property Metadata
”.
You may also nest the cascade element within a
many-to-many element.
Datastores such as relational databases do not preserve the order of records.
Your persistent List fields might be ordered one way the
first time you retrieve an object from the datastore, and a completely different
way the next. To ensure consistent ordering of collection fields, you must use
the OrderBy annotation. The OrderBy
annotation's value is a string defining the order of the collection
elements. An empty value means to sort on the identity value(s) of the elements
in ascending order. Any other value must be of the form:
<field name>[ ASC|DESC][, ...]
Each <field name> is the name of a persistent field in
the collection's element type. You can optionally follow each field by the
keyword ASC for ascending order, or DESC
for descending order. If the direction is omitted, it defaults to ascending.
The equivalent XML element is order-by which can be listed as
a sub-element of the one-to-many or many-to-many
elements. The text within this element is parsed as the order by
string.
OpenJPA expands the available ordering syntax. See ??? in the Reference Guide for details.
JPA supports persistent Map fields through either a
OneToMany
or ManyToMany
association. The related entities form the map values. JPA
derives the map keys by extracting a field from each entity value. The
MapKey annotation designates the field that is used as
the key. It has the following properties:
String name: The name of a field in the related entity class
to use as the map key. If no name is given, defaults to the identity field of
the related entity class.
The equivalent XML element is map-key which can be listed as
a sub-element of the one-to-many or many-to-many
elements. The map-key element has the following
attributes:
name: The name of the field in the related entity class to
use as the map key.
In the absence of any of the annotations above, JPA defines the following default behavior for declared fields:
Fields declared static, transient, or final
default to non-persistent.
Fields of any primitive type, primitive wrapper type,
java.lang.String, byte[],
Byte[], char[],
Character[], java.math.BigDecimal,
java.math.BigInteger,
java.util.Date, java.util.Calendar,
java.sql.Date, java.sql.Timestamp,
or any Serializable type default to persistent, as if
annotated with
@Basic.
Fields of an embeddable type default to persistent, as if annotated with
@Embedded.
All other fields default to non-persistent.
Note that according to these defaults, all relations between entities must be annotated explicitly. Without an annotation, a relation field will default to serialized storage if the related entity type is serializable, or will default to being non-persistent if not.
We present the complete XML schema below. Many of the elements relate to object/relational mapping rather than metadata; these elements are discussed in Chapter 12, Mapping Metadata .
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="http://java.sun.com/xml/ns/persistence/orm"
xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
version="1.0">
<xsd:annotation>
<xsd:documentation>
@(#)orm_1_0.xsd 1.0 Feb 14 2006
</xsd:documentation>
</xsd:annotation>
<xsd:annotation>
<xsd:documentation>
This is the XML Schema for the persistence object-relational
mapping file.
The file may be named "META-INF/orm.xml" in the persistence
archive or it may be named some other name which would be
used to locate the file as resource on the classpath.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType name="emptyType"/>
<xsd:simpleType name="versionType">
<xsd:restriction base="xsd:token">
<xsd:pattern value="[0-9]+(\.[0-9]+)*"/>
</xsd:restriction>
</xsd:simpleType>
<!-- **************************************************** -->
<xsd:element name="entity-mappings">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation>
The entity-mappings element is the root element of an mapping
file. It contains the following four types of elements:
1. The persistence-unit-metadata element contains metadata
for the entire persistence unit. It is undefined if this element
occurs in multiple mapping files within the same persistence unit.
2. The package, schema, catalog and access elements apply to all of
the entity, mapped-superclass and embeddable elements defined in
the same file in which they occur.
3. The sequence-generator, table-generator, named-query,
named-native-query and sql-result-set-mapping elements are global
to the persistence unit. It is undefined to have more than one
sequence-generator or table-generator of the same name in the same
or different mapping files in a persistence unit. It is also
undefined to have more than one named-query or named-native-query
of the same name in the same or different mapping files in a
persistence unit.
4. The entity, mapped-superclass and embeddable elements each define
the mapping information for a managed persistent class. The mapping
information contained in these elements may be complete or it may
be partial.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="description" type="xsd:string"
minOccurs="0"/>
<xsd:element name="persistence-unit-metadata"
type="orm:persistence-unit-metadata"
minOccurs="0"/>
<xsd:element name="package" type="xsd:string"
minOccurs="0"/>
<xsd:element name="schema" type="xsd:string"
minOccurs="0"/>
<xsd:element name="catalog" type="xsd:string"
minOccurs="0"/>
<xsd:element name="access" type="orm:access-type"
minOccurs="0"/>
<xsd:element name="sequence-generator" type="orm:sequence-generator"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="table-generator" type="orm:table-generator"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="named-query" type="orm:named-query"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="named-native-query" type="orm:named-native-query"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="sql-result-set-mapping"
type="orm:sql-result-set-mapping"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="mapped-superclass" type="orm:mapped-superclass"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="entity" type="orm:entity"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="embeddable" type="orm:embeddable"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="version" type="orm:versionType"
fixed="1.0" use="required"/>
</xsd:complexType>
</xsd:element>
<!-- **************************************************** -->
<xsd:complexType name="persistence-unit-metadata">
<xsd:annotation>
<xsd:documentation>
Metadata that applies to the persistence unit and not just to
the mapping file in which it is contained.
If the xml-mapping-metadata-complete element is specified then
the complete set of mapping metadata for the persistence unit
is contained in the XML mapping files for the persistence unit.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="xml-mapping-metadata-complete" type="orm:emptyType"
minOccurs="0"/>
<xsd:element name="persistence-unit-defaults"
type="orm:persistence-unit-defaults"
minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="persistence-unit-defaults">
<xsd:annotation>
<xsd:documentation>
These defaults are applied to the persistence unit as a whole
unless they are overridden by local annotation or XML
element settings.
schema - Used as the schema for all tables or secondary tables
that apply to the persistence unit
catalog - Used as the catalog for all tables or secondary tables
that apply to the persistence unit
access - Used as the access type for all managed classes in
the persistence unit
cascade-persist - Adds cascade-persist to the set of cascade options
in entity relationships of the persistence unit
entity-listeners - List of default entity listeners to be invoked
on each entity in the persistence unit.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="schema" type="xsd:string"
minOccurs="0"/>
<xsd:element name="catalog" type="xsd:string"
minOccurs="0"/>
<xsd:element name="access" type="orm:access-type"
minOccurs="0"/>
<xsd:element name="cascade-persist" type="orm:emptyType"
minOccurs="0"/>
<xsd:element name="entity-listeners" type="orm:entity-listeners"
minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="entity">
<xsd:annotation>
<xsd:documentation>
Defines the settings and mappings for an entity. Is allowed to be
sparsely populated and used in conjunction with the annotations.
Alternatively, the metadata-complete attribute can be used to
indicate that no annotations on the entity class (and its fields
or properties) are to be processed. If this is the case then
the defaulting rules for the entity and its subelements will
be recursively applied.
@Target(TYPE) @Retention(RUNTIME)
public @interface Entity {
String name() default "";
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="description" type="xsd:string" minOccurs="0"/>
<xsd:element name="table" type="orm:table"
minOccurs="0"/>
<xsd:element name="secondary-table" type="orm:secondary-table"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="primary-key-join-column"
type="orm:primary-key-join-column"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="id-class" type="orm:id-class" minOccurs="0"/>
<xsd:element name="inheritance" type="orm:inheritance" minOccurs="0"/>
<xsd:element name="discriminator-value" type="orm:discriminator-value"
minOccurs="0"/>
<xsd:element name="discriminator-column"
type="orm:discriminator-column"
minOccurs="0"/>
<xsd:element name="sequence-generator" type="orm:sequence-generator"
minOccurs="0"/>
<xsd:element name="table-generator" type="orm:table-generator"
minOccurs="0"/>
<xsd:element name="named-query" type="orm:named-query"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="named-native-query" type="orm:named-native-query"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="sql-result-set-mapping"
type="orm:sql-result-set-mapping"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="exclude-default-listeners" type="orm:emptyType"
minOccurs="0"/>
<xsd:element name="exclude-superclass-listeners" type="orm:emptyType"
minOccurs="0"/>
<xsd:element name="entity-listeners" type="orm:entity-listeners"
minOccurs="0"/>
<xsd:element name="pre-persist" type="orm:pre-persist" minOccurs="0"/>
<xsd:element name="post-persist" type="orm:post-persist"
minOccurs="0"/>
<xsd:element name="pre-remove" type="orm:pre-remove" minOccurs="0"/>
<xsd:element name="post-remove" type="orm:post-remove" minOccurs="0"/>
<xsd:element name="pre-update" type="orm:pre-update" minOccurs="0"/>
<xsd:element name="post-update" type="orm:post-update" minOccurs="0"/>
<xsd:element name="post-load" type="orm:post-load" minOccurs="0"/>
<xsd:element name="attribute-override" type="orm:attribute-override"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="association-override"
type="orm:association-override"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="attributes" type="orm:attributes" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="class" type="xsd:string" use="required"/>
<xsd:attribute name="access" type="orm:access-type"/>
<xsd:attribute name="metadata-complete" type="xsd:boolean"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="attributes">
<xsd:annotation>
<xsd:documentation>
This element contains the entity field or property mappings.
It may be sparsely populated to include only a subset of the
fields or properties. If metadata-complete for the entity is true
then the remainder of the attributes will be defaulted according
to the default rules.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:choice>
<xsd:element name="id" type="orm:id"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="embedded-id" type="orm:embedded-id"
minOccurs="0"/>
</xsd:choice>
<xsd:element name="basic" type="orm:basic"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="version" type="orm:version"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="many-to-one" type="orm:many-to-one"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="one-to-many" type="orm:one-to-many"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="one-to-one" type="orm:one-to-one"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="many-to-many" type="orm:many-to-many"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="embedded" type="orm:embedded"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="transient" type="orm:transient"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:simpleType name="access-type">
<xsd:annotation>
<xsd:documentation>
This element determines how the persistence provider accesses the
state of an entity or embedded object.
</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:token">
<xsd:enumeration value="PROPERTY"/>
<xsd:enumeration value="FIELD"/>
</xsd:restriction>
</xsd:simpleType>
<!-- **************************************************** -->
<xsd:complexType name="entity-listeners">
<xsd:annotation>
<xsd:documentation>
@Target({TYPE}) @Retention(RUNTIME)
public @interface EntityListeners {
Class[] value();
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="entity-listener" type="orm:entity-listener"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="entity-listener">
<xsd:annotation>
<xsd:documentation>
Defines an entity listener to be invoked at lifecycle events
for the entities that list this listener.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="pre-persist" type="orm:pre-persist" minOccurs="0"/>
<xsd:element name="post-persist" type="orm:post-persist"
minOccurs="0"/>
<xsd:element name="pre-remove" type="orm:pre-remove" minOccurs="0"/>
<xsd:element name="post-remove" type="orm:post-remove" minOccurs="0"/>
<xsd:element name="pre-update" type="orm:pre-update" minOccurs="0"/>
<xsd:element name="post-update" type="orm:post-update" minOccurs="0"/>
<xsd:element name="post-load" type="orm:post-load" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="class" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="pre-persist">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD}) @Retention(RUNTIME)
public @interface PrePersist {}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="method-name" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="post-persist">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD}) @Retention(RUNTIME)
public @interface PostPersist {}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="method-name" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="pre-remove">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD}) @Retention(RUNTIME)
public @interface PreRemove {}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="method-name" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="post-remove">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD}) @Retention(RUNTIME)
public @interface PostRemove {}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="method-name" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="pre-update">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD}) @Retention(RUNTIME)
public @interface PreUpdate {}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="method-name" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="post-update">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD}) @Retention(RUNTIME)
public @interface PostUpdate {}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="method-name" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="post-load">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD}) @Retention(RUNTIME)
public @interface PostLoad {}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="method-name" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="query-hint">
<xsd:annotation>
<xsd:documentation>
@Target({}) @Retention(RUNTIME)
public @interface QueryHint {
String name();
String value();
}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="value" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="named-query">
<xsd:annotation>
<xsd:documentation>
@Target({TYPE}) @Retention(RUNTIME)
public @interface NamedQuery {
String name();
String query();
QueryHint[] hints() default {};
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="query" type="xsd:string"/>
<xsd:element name="hint" type="orm:query-hint"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="named-native-query">
<xsd:annotation>
<xsd:documentation>
@Target({TYPE}) @Retention(RUNTIME)
public @interface NamedNativeQuery {
String name();
String query();
QueryHint[] hints() default {};
Class resultClass() default void.class;
String resultSetMapping() default ""; //named SqlResultSetMapping
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="query" type="xsd:string"/>
<xsd:element name="hint" type="orm:query-hint"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="result-class" type="xsd:string"/>
<xsd:attribute name="result-set-mapping" type="xsd:string"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="sql-result-set-mapping">
<xsd:annotation>
<xsd:documentation>
@Target({TYPE}) @Retention(RUNTIME)
public @interface SqlResultSetMapping {
String name();
EntityResult[] entities() default {};
ColumnResult[] columns() default {};
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="entity-result" type="orm:entity-result"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="column-result" type="orm:column-result"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="entity-result">
<xsd:annotation>
<xsd:documentation>
@Target({}) @Retention(RUNTIME)
public @interface EntityResult {
Class entityClass();
FieldResult[] fields() default {};
String discriminatorColumn() default "";
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="field-result" type="orm:field-result"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="entity-class" type="xsd:string" use="required"/>
<xsd:attribute name="discriminator-column" type="xsd:string"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="field-result">
<xsd:annotation>
<xsd:documentation>
@Target({}) @Retention(RUNTIME)
public @interface FieldResult {
String name();
String column();
}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="column" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="column-result">
<xsd:annotation>
<xsd:documentation>
@Target({}) @Retention(RUNTIME)
public @interface ColumnResult {
String name();
}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="table">
<xsd:annotation>
<xsd:documentation>
@Target({TYPE}) @Retention(RUNTIME)
public @interface Table {
String name() default "";
String catalog() default "";
String schema() default "";
UniqueConstraint[] uniqueConstraints() default {};
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="unique-constraint" type="orm:unique-constraint"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="catalog" type="xsd:string"/>
<xsd:attribute name="schema" type="xsd:string"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="secondary-table">
<xsd:annotation>
<xsd:documentation>
@Target({TYPE}) @Retention(RUNTIME)
public @interface SecondaryTable {
String name();
String catalog() default "";
String schema() default "";
PrimaryKeyJoinColumn[] pkJoinColumns() default {};
UniqueConstraint[] uniqueConstraints() default {};
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="primary-key-join-column"
type="orm:primary-key-join-column"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="unique-constraint" type="orm:unique-constraint"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="catalog" type="xsd:string"/>
<xsd:attribute name="schema" type="xsd:string"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="unique-constraint">
<xsd:annotation>
<xsd:documentation>
@Target({}) @Retention(RUNTIME)
public @interface UniqueConstraint {
String[] columnNames();
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="column-name" type="xsd:string"
maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="column">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface Column {
String name() default "";
boolean unique() default false;
boolean nullable() default true;
boolean insertable() default true;
boolean updatable() default true;
String columnDefinition() default "";
String table() default "";
int length() default 255;
int precision() default 0; // decimal precision
int scale() default 0; // decimal scale
}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="unique" type="xsd:boolean"/>
<xsd:attribute name="nullable" type="xsd:boolean"/>
<xsd:attribute name="insertable" type="xsd:boolean"/>
<xsd:attribute name="updatable" type="xsd:boolean"/>
<xsd:attribute name="column-definition" type="xsd:string"/>
<xsd:attribute name="table" type="xsd:string"/>
<xsd:attribute name="length" type="xsd:int"/>
<xsd:attribute name="precision" type="xsd:int"/>
<xsd:attribute name="scale" type="xsd:int"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="join-column">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface JoinColumn {
String name() default "";
String referencedColumnName() default "";
boolean unique() default false;
boolean nullable() default true;
boolean insertable() default true;
boolean updatable() default true;
String columnDefinition() default "";
String table() default "";
}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="referenced-column-name" type="xsd:string"/>
<xsd:attribute name="unique" type="xsd:boolean"/>
<xsd:attribute name="nullable" type="xsd:boolean"/>
<xsd:attribute name="insertable" type="xsd:boolean"/>
<xsd:attribute name="updatable" type="xsd:boolean"/>
<xsd:attribute name="column-definition" type="xsd:string"/>
<xsd:attribute name="table" type="xsd:string"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:simpleType name="generation-type">
<xsd:annotation>
<xsd:documentation>
public enum GenerationType { TABLE, SEQUENCE, IDENTITY, AUTO };
</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:token">
<xsd:enumeration value="TABLE"/>
<xsd:enumeration value="SEQUENCE"/>
<xsd:enumeration value="IDENTITY"/>
<xsd:enumeration value="AUTO"/>
</xsd:restriction>
</xsd:simpleType>
<!-- **************************************************** -->
<xsd:complexType name="attribute-override">
<xsd:annotation>
<xsd:documentation>
@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
public @interface AttributeOverride {
String name();
Column column();
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="column" type="orm:column"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="association-override">
<xsd:annotation>
<xsd:documentation>
@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
public @interface AssociationOverride {
String name();
JoinColumn[] joinColumns();
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="join-column" type="orm:join-column"
maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="id-class">
<xsd:annotation>
<xsd:documentation>
@Target({TYPE}) @Retention(RUNTIME)
public @interface IdClass {
Class value();
}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="class" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="id">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface Id {}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="column" type="orm:column"
minOccurs="0"/>
<xsd:element name="generated-value" type="orm:generated-value"
minOccurs="0"/>
<xsd:element name="temporal" type="orm:temporal"
minOccurs="0"/>
<xsd:element name="table-generator" type="orm:table-generator"
minOccurs="0"/>
<xsd:element name="sequence-generator" type="orm:sequence-generator"
minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="embedded-id">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface EmbeddedId {}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="attribute-override" type="orm:attribute-override"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="transient">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface Transient {}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="version">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface Version {}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="column" type="orm:column" minOccurs="0"/>
<xsd:element name="temporal" type="orm:temporal" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="basic">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface Basic {
FetchType fetch() default EAGER;
boolean optional() default true;
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="column" type="orm:column" minOccurs="0"/>
<xsd:choice>
<xsd:element name="lob" type="orm:lob" minOccurs="0"/>
<xsd:element name="temporal" type="orm:temporal" minOccurs="0"/>
<xsd:element name="enumerated" type="orm:enumerated" minOccurs="0"/>
</xsd:choice>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="fetch" type="orm:fetch-type"/>
<xsd:attribute name="optional" type="xsd:boolean"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:simpleType name="fetch-type">
<xsd:annotation>
<xsd:documentation>
public enum FetchType { LAZY, EAGER };
</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:token">
<xsd:enumeration value="LAZY"/>
<xsd:enumeration value="EAGER"/>
</xsd:restriction>
</xsd:simpleType>
<!-- **************************************************** -->
<xsd:complexType name="lob">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface Lob {}
</xsd:documentation>
</xsd:annotation>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:simpleType name="temporal">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface Temporal {
TemporalType value();
}
</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="orm:temporal-type"/>
</xsd:simpleType>
<!-- **************************************************** -->
<xsd:simpleType name="temporal-type">
<xsd:annotation>
<xsd:documentation>
public enum TemporalType {
DATE, // java.sql.Date
TIME, // java.sql.Time
TIMESTAMP // java.sql.Timestamp
}
</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:token">
<xsd:enumeration value="DATE"/>
<xsd:enumeration value="TIME"/>
<xsd:enumeration value="TIMESTAMP"/>
</xsd:restriction>
</xsd:simpleType>
<!-- **************************************************** -->
<xsd:simpleType name="enumerated">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface Enumerated {
EnumType value() default ORDINAL;
}
</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="orm:enum-type"/>
</xsd:simpleType>
<!-- **************************************************** -->
<xsd:simpleType name="enum-type">
<xsd:annotation>
<xsd:documentation>
public enum EnumType {
ORDINAL,
STRING
}
</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:token">
<xsd:enumeration value="ORDINAL"/>
<xsd:enumeration value="STRING"/>
</xsd:restriction>
</xsd:simpleType>
<!-- **************************************************** -->
<xsd:complexType name="many-to-one">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface ManyToOne {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fetch() default EAGER;
boolean optional() default true;
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:choice>
<xsd:element name="join-column" type="orm:join-column"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="join-table" type="orm:join-table"
minOccurs="0"/>
</xsd:choice>
<xsd:element name="cascade" type="orm:cascade-type"
minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="target-entity" type="xsd:string"/>
<xsd:attribute name="fetch" type="orm:fetch-type"/>
<xsd:attribute name="optional" type="xsd:boolean"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="cascade-type">
<xsd:annotation>
<xsd:documentation>
public enum CascadeType { ALL, PERSIST, MERGE, REMOVE, REFRESH};
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="cascade-all" type="orm:emptyType"
minOccurs="0"/>
<xsd:element name="cascade-persist" type="orm:emptyType"
minOccurs="0"/>
<xsd:element name="cascade-merge" type="orm:emptyType"
minOccurs="0"/>
<xsd:element name="cascade-remove" type="orm:emptyType"
minOccurs="0"/>
<xsd:element name="cascade-refresh" type="orm:emptyType"
minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="one-to-one">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface OneToOne {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fetch() default EAGER;
boolean optional() default true;
String mappedBy() default "";
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:choice>
<xsd:element name="primary-key-join-column"
type="orm:primary-key-join-column"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="join-column" type="orm:join-column"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="join-table" type="orm:join-table"
minOccurs="0"/>
</xsd:choice>
<xsd:element name="cascade" type="orm:cascade-type"
minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="target-entity" type="xsd:string"/>
<xsd:attribute name="fetch" type="orm:fetch-type"/>
<xsd:attribute name="optional" type="xsd:boolean"/>
<xsd:attribute name="mapped-by" type="xsd:string"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="one-to-many">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface OneToMany {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fetch() default LAZY;
String mappedBy() default "";
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="order-by" type="orm:order-by"
minOccurs="0"/>
<xsd:element name="map-key" type="orm:map-key"
minOccurs="0"/>
<xsd:choice>
<xsd:element name="join-table" type="orm:join-table"
minOccurs="0"/>
<xsd:element name="join-column" type="orm:join-column"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:choice>
<xsd:element name="cascade" type="orm:cascade-type"
minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="target-entity" type="xsd:string"/>
<xsd:attribute name="fetch" type="orm:fetch-type"/>
<xsd:attribute name="mapped-by" type="xsd:string"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="join-table">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface JoinTable {
String name() default "";
String catalog() default "";
String schema() default "";
JoinColumn[] joinColumns() default {};
JoinColumn[] inverseJoinColumns() default {};
UniqueConstraint[] uniqueConstraints() default {};
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="join-column" type="orm:join-column"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="inverse-join-column" type="orm:join-column"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="unique-constraint" type="orm:unique-constraint"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="catalog" type="xsd:string"/>
<xsd:attribute name="schema" type="xsd:string"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="many-to-many">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface ManyToMany {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fetch() default LAZY;
String mappedBy() default "";
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="order-by" type="orm:order-by"
minOccurs="0"/>
<xsd:element name="map-key" type="orm:map-key"
minOccurs="0"/>
<xsd:element name="join-table" type="orm:join-table"
minOccurs="0"/>
<xsd:element name="cascade" type="orm:cascade-type"
minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="target-entity" type="xsd:string"/>
<xsd:attribute name="fetch" type="orm:fetch-type"/>
<xsd:attribute name="mapped-by" type="xsd:string"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="generated-value">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface GeneratedValue {
GenerationType strategy() default AUTO;
String generator() default "";
}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="strategy" type="orm:generation-type"/>
<xsd:attribute name="generator" type="xsd:string"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="map-key">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface MapKey {
String name() default "";
}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:simpleType name="order-by">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface OrderBy {
String value() default "";
}
</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:string"/>
</xsd:simpleType>
<!-- **************************************************** -->
<xsd:complexType name="inheritance">
<xsd:annotation>
<xsd:documentation>
@Target({TYPE}) @Retention(RUNTIME)
public @interface Inheritance {
InheritanceType strategy() default SINGLE_TABLE;
}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="strategy" type="orm:inheritance-type"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:simpleType name="inheritance-type">
<xsd:annotation>
<xsd:documentation>
public enum InheritanceType
{ SINGLE_TABLE, JOINED, TABLE_PER_CLASS};
</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:token">
<xsd:enumeration value="SINGLE_TABLE"/>
<xsd:enumeration value="JOINED"/>
<xsd:enumeration value="TABLE_PER_CLASS"/>
</xsd:restriction>
</xsd:simpleType>
<!-- **************************************************** -->
<xsd:simpleType name="discriminator-value">
<xsd:annotation>
<xsd:documentation>
@Target({TYPE}) @Retention(RUNTIME)
public @interface DiscriminatorValue {
String value();
}
</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:string"/>
</xsd:simpleType>
<!-- **************************************************** -->
<xsd:simpleType name="discriminator-type">
<xsd:annotation>
<xsd:documentation>
public enum DiscriminatorType { STRING, CHAR, INTEGER };
</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:token">
<xsd:enumeration value="STRING"/>
<xsd:enumeration value="CHAR"/>
<xsd:enumeration value="INTEGER"/>
</xsd:restriction>
</xsd:simpleType>
<!-- **************************************************** -->
<xsd:complexType name="primary-key-join-column">
<xsd:annotation>
<xsd:documentation>
@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
public @interface PrimaryKeyJoinColumn {
String name() default "";
String referencedColumnName() default "";
String columnDefinition() default "";
}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="referenced-column-name" type="xsd:string"/>
<xsd:attribute name="column-definition" type="xsd:string"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="discriminator-column">
<xsd:annotation>
<xsd:documentation>
@Target({TYPE}) @Retention(RUNTIME)
public @interface DiscriminatorColumn {
String name() default "DTYPE";
DiscriminatorType discriminatorType() default STRING;
String columnDefinition() default "";
int length() default 31;
}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="discriminator-type" type="orm:discriminator-type"/>
<xsd:attribute name="column-definition" type="xsd:string"/>
<xsd:attribute name="length" type="xsd:int"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="embeddable">
<xsd:annotation>
<xsd:documentation>
Defines the settings and mappings for embeddable objects. Is
allowed to be sparsely populated and used in conjunction with
the annotations. Alternatively, the metadata-complete attribute
can be used to indicate that no annotations are to be processed
in the class. If this is the case then the defaulting rules will
be recursively applied.
@Target({TYPE}) @Retention(RUNTIME)
public @interface Embeddable {}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="description" type="xsd:string" minOccurs="0"/>
<xsd:element name="attributes" type="orm:embeddable-attributes"
minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="class" type="xsd:string" use="required"/>
<xsd:attribute name="access" type="orm:access-type"/>
<xsd:attribute name="metadata-complete" type="xsd:boolean"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="embeddable-attributes">
<xsd:sequence>
<xsd:element name="basic" type="orm:basic"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="transient" type="orm:transient"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="embedded">
<xsd:annotation>
<xsd:documentation>
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface Embedded {}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="attribute-override" type="orm:attribute-override"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="mapped-superclass">
<xsd:annotation>
<xsd:documentation>
Defines the settings and mappings for a mapped superclass. Is
allowed to be sparsely populated and used in conjunction with
the annotations. Alternatively, the metadata-complete attribute
can be used to indicate that no annotations are to be processed
If this is the case then the defaulting rules will be recursively
applied.
@Target(TYPE) @Retention(RUNTIME)
public @interface MappedSuperclass{}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="description" type="xsd:string" minOccurs="0"/>
<xsd:element name="id-class" type="orm:id-class" minOccurs="0"/>
<xsd:element name="exclude-default-listeners" type="orm:emptyType"
minOccurs="0"/>
<xsd:element name="exclude-superclass-listeners" type="orm:emptyType"
minOccurs="0"/>
<xsd:element name="entity-listeners" type="orm:entity-listeners"
minOccurs="0"/>
<xsd:element name="pre-persist" type="orm:pre-persist" minOccurs="0"/>
<xsd:element name="post-persist" type="orm:post-persist"
minOccurs="0"/>
<xsd:element name="pre-remove" type="orm:pre-remove" minOccurs="0"/>
<xsd:element name="post-remove" type="orm:post-remove" minOccurs="0"/>
<xsd:element name="pre-update" type="orm:pre-update" minOccurs="0"/>
<xsd:element name="post-update" type="orm:post-update" minOccurs="0"/>
<xsd:element name="post-load" type="orm:post-load" minOccurs="0"/>
<xsd:element name="attributes" type="orm:attributes" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="class" type="xsd:string" use="required"/>
<xsd:attribute name="access" type="orm:access-type"/>
<xsd:attribute name="metadata-complete" type="xsd:boolean"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="sequence-generator">
<xsd:annotation>
<xsd:documentation>
@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
public @interface SequenceGenerator {
String name();
String sequenceName() default "";
int initialValue() default 1;
int allocationSize() default 50;
}
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="sequence-name" type="xsd:string"/>
<xsd:attribute name="initial-value" type="xsd:int"/>
<xsd:attribute name="allocation-size" type="xsd:int"/>
</xsd:complexType>
<!-- **************************************************** -->
<xsd:complexType name="table-generator">
<xsd:annotation>
<xsd:documentation>
@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
public @interface TableGenerator {
String name();
String table() default "";
String catalog() default "";
String schema() default "";
String pkColumnName() default "";
String valueColumnName() default "";
String pkColumnValue() default "";
int initialValue() default 0;
int allocationSize() default 50;
UniqueConstraint[] uniqueConstraints() default {};
}
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="unique-constraint" type="orm:unique-constraint"
minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="table" type="xsd:string"/>
<xsd:attribute name="catalog" type="xsd:string"/>
<xsd:attribute name="schema" type="xsd:string"/>
<xsd:attribute name="pk-column-name" type="xsd:string"/>
<xsd:attribute name="value-column-name" type="xsd:string"/>
<xsd:attribute name="pk-column-value" type="xsd:string"/>
<xsd:attribute name="initial-value" type="xsd:int"/>
<xsd:attribute name="allocation-size" type="xsd:int"/>
</xsd:complexType>
</xsd:schema>
That exhausts persistence metadata annotations. We present the class definitions for our sample model below:
Example 5.2. Complete Metadata
package org.mag;
@Entity
@IdClass(Magazine.MagazineId.class)
public class Magazine {
@Id private String isbn;
@Id private String title;
@Version private int version;
private double price; // defaults to @Basic
private int copiesSold; // defaults to @Basic
@OneToOne(fetch=FetchType.LAZY,
cascade={CascadeType.PERSIST,CascadeType.REMOVE})
private Article coverArticle;
@OneToMany(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
@OrderBy
private Collection<Article> articles;
@ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
private Company publisher;
@Transient private byte[] data;
...
public static class MagazineId {
...
}
}
@Entity
public class Article {
@Id private long id;
@Version private int version;
private String title; // defaults to @Basic
private byte[] content; // defaults to @Basic
@ManyToMany(cascade=CascadeType.PERSIST)
@OrderBy("lastName, firstName")
private Collection<Author> authors;
...
}
package org.mag.pub;
@Entity
public class Company {
@Id private long id;
@Version private int version;
private String name; // defaults to @Basic
private double revenue; // defaults to @Basic
private Address address; // defaults to @Embedded
@OneToMany(mappedBy="publisher", cascade=CascadeType.PERSIST)
private Collection<Magazine> mags;
@OneToMany(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
private Collection<Subscription> subscriptions;
...
}
@Entity
public class Author {
@Id private long id;
@Version private int version;
private String firstName; // defaults to @Basic
private double lastName; // defaults to @Basic
private Address address; // defaults to @Embedded
@ManyToMany(mappedBy="authors", cascade=CascadeType.PERSIST)
private Collection<Article> arts;
...
}
@Embeddable
public class Address {
private String street; // defaults to @Basic
private String city; // defaults to @Basic
private String state; // defaults to @Basic
private String zip; // defaults to @Basic
...
}
package org.mag.subscribe;
@MappedSuperclass
public abstract class Document {
@Id private long id;
@Version private int version;
...
}
@Entity
public class Contract
extends Document {
private String terms; // defaults to @Basic
...
}
@Entity
public class Subscription {
@Id private long id;
@Version private int version;
private Date startDate; // defaults to @Basic
private double payment; // defaults to @Basic
@OneToMany(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
@MapKey(name="num")
private Map<Long,LineItem> lineItems;
...
@Entity
public static class LineItem
extends Contract {
private String comments; // defaults to @Basic
private double price; // defaults to @Basic
private long num; // defaults to @Basic
@ManyToOne
private Magazine magazine;
...
}
}
@Entity(name="Lifetime")
public class LifetimeSubscription
extends Subscription {
@Basic(fetch=FetchType.LAZY)
private boolean getEliteClub() { ... }
public void setEliteClub(boolean elite) { ... }
...
}
@Entity(name="Trial")
public class TrialSubscription
extends Subscription {
public Date getEndDate() { ... }
public void setEndDate(Date end) { ... }
...
}
The same metadata declarations in XML:
<entity-mappings>
<!-- declares a default access type for all entities -->
<access-type>FIELD</access-type>
<mapped-superclass class="org.mag.subscribe.Document">
<attributes>
<id name="id">
<generated-value strategy="IDENTITY"/>
</id>
<version name="version"/>
</attributes>
</mapped-superclass>
<entity class="org.mag.Magazine">
<id-class="org.mag.Magazine$MagazineId"/>
<attributes>
<id name="isbn"/>
<id name="title"/>
<basic name="name"/>
<basic name="price"/>
<basic name="copiesSold"/>
<version name="version"/>
<many-to-one name="publisher" fetch="LAZY">
<cascade>
<cascade-persist/>
</cascade>
</many-to-one>
<one-to-many name="articles">
<order-by/>
<cascade>
<cascade-persist/>
<cascade-remove/>
</cascade>
</one-to-many>
<one-to-one name="coverArticle" fetch="LAZY">
<cascade>
<cascade-persist/>
<cascade-remove/>
</cascade>
</one-to-one>
<transient name="data"/>
</attributes>
</entity>
<entity class="org.mag.Article">
<attributes>
<id name="id"/>
<basic name="title"/>
<basic name="content"/>
<version name="version"/>
<many-to-many name="articles">
<order-by>lastName, firstName</order-by>
</many-to-many>
</attributes>
</entity>
<entity class="org.mag.pub.Company">
<attributes>
<id name="id"/>
<basic name="name"/>
<basic name="revenue"/>
<version name="version"/>
<one-to-many name="mags" mapped-by="publisher">
<cascade>
<cascade-persist/>
</cascade>
</one-to-many>
<one-to-many name="subscriptions">
<cascade>
<cascade-persist/>
<cascade-remove/>
</cascade>
</one-to-many>
</attributes>
</entity>
<entity class="org.mag.pub.Author">
<attributes>
<id name="id"/>
<basic name="firstName"/>
<basic name="lastName"/>
<version name="version"/>
<many-to-many name="arts" mapped-by="authors">
<cascade>
<cascade-persist/>
</cascade>
</many-to-many>
</attributes>
</entity>
<entity class="org.mag.subcribe.Contract">
<attributes>
<basic name="terms"/>
</attributes>
</entity>
<entity class="org.mag.subcribe.Subscription">
<attributes>
<id name="id"/>
<basic name="payment"/>
<basic name="startDate"/>
<version name="version"/>
<one-to-many name="items">
<map-key name="num">
<cascade>
<cascade-persist/>
<cascade-remove/>
</cascade>
</one-to-many>
</attributes>
</entity>
<entity class="org.mag.subscribe.Subscription.LineItem">
<attributes>
<basic name="comments"/>
<basic name="price"/>
<basic name="num"/>
<many-to-one name="magazine"/>
</attributes>
</entity>
<entity class="org.mag.subscribe.LifetimeSubscription" name="Lifetime"
access="PROPERTY">
<attributes>
<basic name="eliteClub" fetch="LAZY"/>
</attributes>
</entity>
<entity class="org.mag.subscribe.TrialSubscription" name="Trial">
<attributes>
<basic name="endDate"/>
</attributes>
</entity>
<embeddable class="org.mag.pub.Address">
<attributes>
<basic name="street"/>
<basic name="city"/>
<basic name="state"/>
<basic name="zip"/>
</attributes>
</embeddable>
</entity-mappings>
Chapter 12, Mapping Metadata will show you how to map your persistent classes to the datastore using additional annotations and XML markup. First, however, we turn to the JPA runtime APIs.
Table of Contents
![]() |
OpenJPA also includes the
OpenJPAPersistence helper class to provide
additional utility methods.
Within a container, you will typically use injection to
access an EntityManagerFactory. Applications operating
of a container, however, can use the
Persistence class to obtain
EntityManagerFactory objects in a vendor-neutral fashion.
public static EntityManagerFactory createEntityManagerFactory(String name); public static EntityManagerFactory createEntityManagerFactory(String name, Map props);
Each createEntityManagerFactory method searches the
system for an EntityManagerFactory definition with the
given name. Use null for an unnamed factory. The optional map
contains vendor-specific property settings used to further configure the
factory.
persistence.xml files define
EntityManagerFactories. The createEntityManagerFactory
methods search for persistence.xml files
within the META-INF directory of any CLASSPATH
element. For example, if your CLASSPATH contains
the conf directory, you could place an
EntityManagerFactory definition in
conf/META-INF/persistence.xml.
The persistence.xml file format obeys the following
Document Type Descriptor (DTD):
<!ELEMENT persistence (persistence-unit*)> <!ELEMENT persistence-unit (description?,provider?,jta-datasource?, non-jta-datasource?,(class|jar-file|mapping-file)*, exclude-unlisted-classes?,properties?)> <!ATTLIST persistence-unit name CDATA #REQUIRED> <!ATTLIST persistence-unit transaction-type (JTA|RESOURCE_LOCAL) "JTA"> <!ELEMENT description (#PCDATA)> <!ELEMENT provider (#PCDATA)> <!ELEMENT jta-datasource (#PCDATA)> <!ELEMENT non-jta-datasource (#PCDATA)> <!ELEMENT mapping-file (#PCDATA)> <!ELEMENT jar-file (#PCDATA)> <!ELEMENT class (#PCDATA)> <!ELEMENT exclude-unlisted-classes EMPTY> <!ELEMENT properties (property*)> <!ELEMENT property EMPTY> <!ATTLIST property name CDATA #REQUIRED> <!ATTLIST property value CDATA #REQUIRED>
The root element of a persistence.xml file is
persistence, which then contains one or more
persistence-unit definitions. Each persistence unit describes the
configuration for the entity managers created by the persistence unit's entity
manager factory. The persistence unit can specify these elements and attribtues.
name: This is the name you pass to the
Persistence.createEntityManagerFactory methods described above. The
name attribute is required.
transaction-type: Whether to use managed
(JTA) or local (RESOURCE_LOCAL)
transaction management.
provider: If you are using a third-party JPA vendor, this
element names its implementation of the
PersistenceProvider bootstrapping interface.
Set the provider to
org.apache.openjpa.persistence.PersistenceProviderImpl to use
OpenJPA.
jta-data-source: The JNDI name of a JDBC
DataSource that is automatically enlisted in JTA transactions. This
may be an XA DataSource.
non-jta-data-source: The JNDI name of a JDBC
DataSource that is not enlisted in JTA transactions.
mapping-file*: The resource names of XML mapping files for
entities and embeddable classes. You can also specify mapping information in an
orm.xml file in your META-INF
directory. If present, the orm.xml mapping file will be
read automatically.
jar-file*: The names of jar files containing entities and
embeddable classes. The implementation will scan the jar for annotated classes.
class*: The class names of entities and embeddable classes.
properties: This element contains nested property
elements used to specify vendor-specific settings. Each
property has a name attribute and a value attribute.
The Reference Guide's Chapter 2, Configuration describes OpenJPA's configuration properties.
Here is a typical persistence.xml file for a non-EE
environment:
Example 6.1. persistence.xml
<?xml version="1.0"?>
<persistence>
<persistence-unit name="openjpa">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<class>tutorial.Animal</class>
<class>tutorial.Dog</class>
<class>tutorial.Rabbit</class>
<class>tutorial.Snake</class>
<properties>
<property name="openjpa.ConnectionURL" value="jdbc:hsqldb:tutorial_database"/>
<property name="openjpa.ConnectionDriverName" value="org.hsqldb.jdbcDriver"/>
<property name="openjpa.ConnectionUserName" value="sa"/>
<property name="openjpa.ConnectionPassword" value=""/>
<property name="openjpa.Log" value="DefaultLevel=WARN, Tool=INFO"/>
</properties>
</persistence-unit>
</persistence>
The example below demonstrates the Persistence class in
action. You will typically execute code like this on application startup, then
cache the resulting factory for future use. This bootstrapping code is only
necessary in non-EE environments; in an EE environment
EntityManagerFactories are typically injected.
Example 6.2. Obtaining an EntityManagerFactory
// if your persistence.xml file does not contain all settings already, you
// can add vendor settings to a map
Properties props = new Properties();
...
// create the factory defined by the "openjpa" entity-manager entry
EntityManagerFactory emf = Persistence.createEntityManagerFactory("openjpa", props);
Table of Contents
![]() |
The EntityManagerFactory creates
EntityManager instances for application use.
OpenJPA extends the standard EntityManagerFactory
interface with the
OpenJPAEntityManagerFactory to provide additional
functionality.
Within a container, you will typically use injection to
access an EntityManagerFactory. There are, however,
alternative mechanisms for EntityManagerFactory
construction.
Some vendors may supply public constructors for their
EntityManagerFactory implementations, but we recommend using the
Java Connector Architecture (JCA) in a managed environment, or the
Persistence class' createEntityManagerFactory
methods in an unmanaged environment, as described in
Chapter 6,
Persistence
. These strategies allow
vendors to pool factories, cutting down on resource utilization.
JPA allows you to create and configure an
EntityManagerFactory, then store it in a Java Naming and Directory
Interface (JNDI) tree for later retrieval and use.
public EntityManager createEntityManager(); public EntityManager createEntityManager(Map map);
The two createEntityManager methods above create a new
EntityManager each time they are invoked. The optional
Map is used to to supply vendor-specific settings. If you
have configured your implementation for JTA transactions and a JTA transaction
is active, the returned EntityManager will be
synchronized with that transaction.
OpenJPA recognizes the following string keys in the map supplied to
createEntityManager:
openjpa.ConnectionUserName
openjpa.ConnectionPassword
openjpa.ConnectionRetainMode
openjpa.TransactionMode
openjpa.<property>, where <property>
is any JavaBean property of the
org.apache.openjpa.persistence.OpenJPAEntityManager.
The last option uses reflection to configure any property of OpenJPA's
EntityManager implementation with the value supplied in
your map. The first options correspond exactly to the same-named OpenJPA
configuration keys described in Chapter 2,
Configuration
of the
Reference Guide.
A persistence context is a set of entities such that for any persistent identity
there is a unique entity instance. Within a persistence context, entities are
managed. The EntityManager controls
their lifecycle, and they can access datastore resources.
When a persistence context ends, previously-managed entities become
detached. A detached entity is no longer under the control of the
EntityManager, and no longer has access to datastore
resources. We discuss detachment is detail in
Section 2, “
Entity Lifecycle Management
”. For now, it is sufficient to
know that detachment as has two obvious consequences:
The detached entity cannot load any additional persistent state.
The EntityManager will not return the detached entity
from find, nor will queries include the detached
entity in their results. Instead, find method
invocations and query executions that would normally incorporate the detached
entity will create a new managed entity with the same identity.
OpenJPA offers several features related to detaching entities. See
Section 1, “
Detach and Attach
” in the Reference Guide.
Section 1.3, “
Defining the Detached Object Graph
” in particular describes how to
use the DetachState setting to boost the performance of
merging detached entities.
Injected EntityManagers have use a transaction
, while EntityManagers obtained through the
EntityManagerFactory have an extended
persistence context. We describe these persistence context types
below.
Under the transaction persistence context model, an EntityManager
begins a new persistence context with each transaction, and ends
the context when the transaction commits or rolls back. Within the transaction,
entities you retrieve through the EntityManager or via
Queries are managed entities. They can access datastore
resources to lazy-load additional persistent state as needed, and only one
entity may exist for any persistent identity.
When the transaction completes, all entities lose their association with the
EntityManager and become detached. Traversing a
persistent field that wasn't already loaded now has undefined results. And using
the EntityManager or a Query to
retrieve additional objects may now create new instances with the same
persistent identities as detached instances.
If you use an EntityManager with a transaction
persistence context model outside of an active transaction, each method
invocation creates a new persistence context, performs the method action, and
ends the persistence context. For example, consider using the
EntityManager.find method outside of a transaction. The
EntityManager will create a temporary persistence context, perform
the find operation, end the persistence context, and return the detached result
object to you. A second call with the same id will return a second detached
object.
When the next transaction begins, the EntityManager will
begin a new persistence context, and will again start returning managed
entities. As you'll see in Chapter 8,
EntityManager
, you can
also merge the previously-detached entites back into the new persistence
context.
Example 7.1. Behavior of Transaction Persistence Context
The following code illustrates the behavior of entites under an
EntityManager using a transaction persistence context.
EntityManager em; // injected ... // outside a transaction: // each operation occurs in a separate persistence context, and returns // a new detached instance Magazine mag1 = em.find(Magazine.class, magId); Magazine mag2 = em.find(Magazine.class, magId); assertTrue(mag2 != mag1); ... // transaction begins: // within a transaction, a subsequent lookup doesn't return any of the // detached objects. however, two lookups within the same transaction // return the same instance, because the persistence context spans the // transaction Magazine mag3 = em.find(Magazine.class, magId); assertTrue(mag3 != mag1 && mag3 != mag2); Magazine mag4 = em.find(Magazine.class (magId); assertTrue(mag4 == mag3); ... // transaction commits: // once again, each operation returns a new instance Magazine mag5 = em.find(Magazine.class, magId); assertTrue(mag5 != mag3);
An EntityManager using an extended persistence context
maintains the same persistence context for its entire lifecycle. Whether inside
a transaction or not, all entities returned from the EntityManager
are managed, and the EntityManager never
creates two entity instances to represent the same persistent identity. Entities
only become detached when you finally close the EntityManager
(or when they are serialized).
Example 7.2. Behavior of Extended Persistence Context
The following code illustrates the behavior of entites under an
EntityManager using an extended persistence context.
EntityManagerFactory emf = ... EntityManager em = emf.createEntityManager(); // persistence context active for entire life of EM, so only one entity // for a given persistent identity Magazine mag1 = em.find(Magazine.class, magId); Magazine mag2 = em.find(Magazine.class, magId); assertTrue(mag2 == mag1); em.getTransaction().begin(); // same persistence context active within the transaction Magazine mag3 = em.find(Magazine.class, magId); assertTrue(mag3 == mag1); Magazine mag4 = em.find(Magazine.class (magId); assertTrue(mag4 == mag1); em.getTransaction.commit (); // when the transaction commits, instance still managed Magazine mag5 = em.find(Magazine.class, magId); assertTrue(mag5 == mag1); // instance finally becomes detached when EM closes em.close();
public boolean isOpen (); public void close ();
EntityManagerFactory instances are heavyweight objects.
Each factory might maintain a metadata cache, object state cache,
EntityManager pool, connection pool, and more. If your application
no longer needs an EntityManagerFactory, you should
close it to free these resources. When an EntityManagerFactory
closes, all EntityManagers from that
factory, and by extension all entities managed by those
EntityManager s, become invalid. Attempting to close an
EntityManagerFactory while one or more of its
EntityManagers has an active transaction may result in an
IllegalStateException.
Closing an EntityManagerFactory should not be taken
lightly. It is much better to keep a factory open for a long period of time than
to repeatedly create and close new factories. Thus, most applications will never
close the factory, or only close it when the application is exiting. Only
applications that require multiple factories with different configurations have
an obvious reason to create and close multiple EntityManagerFactory
instances. Once a factory is closed, all methods except
isOpen throw an
IllegalStateException.
Table of Contents
![]() |
The diagram above presents an overview of the EntityManager
interface. For a complete treatment of the
EntityManager API, see the
Javadoc documentation. Methods whose parameter signatures consist of
an ellipsis (...) are overloaded to take multiple parameter types.
OpenJPA extends the standard EntityManager interface with
the
org.apache.openjpa.persistence.OpenJPAEntityManager
interface to provide additional functionality.
The EntityManager is the primary interface used by
application developers to interact with the JPA runtime. The methods
of the EntityManager can be divided into the following
functional categories:
Transaction association.
Entity lifecycle management.
Entity identity management.
Cache management.
Query factory.
Closing.
public EntityTransaction getTransaction ();
Every EntityManager has a one-to-one relation with an
EntityTransaction
instance. In fact, many vendors use a single class to implement both the
EntityManager and EntityTransaction
interfaces. If your application requires multiple concurrent
transactions, you will use multiple EntityManagers.
You can retrieve the EntityTransaction associated with an
EntityManager through the getTransaction
method. Note that most most JPA implementations can
integrate with an application server's managed transactions. If you take
advantage of this feature, you will control transactions by declarative
demarcation or through the Java Transaction API (JTA) rather than through the
EntityTransaction.
EntityManagers perform several actions that affect the
lifecycle state of entity instances.
public void persist(Object entity);
Transitions new instances to managed. On the next flush or commit, the newly persisted instances will be inserted into the datastore.
For a given entity A, the persist
method behaves as follows:
If A is a new entity, it becomes managed.
If A is an existing managed entity, it is ignored. However,
the persist operation cascades as defined below.
If A is a removed entity, it becomes managed.
If A is a detached entity, an
IllegalArgumentException is thrown.
The persist operation recurses on all relation fields of A
whose cascades include
CascadeType.PERSIST.
This action can only be used in the context of an active transaction.
public void remove(Object entity);
Transitions managed instances to removed. The instances will be deleted from the datastore on the next flush or commit. Accessing a removed entity has undefined results.
For a given entity A, the remove
method behaves as follows:
If A is a new entity, it is ignored. However, the remove
operation cascades as defined below.
If A is an existing managed entity, it becomes removed.
If A is a removed entity, it is ignored.
If A is a detached entity, an
IllegalArgumentException is thrown.
The remove operation recurses on all relation fields of A
whose cascades include
CascadeType.REMOVE.
This action can only be used in the context of an active transaction.
public void refresh(Object entity);
Use the refresh action to make sure the persistent
state of an instance is synchronized with the values in the datastore.
refresh is intended for long-running optimistic
transactions in which there is a danger of seeing stale data.
For a given entity A, the refresh
method behaves as follows:
If A is a new entity, it is ignored. However, the remove
operation cascades as defined below.
If A is an existing managed entity, its state is refreshed
from the datastore.
If A is a removed entity, it is ignored.
If A is a detached entity, an
IllegalArgumentException is thrown.
The refresh operation recurses on all relation fields of A
whose cascades include
CascadeType.REFRESH.
public Object merge(Object entity);
A common use case for an application running in a servlet or application server is to "detach" objects from all server resources, modify them, and then "attach" them again. For example, a servlet might store persistent data in a user session between a modification based on a series of web forms. Between each form request, the web container might decide to serialize the session, requiring that the stored persistent state be disassociated from any other resources. Similarly, a client/server application might transfer persistent objects to a client via serialization, allow the client to modify their state, and then have the client return the modified data in order to be saved. This is sometimes referred to as the data transfer object or value object pattern, and it allows fine-grained manipulation of data objects without incurring the overhead of multiple remote method invocations.
JPA provides support for this pattern by automatically detaching
entities when they are serialized or when a persistence context ends (see
Section 3, “
Persistence Context
” for an exploration of
persistence contexts). The JPA merge API
re-attaches detached entities. This allows you to detach a persistent instance,
modify the detached instance offline, and merge the instance back into an
EntityManager (either the same one that detached the
instance, or a new one). The changes will then be applied to the existing
instance from the datastore.
A detached entity maintains its persistent identity, but cannot load additional state from the datastore. Accessing any persistent field or property that was not loaded at the time of detachment has undefined results. Also, be sure not to alter the version or identity fields of detached instances if you plan on merging them later.
The merge method returns a managed copy of the given
detached entity. Changes made to the persistent state of the detached entity are
applied to this managed instance. Because merging involves changing persistent
state, you can only merge within a transaction.
If you attempt to merge an instance whose representation has changed in the datastore since detachment, the merge operation will throw an exception, or the transaction in which you perform the merge will fail on commit, just as if a normal optimistic conflict were detected.
OpenJPA offers enhancements to JPA detachment functionality, including additional options to control which fields are detached. See Section 1, “ Detach and Attach ” in the Reference Guide for details.
For a given entity A, the merge
method behaves as follows:
If A is a detached entity, its state is copied into existing
managed instance A' of the same entity identity, or a new
managed copy of A is created.
If A is a new entity, a new managed entity A'
is created and the state of A is copied into
A'.
If A is an existing managed entity, it is ignored. However,
the merge operation still cascades as defined below.
If A is a removed entity, an
IllegalArgumentException is thrown.
The merge operation recurses on all relation fields of A
whose cascades include
CascadeType.MERGE.
public void lock (Object entity, LockModeType mode);
This method locks the given entity using the named mode. The
javax.persistence.LockModeType enum defines two
modes:
READ: Other transactions may concurrently read the object,
but cannot concurrently update it.
WRITE: Other transactions cannot concurrently read or write
the object. When a transaction is committed that holds WRITE locks on any
entites, those entites will have their version incremented even if the entities
themselves did not change in the transaction.
OpenJPA has additional APIs for controlling object locking. See Section 3, “ Object Locking ” in the Reference Guide for details.
The following diagram illustrates the lifecycle of an entity with respect to the APIs presented in this section.
![]() |
The examples below demonstrate how to use the lifecycle methods presented in the
previous section. The examples are appropriate for out-of-container use. Within
a container, EntityManagers are usually injected, and
transactions are usually managed. You would therefore omit the
createEntityManager and close calls, as
well as all transaction demarcation code.
Example 8.1. Persisting Objects
// create some objects
Magazine mag = new Magazine("1B78-YU9L", "JavaWorld");
Company pub = new Company("Weston House");
pub.setRevenue(1750000D);
mag.setPublisher(pub);
pub.addMagazine(mag);
Article art = new Article("JPA Rules!", "Transparent Object Persistence");
art.addAuthor(new Author("Fred", "Hoyle"));
mag.addArticle(art);
// persist
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(mag);
em.persist(pub);
em.persist(art);
em.getTransaction().commit();
// or we could continue using the EntityManager...
em.close();
Example 8.2. Updating Objects
Magazine.MagazineId mi = new Magazine.MagazineId(); mi.isbn = "1B78-YU9L"; mi.title = "JavaWorld"; // updates should always be made within transactions; note that // there is no code explicitly linking the magazine or company // with the transaction; JPA automatically tracks all changes EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); Magazine mag = em.find(Magazine.class, mi); mag.setPrice(5.99); Company pub = mag.getPublisher(); pub.setRevenue(1750000D); em.getTransaction().commit(); // or we could continue using the EntityManager... em.close();
Example 8.3. Removing Objects
// assume we have an object id for the company whose subscriptions
// we want to delete
Object oid = ...;
// deletes should always be made within transactions
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Company pub = (Company) em.find(Company.class, oid);
for (Subscription sub : pub.getSubscriptions())
em.remove(sub);
pub.getSubscriptions().clear();
em.getTransaction().commit();
// or we could continue using the EntityManager...
em.close();
Example 8.4. Detaching and Merging
This example demonstrates a common client/server scenario. The client requests objects and makes changes to them, while the server handles the object lookups and transactions.
// CLIENT:
// requests an object with a given oid
Record detached = (Record) getFromServer(oid);
...
// SERVER:
// send object to client; object detaches on EM close
Object oid = processClientRequest();
EntityManager em = emf.createEntityManager();
Record record = em.find(Record.class, oid);
em.close();
sendToClient(record);
...
// CLIENT:
// makes some modifications and sends back to server
detached.setSomeField("bar");
sendToServer(detached);
...
// SERVER:
// merges the instance and commit the changes
Record modified = (Record) processClientRequest();
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Record merged = (Record) em.merge(modified);
merged.setLastModified(System.currentTimeMillis());
merged.setModifier(getClientIdentityCode());
em.getTransaction().commit();
em.close();
Each EntityManager is responsible for managing the
persistent identities of the managed objects in the persistence context. The
following methods allow you to interact with the management of persistent
identities. The behavior of these methods is deeply affected by the persistence
context type of the EntityManager; see
Section 3, “
Persistence Context
” for an explanation of
persistence contexts.
public <T> T find(Class<T> cls, Object oid);
This method returns the persistent instance of the given type with the given persistent identity. If the instance is already present in the current persistence context, the cached version will be returned. Otherwise, a new instance will be constructed and loaded with state from the datastore. If no entity with the given type and identity exists in the datastore, this method returns null.
public <T> T getReference(Class<T> cls, Object oid);
This method is similar to find, but does not
necessarily go to the database when the entity is not found in cache. The
implementation may construct a hollow entity and return it
to you instead. Hollow entities do not have any state loaded. The state only
gets loaded when you attempt to access a persistent field. At that time, the
implementation may throw an EntityNotFoundException if it
discovers that the entity does not exist in the datastore. The implementation
may also throw an EntityNotFoundException from the
getReference method itself. Unlike
find, getReference does not return null.
public boolean contains(Object entity);
Returns true if the given entity is part of the current persistence context, and false otherwise. Removed entities are not considered part of the current persistence context.
public void flush();
The flush method writes any changes that have been made
in the current transaction to the datastore. If the EntityManager
does not already have a connection to the datastore, it obtains one
for the flush and retains it for the duration of the transaction. Any exceptions
during flush cause the transaction to be marked for rollback. See
Chapter 9,
Transaction
.
Flushing requires an active transaction. If there isn't a transaction in
progress, the flush method throws a
TransactionRequiredException.
public FlushModeType getFlushMode(); public void setFlushMode(FlushModeType flushMode);
The EntityManager's FlushMode property
controls whether to flush transactional changes before executing queries. This
allows the query results to take into account changes you have made during the
current transaction. Available
javax.persistence.FlushModeType constants are:
COMMIT: Only flush when committing, or when told to do so
through the flush method. Query results may not take
into account changes made in the current transaction.
AUTO: The implementation is permitted to flush before
queries to ensure that the results reflect the most recent object state.
You can also set the flush mode on individual
Query
instances.
OpenJPA only flushes before a query if the query might be affected by data changed in the current transaction. Additionally, OpenJPA allows fine-grained control over flushing behavior. See the Reference Guide's Section 8, “ Configuring the Use of JDBC Connections ”.
public void clear();
Clearing the EntityManager effectively ends the
persistence context. All entities managed by the EntityManager
become detached.
public Query createQuery(String query);
Query objects are used to find entities matching certain
criteria. The createQuery method creates a query using
the given Java Persistence Query Language (JPQL) string. See
Chapter 10,
JPA Query
for details.
public Query createNamedQuery(String name);
This method retrieves a query defined in metadata by name. The returned
Query instance is initialized with the information
declared in metadata. For more information on named queries, read
Section 1.9, “
Named Queries
”.
public Query createNativeQuery(String sql); public Query createNativeQuery(String sql, Class resultCls); public Query createNativeQuery(String sql, String resultMapping);
Native queries are queries in the datastore's native language. For relational databases, this the Structured Query Language (SQL). Chapter 11, SQL Queries elaborates on JPA's native query support.
public boolean isOpen(); public void close();
When an EntityManager is no longer needed, you should
call its close method. Closing an
EntityManager releases any resources it is using. The persistence
context ends, and the entities managed by the EntityManager
become detached. Any Query instances the
EntityManager created become invalid. Calling any method
other than isOpen on a closed EntityManager
results in an IllegalStateException. You
cannot close a EntityManager that is in the middle of a
transaction.
If you are in a managed environment using injected entity managers, you should not close them.
Table of Contents
Transactions are critical to maintaining data integrity. They are used to group operations into units of work that act in an all-or-nothing fashion. Transactions have the following qualities:
Atomicity. Atomicity refers to the all-or-nothing property of transactions. Either every data update in the transaction completes successfully, or they all fail, leaving the datastore in its original state. A transaction cannot be only partially successful.
Consistency. Each transaction takes the datastore from one consistent state to another consistent state.
Isolation. Transactions are isolated from each other. When you are reading persistent data in one transaction, you cannot "see" the changes that are being made to that data in other transactions. Similarly, the updates you make in one transaction cannot conflict with updates made in concurrent transactions. The form of conflict resolution employed depends on whether you are using pessimistic or optimistic transactions. Both types are described later in this chapter.
Durability. The effects of successful transactions are durable; the updates made to persistent data last for the lifetime of the datastore.
Together, these qualities are called the ACID properties of transactions. To understand why these properties are so important to maintaining data integrity, consider the following example:
Suppose you create an application to manage bank accounts. The application includes a method to transfer funds from one user to another, and it looks something like this:
public void transferFunds(User from, User to, double amnt) {
from.decrementAccount(amnt);
to.incrementAccount(amnt);
}
Now suppose that user Alice wants to transfer 100 dollars to user Bob. No
problem; you simply invoke your transferFunds method,
supplying Alice in the from parameter, Bob in the
to parameter, and 100.00 as the amnt
. The first line of the method is executed, and 100 dollars is
subtracted from Alice's account. But then, something goes wrong. An unexpected
exception occurs, or the hardware fails, and your method never completes.
You are left with a situation in which the 100 dollars has simply disappeared. Thanks to the first line of your method, it is no longer in Alice's account, and yet it was never transferred to Bob's account either. The datastore is in an inconsistent state.
The importance of transactions should now be clear. If the two lines of the
transferFunds method had been placed together in a
transaction, it would be impossible for only the first line to succeed. Either
the funds would be transferred properly or they would not be transferred at all,
and an exception would be thrown. Money could never vanish into thin air, and
the data store could never get into an inconsistent state.
There are two major types of transactions: pessimistic transactions and optimistic transactions. Each type has both advantages and disadvantages.
Pessimistic transactions generally lock the datastore records they act on, preventing other concurrent transactions from using the same data. This avoids conflicts between transactions, but consumes database resources. Additionally, locking records can result in deadlock, a situation in which two transactions are both waiting for the other to release its locks before completing. The results of a deadlock are datastore-dependent; usually one transaction is forcefully rolled back after some specified timeout interval, and an exception is thrown.
This document will often use the term datastore transaction in place of pessimistic transaction. This is to acknowledge that some datastores do not support pessimistic semantics, and that the exact meaning of a non-optimistic JPA transaction is dependent on the datastore. Most of the time, a datastore transaction is equivalent to a pessimistic transaction.
Optimistic transactions consume less resources than pessimistic/datastore transactions, but only at the expense of reliability. Because optimistic transactions do not lock datastore records, two transactions might change the same persistent information at the same time, and the conflict will not be detected until the second transaction attempts to flush or commit. At this time, the second transaction will realize that another transaction has concurrently modified the same records (usually through a timestamp or versioning system), and will throw an appropriate exception. Note that optimistic transactions still maintain data integrity; they are simply more likely to fail in heavily concurrent situations.
Despite their drawbacks, optimistic transactions are the best choice for most applications. They offer better performance, better scalability, and lower risk of hanging due to deadlock.
OpenJPA uses optimistic semantics by default, but supports both optimistic and datastore transactions. OpenJPA also offers advanced locking and versioning APIs for fine-grained control over database resource allocation and object versioning. See Section 3, “ Object Locking ” of the Reference Guide for details on locking. Section 2.5, “ Version ” of this document covers standard object versioning, while Section 7, “ Additional JPA Mappings ” of the Reference Guide discusses additional versioning strategies available in OpenJPA.
![]() |
JPA integrates with your container's managed transactions,
allowing you to use the container's declarative transaction demarcation and its
Java Transaction API (JTA) implementation for transaction management. Outside of
a container, though, you must demarcate transactions manually through JPA. The
EntityTransaction interface controls unmanaged
transactions in JPA.
public void begin(); public void commit(); public void rollback();
The begin, commit, and
rollback methods demarcate transaction boundaries. The
methods should be self-explanatory: begin starts a
transaction, commit attempts to commit the
transaction's changes to the datastore, and rollback
aborts the transaction, in which case the datastore is "rolled back" to its
previous state. JPA implementations will automatically roll back transactions if
any exception is thrown during the commit process.
Unless you are using an extended persistence context, committing or rolling back
also ends the persistence context. All managed entites will be detached from the
EntityManager.
public boolean isActive();
Finally, the isActive method returns true
if the transaction is in progress (begin
has been called more recently than commit or
rollback), and false otherwise.
Example 9.1. Grouping Operations with Transactions
public void transferFunds(EntityManager em, User from, User to, double amnt) {
// note: it would be better practice to move the transaction demarcation
// code out of this method, but for the purposes of example...
Transaction trans = em.getTransaction();
trans.begin();
try
{
from.decrementAccount(amnt);
to.incrementAccount(amnt);
trans.commit();
}
catch (RuntimeException re)
{
if (trans.isActive())
trans.rollback(); // or could attempt to fix error and retry
throw re;
}
}
Table of Contents
![]() |
The javax.persistence.Query interface is the mechanism
for issuing queries in JPA. The primary query language used is the Java
Persistence Query Language, or JPQL. JPQL is syntactically
very similar to SQL, but is object-oriented rather than table-oriented.
The API for executing JPQL queries will be discussed in Section 1, “ JPQL API ”, and a full language reference will be covered in Section 2, “ JPQL Language Reference ”.
SELECT x FROM Magazine x
The preceding is a simple JPQL query for all Magazine
entities.
public Query createQuery(String jpql);
The
EntityManager.createQuery method creates a
Query instance from a given JPQL string.
public List getResultList();
Invoking
Query.getResultList executes the query and
returns a List containing the matching objects. The
following example executes our Magazine query above:
EntityManager em = ...
Query q = em.createQuery("SELECT x FROM Magazine x");
List<Magazine> results = (List<Magazine>) q.getResultList();
A JPQL query has an internal namespace declared in the from
clause of the query. Arbitrary identifiers are assigned to entities so that they
can be referenced elsewhere in the query. In the query example above, the
identifier x is assigned to the entity Magazine
.
The as keyword can optionally be used when declaring
identifiers in the from clause. SELECT x FROM
Magazine x and SELECT x FROM Magazine AS x are
synonymous.
Following the select clause of the query is the object or
objects that the query returns. In the case of the query above, the query's
result list will contain instances of the Magazine class.
When selecting entities, you can optional use the keyword object
. The clauses select x and SELECT
OBJECT(x) are synonymous.
The optional where clause places criteria on matching
results. For example:
SELECT x FROM Magazine x WHERE x.title = 'JDJ'
Keywords in JPQL expressions are case-insensitive, but entity, identifier, and member names are not. For example, the expression above could also be expressed as:
SELECT x FROM Magazine x WHERE x.title = 'JDJ'
But it could not be expressed as:
SELECT x FROM Magazine x WHERE x.TITLE = 'JDJ'
As with the select clause, alias names in the where
clause are resolved to the entity declared in the from
clause. The query above could be described in English as "for all
Magazine instances x, return a list
of every x such that x's title
field is equal to 'JDJ'".
JPQL uses SQL-like syntax for query criteria. The and and
or logical operators chain multiple criteria together:
SELECT x FROM Magazine x WHERE x.title = 'JDJ' OR x.title = 'JavaPro'
The = operator tests for equality. <>
tests for inequality. JPQL also supports the following arithmetic
operators for numeric comparisons: >, >=, <, <=.
For example:
SELECT x FROM Magazine x WHERE x.price > 3.00 AND x.price <= 5.00
This query returns all magazines whose price is greater than 3.00 and less than or equal to 5.00.
SELECT x FROM Magazine x WHERE x.price <> 3.00
This query returns all Magazines whose price is not equals to 3.00.
You can group expressions together using parentheses in order to specify how they are evaluated. This is similar to how parentheses are used in Java. For example:
SELECT x FROM Magazine x WHERE (x.price > 3.00 AND x.price <= 5.00) OR x.price = 7.00
This expression would match magazines whose price is 4.00, 5.00, or 7.00, but not 6.00. Alternately:
SELECT x FROM Magazine x WHERE x.price > 3.00 AND (x.price <= 5.00 OR x.price = 7.00)
This expression will magazines whose price is 5.00 or 7.00, but not 4.00 or 6.00.
JPQL also includes the following conditionals:
[NOT] BETWEEN: Shorthand for expressing that a value falls
between two other values. The following two statements are synonymous:
SELECT x FROM Magazine x WHERE x.price >= 3.00 AND x.price <= 5.00
SELECT x FROM Magazine x WHERE x.price BETWEEN 3.00 AND 5.00
[NOT] LIKE: Performs a string comparison with wildcard
support. The special character '_' in the parameter means to match any single
character, and the special character '%' means to match any sequence of
characters. The following statement matches title fields "JDJ" and "JavaPro",
but not "IT Insider":
SELECT x FROM Magazine x WHERE x.title LIKE 'J%'
The following statement matches the title field "JDJ" but not "JavaPro":
SELECT x FROM Magazine x WHERE x.title LIKE 'J__'
[NOT] IN: Specifies that the member must be equal to one
element of the provided list. The following two statements are synonymous:
SELECT x FROM Magazine x WHERE x.title IN ('JDJ', 'JavaPro', 'IT Insider')
SELECT x FROM Magazine x WHERE x.title = 'JDJ' OR x.title = 'JavaPro' OR x.title = 'IT Insider'
IS [NOT] EMPTY: Specifies that the collection field holds no
elements. For example:
SELECT x FROM Magazine x WHERE x.articles is empty
This statement will return all magazines whose articles
member contains no elements.
IS [NOT] NULL: Specifies that the field is equal to null.
For example:
SELECT x FROM Magazine x WHERE x.publisher is null
This statement will return all Magazine instances whose "publisher" field is set
to null.
NOT: Negates the contained expression. For example, the
following two statements are synonymous:
SELECT x FROM Magazine x WHERE NOT(x.price = 10.0)
SELECT x FROM Magazine x WHERE x.price <> 10.0
Relations between objects can be traversed using Java-like syntax. For example, if the Magazine class has a field named "publisher" or type Company, that relation can be queried as follows:
SELECT x FROM Magazine x WHERE x.publisher.name = 'Random House'
This query returns all Magazine instances whose
publisher field is set to a Company instance
whose name is "Random House".
Single-valued relation traversal implies that the relation is not null. In SQL terms, this is known as an inner join. If you want to also include relations that are null, you can specify:
SELECT x FROM Magazine x WHERE x.publisher.name = 'Random House' or x.publisher is null
You can also traverse collection fields in queries, but you must declare each
traversal in the from clause. Consider:
SELECT x FROM Magazine x, IN(x.articles) y WHERE y.authorName = 'John Doe'
This query says that for each Magazine x
, traverse the articles relation and check each
Articley, and pass the filter if
y's authorName field is equal to "John
Doe". In short, this query will return all magazines that have any articles
written by John Doe.
The IN() syntax can also be expressed with the keywords
inner join. The statements SELECT x FROM Magazine
x, IN(x.articles) y WHERE y.authorName = 'John Doe' and
SELECT x FROM Magazine x inner join x.articles y WHERE y.authorName = 'John Doe'
are synonymous.
JPQL queries may specify one or more join fetch declarations,
which allow the query to specify which fields in the returned instances will be
pre-fetched.
SELECT x FROM Magazine x join fetch x.articles WHERE x.title = 'JDJ'
The query above returns Magazine instances and guarantees
that the articles field will already be fetched in the
returned instances.
Multiple fields may be specified in separate join fetch
declarations:
SELECT x FROM Magazine x join fetch x.articles join fetch x.authors WHERE x.title = 'JDJ'
Specifying the join fetch declaration is
functionally equivalent to adding the fields to the Query's
FetchConfiguration. See Section 6, “
Fetch Groups
”.
As well as supporting direct field and relation comparisons, JPQL supports a pre-defined set of functions that you can apply.
CONCAT(string1, string2): Concatenates two string fields or
literals. For example:
SELECT x FROM Magazine x WHERE CONCAT(x.title, 's') = 'JDJs'
SUBSTRING(string, startIndex, length): Returns the part of
the string argument starting at startIndex
(1-based) and ending at length characters past
startIndex.
SELECT x FROM Magazine x WHERE SUBSTRING(x.title, 1, 1) = 'J'
TRIM([LEADING | TRAILING | BOTH] [character FROM] string:
Trims the specified character from either the beginning ( LEADING
) end ( TRAILING) or both ( BOTH
) of the string argument. If no trim character is specified, the
space character will be trimmed.
SELECT x FROM Magazine x WHERE TRIM(BOTH 'J' FROM x.title) = 'D'
LOWER(string): Returns the lower-case of the specified
string argument.
SELECT x FROM Magazine x WHERE LOWER(x.title) = 'jdj'
UPPER(string): Returns the upper-case of the specified
string argument.
SELECT x FROM Magazine x WHERE UPPER(x.title) = 'JAVAPRO'
LENGTH(string): Returns the number of characters in the
specified string argument.
SELECT x FROM Magazine x WHERE LENGTH(x.title) = 3
LOCATE(searchString, candidateString [, startIndex]):
Returns the first index of searchString in
candidateString. Positions are 1-based. If the string is not found,
returns 0.
SELECT x FROM Magazine x WHERE LOCATE('D', x.title) = 2
ABS(number): Returns the absolute value of the argument.
SELECT x FROM Magazine x WHERE ABS(x.price) >= 5.00
SQRT(number): Returns the square root of the argument.
SELECT x FROM Magazine x WHERE SQRT(x.price) >= 1.00
MOD(number, divisor): Returns the modulo of number
and divisor.
SELECT x FROM Magazine x WHERE MOD(x.price, 10) = 0
All JPQL queries are polymorphic, which means the from clause
of a query includes not only instances of the specific entity class to which it
refers, but all subclasses of that class as well. The instances returned by a
query include instances of the subclasses that satisfy the query conditions. For
example, the following query may return instances of Magazine
, as well as Tabloid and Digest
instances, where Tabloid and
Digest are Magazine subclasses.
SELECT x FROM Magazine x WHERE x.price < 5
JPQL provides support for parameterized queries. Either named parameters or positional parameters may be specified in the query string. Parameters allow you to re-use query templates where only the input parameters vary. A single query can declare either named parameters or positional parameters, but is not allowed to declare both named and positional parameters.
public Query setParameter (int pos, Object value);
Specify positional parameters in your JPQL string using an integer prefixed by a
question mark. You can then populate the Query object
with positional parameter values via calls to the setParameter
method above. The method returns the Query
instance for optional method chaining.
EntityManager em = ...
Query q = em.createQuery("SELECT x FROM Magazine x WHERE x.title = ?1 and x.price > ?2");
q.setParameter(1, "JDJ").setParameter(2, 5.0);
List<Magazine> results = (List<Magazine>) q.getResultList();
This code will substitute JDJ for the ?1
parameter and 5.0 for the ?2 parameter,
then execute the query with those values.
public Query setParameter(String name, Object value);
Named parameter are denoted by prefixing an arbitrary name with a colon in your
JPQL string. You can then populate the Query object with
parameter values using the method above. Like the positional parameter method,
this method returns the Query instance for optional
method chaining.
EntityManager em = ...
Query q = em.createQuery("SELECT x FROM Magazine x WHERE x.title = :titleParam and x.price > :priceParam");
q.setParameter("titleParam", "JDJ").setParameter("priceParam", 5.0);
List<Magazine> results = (List<Magazine>) q.getResultList();
This code substitutes JDJ for the :titleParam
parameter and 5.0 for the :priceParam
parameter, then executes the query with those values.
JPQL queries may optionally contain an order by clause which
specifies one or more fields to order by when returning query results. You may
follow the order by field clause with the asc
or desc keywords, which indicate that ordering
should be ascending or descending, respectively. If the direction is omitted,
ordering is ascending by default.
SELECT x FROM Magazine x order by x.title asc, x.price desc
The query above returns Magazine instances sorted by
their title in ascending order. In cases where the titles of two or more
magazines are the same, those instances will be sorted by price in descending
order.
JPQL queries can select aggregate data as well as objects. JPQL includes the
min, max, avg, and
count aggregates. These functions can be used for reporting
and summary queries.
The following query will return the average of all the prices of all the magazines:
EntityManager em = ...
Query q = em.createQuery("SELECT AVG(x.price) FROM Magazine x");
Number result = (Number) q.getSingleResult();
The following query will return the highest price of all the magazines titled "JDJ":
EntityManager em = ...
Query q = em.createQuery("SELECT MAX(x.price) FROM Magazine x WHERE x.title = 'JDJ'");
Number result = (Number) q.getSingleResult();
Query templates can be statically declared using the NamedQuery
and NamedQueries annotations. For example:
@Entity
@NamedQueries({
@NamedQuery(name="magsOverPrice",
query="SELECT x FROM Magazine x WHERE x.price > ?1"),
@NamedQuery(name="magsByTitle",
query="SELECT x FROM Magazine x WHERE x.title = :titleParam")
})
public class Magazine {
...
}
These declarations will define two named queries called magsOverPrice
and magsByTitle.
public Query createNamedQuery(String name);
You retrieve named queries with the above EntityManager
method. For example:
EntityManager em = ...
Query q = em.createNamedQuery("magsOverPrice");
q.setParameter(1, 5.0f);
List<Magazine> results = (List<Magazine>) q.getResultList();
EntityManager em = ...
Query q = em.createNamedQuery("magsByTitle");
q.setParameter("titleParam", "JDJ");
List<Magazine> results = (List<Magazine>) q.getResultList();
Queries are useful not only for finding objects, but for efficiently deleting them as well. For example, you might delete all records created before a certain date. Rather than bring these objects into memory and delete them individually, JPA allows you to perform a single bulk delete based on JPQL criteria.
Delete by query uses the same JPQL syntax as normal queries, with one exception:
begin your query string with the delete keyword instead of
the select keyword. To then execute the delete, you call the
following Query method:
public int executeUpdate();
This method returns the number of objects deleted. The following example deletes all subscriptions whose expiration date has passed.
Similar to bulk deletes, it is sometimes necessary to perform updates against a large number of queries in a single operation, without having to bring all the instances down to the client. Rather than bring these objects into memory and modifying them individually, JPA allows you to perform a single bulk update based on JPQL criteria.
Update by query uses the same JPQL syntax as normal queries, except that the
query string begins with the update keyword instead of
select. To execute the update, you call the following
Query method:
public int executeUpdate();
This method returns the number of objects updated. The following example updates all subscriptions whose expiration date has passed to have the "paid" field set to true..
The Java Persistence Query Language (JPQL) is used to define searches against
persistent entities independent of the mechanism used to store those entities.
As such, JPQL is "portable", and not constrained to any particular data store.
The Java Persistence query language is an extension of the Enterprise JavaBeans
query language, EJB QL, adding operations such as bulk
deletes and updates, join operations, aggregates, projections, and subqueries.
Furthermore, JPQL queries can be declared statically in metadata, or can be
dynamically built in code. This chapter provides the full definition of the
language.
Much of this section is paraphrased or taken directly from Chapter 4 of the JSR 220 specification.
A JPQL statement may be either a SELECT statement, an
UPDATE statement, or a DELETE statement.
This chapter refers to all such statements as "queries". Where it is important
to distinguish among statement types, the specific statement type is referenced.
In BNF syntax, a query language statement is defined as:
QL_statement ::= select_statement | update_statement | delete_statement
The complete BNF for JPQL is defined in Section 2.12, “ JPQL BNF ”. Any JPQL statement may be constructed dynamically or may be statically defined in a metadata annotation or XML descriptor element. All statement types may have parameters, as discussed in Section 2.5.4, “ JPQL Input Parameters ”.
A select statement is a string which consists of the following clauses:
a SELECT clause, which determines the type of the objects
or values to be selected.
a FROM clause, which provides declarations that designate the
domain to which the expressions specified in the other clauses of the query
apply.
an optional WHERE clause, which may be used to restrict the
results that are returned by the query.
an optional GROUP BY clause, which allows query results to be
aggregated in terms of groups.
an optional HAVING clause, which allows filtering over
aggregated groups.
an optional ORDER BY clause, which may be used to order the
results that are returned by the query.
In BNF syntax, a select statement is defined as:
select_statement ::= select_clause from_clause [where_clause] [groupby_clause] [having_clause] [orderby_clause]
A select statement must always have a SELECT and a
FROM clause. The square brackets [] indicate that the other
clauses are optional.
Update and delete statements provide bulk operations over sets of entities. In BNF syntax, these operations are defined as:
update_statement ::= update_clause [where_clause]
delete_statement ::= delete_clause [where_clause]
The update and delete clauses determine the type of the entities to be updated
or deleted. The WHERE clause may be used to restrict the
scope of the update or delete operation. Update and delete statements are
described further in Section 2.9, “
JPQL Bulk Update and Delete
”.
The Java Persistence query language is a typed language, and every expression has a type. The type of an expression is derived from the structure of the expression, the abstract schema types of the identification variable declarations, the types to which the persistent fields and relationships evaluate, and the types of literals. The abstract schema type of an entity is derived from the entity class and the metadata information provided by Java language annotations or in the XML descriptor.
Informally, the abstract schema type of an entity can be characterized as follows:
For every persistent field or get accessor method (for a persistent property) of the entity class, there is a field ("state-field") whose abstract schema type corresponds to that of the field or the result type of the accessor method.
For every persistent relationship field or get accessor method (for a persistent relationship property) of the entity class, there is a field ("association-field") whose type is the abstract schema type of the related entity (or, if the relationship is a one-to-many or many-to-many, a collection of such). Abstract schema types are specific to the query language data model. The persistence provider is not required to implement or otherwise materialize an abstract schema type. The domain of a query consists of the abstract schema types of all entities that are defined in the same persistence unit. The domain of a query may be restricted by the navigability of the relationships of the entity on which it is based. The association-fields of an entity's abstract schema type determine navigability. Using the association-fields and their values, a query can select related entities and use their abstract schema types in the query.
Entities are designated in query strings by their entity names. The entity name is defined by the name element of the Entity annotation (or the entity-name XML descriptor element), and defaults to the unqualified name of the entity class. Entity names are scoped within the persistence unit and must be unique within the persistence unit.
This example assumes that the application developer provides several entity
classes, representing magazines, publishers, authors, and articles. The abstract
schema types for these entities are Magazine,
Publisher, Author, and Article.
Several Entities with Abstract Persistence Schemas Defined in the Same
Persistence Unit. The entity Publisher has a one-to-many
relationships with Magazine. There is also a one-to-many
relationship between Magazine and Article
. The entity Article is related to Author
in a one-to-one relationship.
Queries to select magazines can be defined by navigating over the association-fields and state-fields defined by Magazine and Author. A query to find all magazines that have unpublished articles is as follows:
SELECT DISTINCT mag FROM Magazine AS mag JOIN mag.articles AS art WHERE art.published = FALSE
This query navigates over the association-field authors of the
abstract schema type Magazine to find articles, and uses the
state-field published of Article to select
those magazines that have at least one article that is published. Although
predefined reserved identifiers, such as DISTINCT,
FROM, AS, JOIN,
WHERE, and FALSE appear in upper case in this
example, predefined reserved identifiers are case insensitive. The
SELECT clause of this example designates the return type of this
query to be of type Magazine. Because the same persistence unit defines the
abstract persistence schemas of the related entities, the developer can also
specify a query over articles that utilizes the abstract
schema type for products, and hence the state-fields and association-fields of
both the abstract schema types Magazine and Author. For example, if the
abstract schema type Author has a state-field named firstName, a query over
articles can be specified using this state-field. Such a query might be to
find all magazines that have articles authored by someone with the first name
"John".
SELECT DISTINCT mag FROM Magazine mag JOIN mag.articles art JOIN art.author auth WHERE auth.firstName = 'John'
Because Magazine is related to Author by means of the relationships between Magazine and Article and between Article and Author, navigation using the association-fields authors and product is used to express the query. This query is specified by using the abstract schema name Magazine, which designates the abstract schema type over which the query ranges. The basis for the navigation is provided by the association-fields authors and product of the abstract schema types Magazine and Article respectively.
The FROM clause of a query defines the domain of the query by
declaring identification variables. An identification variable is an identifier
declared in the FROM clause of a query. The domain of the
query may be constrained by path expressions. Identification variables designate
instances of a particular entity abstract schema type. The FROM
clause can contain multiple identification variable declarations
separated by a comma (,).
from_clause ::= FROM identification_variable_declaration {, {identification_variable_declaration | collection_member_declaration}}*
identification_variable_declaration ::= range_variable_declaration { join | fetch_join }*
range_variable_declaration ::= abstract_schema_name [AS] identification_variable
join ::= join_spec join_association_path_expression [AS] identification_variable
fetch_join ::= join_spec FETCH join_association_path_expression
join_association_path_expression ::= join_collection_valued_path_expression | join_single_valued_association_path_expression
join_spec ::= [ LEFT [OUTER] | INNER ] JOIN
collection_member_declaration ::= IN (collection_valued_path_expression) [AS] identification_variable
An identifier is a character sequence of unlimited length. The character
sequence must begin with a Java identifier start character, and all other
characters must be Java identifier part characters. An identifier start
character is any character for which the method
Character.isJavaIdentifierStart returns true.
This includes the underscore (_) character and the dollar sign ($) character. An
identifier part character is any character for which the method
Character.isJavaIdentifierPart returns true.
The question mark (?) character is reserved for use by the Java Persistence
query language. The following are reserved identifiers:
SELECT
FROM
WHERE
UPDATE
DELETE
JOIN
OUTER
INNER
LEFT
GROUP
BY
HAVING
FETCH
DISTINCT
OBJECT
NULL
TRUE
FALSE
NOT
AND
OR
BETWEEN
LIKE
IN
AS
UNKNOWN
EMPTY
MEMBER
OF
IS
AVG
MAX
MIN
SUM
COUNT
ORDER
BY
ASC
DESC
MOD
UPPER
LOWER
TRIM
POSITION
CHARACTER_LENGTH
CHAR_LENGTH
BIT_LENGTH
CURRENT_TIME
CURRENT_DATE
CURRENT_TIMESTAMP
NEW
EXISTS
ALL
ANY
SOME
Reserved identifiers are case insensitive. Reserved identifiers must not be used as identification variables. It is recommended that other SQL reserved words also not be as identification variables in queries because they may be used as reserved identifiers in future releases of the specification.
An identification variable is a valid identifier declared in the FROM
clause of a query. All identification variables must be declared in
the FROM clause. Identification variables cannot be declared
in other clauses. An identification variable must not be a reserved identifier
or have the same name as any entity in the same persistence unit: Identification
variables are case insensitive. An identification variable evaluates to a value
of the type of the expression used in declaring the variable. For example,
consider the previous query:
SELECT DISTINCT mag FROM Magazine mag JOIN mag.articles art JOIN art.author auth WHERE auth.firstName = 'John'In the
FROM clause declaration
mag.articlesart, the identification variable
art evaluates to any Article value
directly reachable from Magazine. The association-field
articles is a collection of instances of the abstract schema
type Article and the identification variable art
refers to an element of this collection. The type of auth
is the abstract schema type of Author. An
identification variable ranges over the abstract schema type of an entity. An
identification variable designates an instance of an entity abstract schema type
or an element of a collection of entity abstract schema type instances.
Identification variables are existentially quantified in a query. An
identification variable always designates a reference to a single value. It is
declared in one of three ways: in a range variable declaration, in a join
clause, or in a collection member declaration. The identification variable
declarations are evaluated from left to right in the FROM
clause, and an identification variable declaration can use the result of a
preceding identification variable declaration of the query string.
The syntax for declaring an identification variable as a range variable is similar to that of SQL; optionally, it uses the AS keyword.
range_variable_declaration ::= abstract_schema_name [AS] identification_variable
Range variable declarations allow the developer to designate a "root" for
objects which may not be reachable by navigation. In order to select values by
comparing more than one instance of an entity abstract schema type, more than
one identification variable ranging over the abstract schema type is needed in
the FROM clause.
The following query returns magazines whose price is greater than the price of
magazines published by "Adventure" publishers. This example illustrates the use
of two different identification variables in the FROM clause,
both of the abstract schema type Magazine. The SELECT clause
of this query determines that it is the magazines with prices greater than those
of "Adventure" publisher's that are returned.
SELECT DISTINCT mag1 FROM Magazine mag1, Magazine mag2 WHERE mag1.price > mag2.price AND mag2.publisher.name = 'Adventure'
An identification variable followed by the navigation operator (.) and a state-field or association-field is a path expression. The type of the path expression is the type computed as the result of navigation; that is, the type of the state-field or association-field to which the expression navigates. Depending on navigability, a path expression that leads to a association-field may be further composed. Path expressions can be composed from other path expressions if the original path expression evaluates to a single-valued type (not a collection) corresponding to a association-field. Path expression navigability is composed using "inner join" semantics. That is, if the value of a non-terminal association-field in the path expression is null, the path is considered to have no value, and does not participate in the determination of the result. The syntax for single-valued path expressions and collection valued path expressions is as follows:
single_valued_path_expression ::= state_field_path_expression | single_valued_association_path_expression
state_field_path_expression ::= {identification_variable | single_valued_association_path_expression}.state_field
single_valued_association_path_expression ::= identification_variable.{single_valued_association_field.}*single_valued_association_field
collection_valued_path_expression ::= identification_variable.{single_valued_association_field.}*collection_valued_association_field
state_field ::= {embedded_class_state_field.}*simple_state_field
A single_valued_association_field is designated by the name of an association-field in a one-to-one or many-to-one relationship. The type of a single_valued_association_field and thus a single_valued_association_path_expression is the abstract schema type of the related entity. A collection_valued_association_field is designated by the name of an association-field in a one-to-many or a many-to-many relationship. The type of a collection_valued_association_field is a collection of values of the abstract schema type of the related entity. An embedded_class_state _field is designated by the name of an entity state field that corresponds to an embedded class. Navigation to a related entity results in a value of the related entity's abstract schema type.
The evaluation of a path expression terminating in a state-field results in the
abstract schema type corresponding to the Java type designated by the
state-field. It is syntactically illegal to compose a path expression from a
path expression that evaluates to a collection. For example, if mag
designates Magazine, the path expression
mag.articles.author is illegal since navigation to authors results in
a collection. This case should produce an error when the query string is
verified. To handle such a navigation, an identification variable must be
declared in the FROM clause to range over the elements of the
articles collection. Another path expression must be used to
navigate over each such element in the WHERE clause of the
query, as in the following query which returns all authors that have any
articles in any magazines:
SELECT DISTINCT art.author FROM Magazine AS mag, IN(mag.articles) art
An inner join may be implicitly specified by the use of a cartesian product in
the FROM clause and a join condition in the WHERE
clause.
The syntax for explicit join operations is as follows:
join ::= join_spec join_association_path_expression [AS] identification_variable
fetch_join ::= join_spec FETCH join_association_path_expression
join_association_path_expression ::= join_collection_valued_path_expression | join_single_valued_association_path_expression
join_spec ::= [ LEFT [OUTER] | INNER ] JOIN
The following inner and outer join operation types are supported.
The syntax for the inner join operation is
[ INNER ] JOIN join_association_path_expression [AS] identification_variableFor example, the query below joins over the relationship between publishers and magazines. This type of join typically equates to a join over a foreign key relationship in the database.
SELECT pub FROM Publisher pub JOIN pub.magazines mag WHERE pub.revenue > 1000000
The keyword INNER may optionally be used:
SELECT pub FROM Publisher pub INNER JOIN pub.magazines mag WHERE pub.revenue > 1000000
This is equivalent to the following query using the earlier
IN construct. It selects those publishers with revenue of
over 1 million for which at least one magazine exists:
SELECT OBJECT(pub) FROM Publisher pub, IN(pub.magazines) mag WHERE pub.revenue > 1000000
LEFT JOIN and LEFT OUTER JOIN are
synonymous. They enable the retrieval of a set of entities where matching values
in the join condition may be absent. The syntax for a left outer join is:
LEFT [OUTER] JOIN join_association_path_expression [AS] identification_variable
For example:
SELECT pub FROM Publisher pub LEFT JOIN pub.magazines mag WHERE pub.revenue > 1000000The keyword
OUTER may optionally be used:
SELECT pub FROM Publisher pub LEFT OUTER JOIN pub.magazines mags WHERE pub.revenue > 1000000An important use case for
LEFT JOIN is in
enabling the prefetching of related data items as a side effect of a query. This
is accomplished by specifying the LEFT JOIN as a
FETCH JOIN.
A FETCH JOIN enables the fetching of an association as a side
effect of the execution of a query. A FETCH JOIN is specified
over an entity and its related entities. The syntax for a fetch join is
fetch_join ::= [ LEFT [OUTER] | INNER ] JOIN FETCH join_association_path_expression
The association referenced by the right side of the FETCH JOIN
clause must be an association that belongs to an entity that is
returned as a result of the query. It is not permitted to specify an
identification variable for the entities referenced by the right side of the
FETCH JOIN clause, and hence references to the implicitly
fetched entities cannot appear elsewhere in the query. The following query
returns a set of magazines. As a side effect, the associated articles for those
magazines are also retrieved, even though they are not part of the explicit
query result. The persistent fields or properties of the articles that are
eagerly fetched are fully initialized. The initialization of the relationship
properties of the articles that are retrieved is determined
by the metadata for the Article entity class.
SELECT mag FROM Magazine mag LEFT JOIN FETCH mag.articles WHERE mag.id = 1
A fetch join has the same join semantics as the corresponding inner or outer join, except that the related objects specified on the right-hand side of the join operation are not returned in the query result or otherwise referenced in the query. Hence, for example, if magazine id 1 has five articles, the above query returns five references to the magazine 1 entity.
An identification variable declared by a collection_member_declaration ranges
over values of a collection obtained by navigation using a path expression. Such
a path expression represents a navigation involving the association-fields of an
entity abstract schema type. Because a path expression can be based on another
path expression, the navigation can use the association-fields of related
entities. An identification variable of a collection member declaration is
declared using a special operator, the reserved identifier IN
. The argument to the IN operator is a collection-valued path
expression. The path expression evaluates to a collection type specified as a
result of navigation to a collection-valued association-field of an entity
abstract schema type. The syntax for declaring a collection member
identification variable is as follows:
collection_member_declaration ::= IN (collection_valued_path_expression) [AS] identification_variable
For example, the query
SELECT DISTINCT mag FROM Magazine mag
JOIN mag.articles art
JOIN art.author auth
WHERE auth.lastName = 'Grisham' may equivalently be
expressed as follows, using the IN operator: SELECT DISTINCT mag FROM Magazine mag,
IN(mag.articles) art
WHERE art.author.lastName = 'Grisham' In this example,
articles is the name of an association-field whose value is a
collection of instances of the abstract schema type Article.
The identification variable art designates a member of this
collection, a single Article abstract schema type instance.
In this example, mag is an identification variable of the
abstract schema type Magazine.
Java Persistence queries are automatically polymorphic. The FROM
clause of a query designates not only instances of the specific
entity classes to which explicitly refers but of subclasses as well. The
instances returned by a query include instances of the subclasses that satisfy
the query criteria.
The WHERE clause of a query consists of a conditional
expression used to select objects or values that satisfy the expression. The
WHERE clause restricts the result of a select statement or
the scope of an update or delete operation. A WHERE clause is
defined as follows:
where_clause ::= WHERE conditional_expression
The GROUP BY construct enables the aggregation of values
according to the properties of an entity class. The HAVING
construct enables conditions to be specified that further restrict the query
result as restrictions upon the groups. The syntax of the HAVING
clause is as follows:
having_clause ::= HAVING conditional_expression
The GROUP BY and HAVING constructs are
further discussed in Section 2.6, “
JPQL GROUP BY, HAVING
”.
The following sections describe the language constructs that can be used in a
conditional expression of the WHERE clause or
HAVING clause. State-fields that are mapped in serialized form or as
lobs may not be portably used in conditional expressions.
The implementation is not expected to perform such query operations involving such fields in memory rather than in the database.
A string literal is enclosed in single quotes--for example: 'literal'. A string
literal that includes a single quote is represented by two single quotes--for
example: 'literal''s'. String literals in queries, like Java String literals,
use unicode character encoding. The use of Java escape notation is not supported
in query string literals Exact numeric literals support the use of Java integer
literal syntax as well as SQL exact numeric literal syntax. Approximate literals
support the use Java floating point literal syntax as well as SQL approximate
numeric literal syntax. Enum literals support the use of Java enum literal
syntax. The enum class name must be specified. Appropriate suffixes may be used
to indicate the specific type of a numeric literal in accordance with the Java
Language Specification. The boolean literals are TRUE and
FALSE. Although predefined reserved literals appear in upper
case, they are case insensitive.
All identification variables used in the WHERE or
HAVING clause of a SELECT or DELETE
statement must be declared in the FROM clause, as
described in Section 2.3.2, “
JPQL Identification Variables
”. The identification
variables used in the WHERE clause of an UPDATE
statement must be declared in the UPDATE clause.
Identification variables are existentially quantified in the WHERE
and HAVING clause. This means that an
identification variable represents a member of a collection or an instance of an
entity's abstract schema type. An identification variable never designates a
collection in its entirety.
It is illegal to use a collection_valued_path_expression within a
WHERE or HAVING clause as part of a conditional
expression except in an empty_collection_comparison_expression, in a
collection_member_expression, or as an argument to the SIZE
operator.
Either positional or named parameters may be used. Positional and named
parameters may not be mixed in a single query. Input parameters can only be used
in the WHERE clause or HAVING clause of a
query.
Note that if an input parameter value is null, comparison operations or arithmetic operations involving the input parameter will return an unknown value. See Section 2.10, “ JPQL Null Values ”.
The following rules apply to positional parameters.
Input parameters are designated by the question mark (?) prefix followed by an integer. For example: ?1.
Input parameters are numbered starting from 1. Note that the same parameter can be used more than once in the query string and that the ordering of the use of parameters within the query string need not conform to the order of the positional parameters.
A named parameter is an identifier that is prefixed by the ":" symbol. It follows the rules for identifiers defined in Section 2.3.1, “ JPQL FROM Identifiers ”. Named parameters are case sensitive.
Example:
SELECT pub FROM Publisher pub WHERE pub.revenue > :rev
Conditional expressions are composed of other conditional expressions, comparison operations, logical operations, path expressions that evaluate to boolean values, boolean literals, and boolean input parameters. Arithmetic expressions can be used in comparison expressions. Arithmetic expressions are composed of other arithmetic expressions, arithmetic operations, path expressions that evaluate to numeric values, numeric literals, and numeric input parameters. Arithmetic operations use numeric promotion. Standard bracketing () for ordering expression evaluation is supported. Conditional expressions are defined as follows:
conditional_expression ::= conditional_term | conditional_expression OR conditional_term
conditional_term ::= conditional_factor | conditional_term AND conditional_factor
conditional_factor ::= [ NOT ] conditional_primary
conditional_primary ::= simple_cond_expression | (conditional_expression)
simple_cond_expression ::= comparison_expression | between_expression | like_expression | in_expression | null_comparison_expression | empty_collection_comparison_expression | collection_member_expression | exists_expression
Aggregate functions can only be used in conditional expressions in a
HAVING clause. See Section 2.6, “
JPQL GROUP BY, HAVING
”.
The operators are listed below in order of decreasing precedence.
Navigation operator (.)
Arithmetic operators: +, - unary *, / multiplication and division +, - addition and subtraction
Comparison operators: =, >, >=, <, <=, <> (not equal), [
NOT ] BETWEEN, [ NOT ]
LIKE, [ NOT ] IN,
IS [ NOT ] NULL,
IS [ NOT ] EMPTY, [
NOT ] MEMBER [ OF ]
Logical operators: NOTANDOR
The syntax for the use of the comparison operator [ NOT ]
BETWEEN in a conditional expression is as follows:
arithmetic_expression [NOT] BETWEEN arithmetic_expression AND arithmetic_expression | string_expression [NOT] BETWEEN string_expression AND string_expression | datetime_expression [NOT] BETWEEN datetime_expression AND datetime_expression
The BETWEEN expression
x BETWEEN y AND zis semantically equivalent to:
y <= x AND x <= zThe rules for unknown and
NULL values in
comparison operations apply. See Section 2.10, “
JPQL Null Values
”
. Examples are: p.age BETWEEN 15 and 19is equivalent to
p.age >= 15 AND p.age <= 19
p.age NOT BETWEEN 15 and 19is equivalent to
p.age < 15 OR p.age > 19
The syntax for the use of the comparison operator [ NOT ]
IN in a conditional expression is as follows:
in_expression ::= state_field_path_expression [NOT] IN ( in_item {, in_item}* | subquery)
in_item ::= literal | input_parameter
The state_field_path_expression must have a string, numeric, or enum value. The literal and/or input_parameter values must be like the same abstract schema type of the state_field_path_expression in type. (See Section 2.11, “ JPQL Equality and Comparison Semantics ” ).
The results of the subquery must be like the same abstract schema type of the state_field_path_expression in type. Subqueries are discussed in Section 2.5.15, “ JPQL Subqueries ”. Examples are:
o.country IN ('UK', 'US', 'France')
is true for UK and false for Peru, and is equivalent to the
expression: (o.country = 'UK') OR (o.country = 'US') OR (o.country = ' France')In the following expression:
o.country NOT IN ('UK', 'US', 'France')
is false for UK and true for Peru, and is equivalent to the
expression: NOT ((o.country = 'UK') OR (o.country = 'US') OR (o.country = 'France'))There must be at least one element in the comma separated list that defines the set of values for the
IN expression. If the
value of a state_field_path_expression in an IN or
NOT IN expression is NULL or unknown, the value of
the expression is unknown.
The syntax for the use of the comparison operator [ NOT ]
LIKE in a conditional expression is as follows:
string_expression [NOT] LIKE pattern_value [ESCAPE escape_character]
The string_expression must have a string value. The pattern_value is a string literal or a string-valued input parameter in which an underscore (_) stands for any single character, a percent (%) character stands for any sequence of characters (including the empty sequence), and all other characters stand for themselves. The optional escape_character is a single-character string literal or a character-valued input parameter (i.e., char or Character) and is used to escape the special meaning of the underscore and percent characters in pattern_value. Examples are:
address.phone LIKE '12%3'is true for '123' '12993' and false for '1234'
asentence.word LIKE 'l_se'is true for 'lose' and false for 'loose'
aword.underscored LIKE '\_%' ESCAPE '\'is true for '_foo' and false for 'bar'
address.phone NOT LIKE '12%3'is false for '123' and '12993' and true for '1234' If the value of the string_expression or pattern_value is
NULL or unknown, the value of the
LIKE expression is unknown. If the escape_character is specified and
is NULL, the value of the LIKE expression
is unknown.
The syntax for the use of the comparison operator IS NULL in
a conditional expression is as follows:
{single_valued_path_expression | input_parameter } IS [NOT] NULL
A null comparison expression tests whether or not the single-valued path
expression or input parameter is a NULL value.
The syntax for the use of the comparison operator IS EMPTY in
an empty_collection_comparison_expression is as follows:
collection_valued_path_expression IS [NOT] EMPTY
This expression tests whether or not the collection designated by the collection-valued path expression is empty (i.e, has no elements).
For example, the following query will return all magazines that don't have any articles at all:
SELECT mag FROM Magazine mag WHERE mag.articles IS EMPTYIf the value of the collection-valued path expression in an empty collection comparison expression is unknown, the value of the empty comparison expression is unknown.
The use of the comparison collection_member_expression is as follows: syntax for
the operator MEMBER OF in an
collection_member_expression ::= entity_expression [NOT] MEMBER [OF] collection_valued_path_expression
entity_expression ::= single_valued_association_path_expression | simple_entity_expression
simple_entity_expression ::= identification_variable | input_parameter
This expression tests whether the designated value is a member of the collection
specified by the collection-valued path expression. If the collection valued
path expression designates an empty collection, the value of the
MEMBER OF expression is FALSE and the value of the
NOT MEMBER OF expression is TRUE.
Otherwise, if the value of the collection-valued path expression or
single-valued association-field path expression in the collection member
expression is NULL or unknown, the value of the collection
member expression is unknown.
An EXISTS expression is a predicate that is true only if the
result of the subquery consists of one or more values and that is false
otherwise. The syntax of an exists expression is
exists_expression ::= [NOT] EXISTS (subquery)
The use of the reserved word OF is optional in this expression.
Example:
SELECT DISTINCT auth FROM Author auth
WHERE EXISTS
(SELECT spouseAuthor FROM Author spouseAuthor WHERE spouseAuthor = auth.spouse)
The result of this query consists of all authors whose spouse
is also an author.
An ALL conditional expression is a predicate that is true if
the comparison operation is true for all values in the result of the subquery or
the result of the subquery is empty. An ALL conditional
expression is false if the result of the comparison is false for at least one
row, and is unknown if neither true nor false. An ANY
conditional expression is a predicate that is true if the comparison operation
is true for some value in the result of the subquery. An ANY
conditional expression is false if the result of the subquery is empty or if the
comparison operation is false for every value in the result of the subquery, and
is unknown if neither true nor false. The keyword SOME is
synonymous with ANY. The comparison operators used with
ALL or ANY conditional expressions are =,
<, <=, >, >=, <>. The result of the subquery must be like that
of the other argument to the comparison operator in type. See
Section 2.11, “
JPQL Equality and Comparison Semantics
”. The syntax of an ALL
or ANY expression is specified as follows:
all_or_any_expression ::= { ALL | ANY | SOME} (subquery)
The following example select the authors who make the highest salary for their magazine:
SELECT auth FROM Author auth
WHERE auth.salary >= ALL(SELECT a.salary FROM Author a WHERE a.magazine = auth.magazine)
Subqueries may be used in the WHERE or HAVING
clause. The syntax for subqueries is as follows:
subquery ::= simple_select_clause subquery_from_clause [where_clause] [groupby_clause] [having_clause]
Subqueries are restricted to the WHERE and HAVING
clauses in this release. Support for subqueries in the FROM
clause will be considered in a later release of the specification.
simple_select_clause ::= SELECT [DISTINCT] simple_select_expression
subquery_from_clause ::= FROM subselect_identification_variable_declaration {, subselect_identification_variable_declaration}*
subselect_identification_variable_declaration ::= identification_variable_declaration | association_path_expression [AS] identification_variable | collection_member_declaration
simple_select_expression ::= single_valued_path_expression | aggregate_expression | identification_variable
Examples:
SELECT DISTINCT auth FROM Author auth
WHERE EXISTS (SELECT spouseAuth FROM Author spouseAuth WHERE spouseAuth = auth.spouse)
SELECT mag FROM Magazine mag
WHERE (SELECT COUNT(art) FROM mag.articles art) > 10
Note that some contexts in which a subquery can be used require that the
subquery be a scalar subquery (i.e., produce a single result). This is
illustrated in the following example involving a numeric comparison operation.
SELECT goodPublisher FROM Publisher goodPublisher
WHERE goodPublisher.revenue < (SELECT AVG(p.revenue) FROM Publisher p)
The JPQL includes the following built-in functions, which may be used in the
WHERE or HAVING clause of a query. If the
value of any argument to a functional expression is null or unknown, the value
of the functional expression is unknown.
functions_returning_strings ::= CONCAT(string_primar y, string_primary) | SUBSTRING(string_primar y, simple_arithmetic_expression, simple_arithmetic_expression) | TRIM([[trim_specification] [trim_character] FROM] string_primary) | LOWER(string_primar y) | UPPER(string_primar y)
trim_specification ::= LEADING | TRAILING | BOTH
functions_returning_numerics ::= LENGTH(string_primar y) | LOCATE(string_primar y, string_primar y[, simple_arithmetic_expression])
The CONCAT function returns a string that is a concatenation
of its arguments. The second and third arguments of the SUBSTRING
function denote the starting position and length of the substring to
be returned. These arguments are integers. The first position of a string is
denoted by 1. The SUBSTRING function returns a string. The
TRIM function trims the specified character from a string. If
the character to be trimmed is not specified, it is assumed to be space (or
blank). The optional trim_character is a single-character string literal or a
character-valued input parameter (i.e., char or Character). If a trim
specification is not provided, BOTH is assumed. The
TRIM function returns the trimmed string. The LOWER
and UPPER functions convert a string to lower and upper case,
respectively. They return a string. The LOCATE function
returns the position of a given string within a string, starting the search at a
specified position. It returns the first position at which the string was found
as an integer. The first argument is the string to be located; the second
argument is the string to be searched; the optional third argument is an integer
that represents the string position at which the search is started (by default,
the beginning of the string to be searched). The first position in a string is
denoted by 1. If the string is not found, 0 is returned. The LENGTH
function returns the length of the string in characters as an
integer.
functions_returning_numerics ::= ABS(simple_arithmetic_expression) | SQRT(simple_arithmetic_expression) | MOD(simple_arithmetic_expression, simple_arithmetic_expression) | SIZE(collection_valued_path_expression)
The ABS function takes a numeric argument and returns a
number (integer, float, or double) of the same type as the argument to the
function. The SQRT function takes a numeric argument and
returns a double.
Note that not all databases support the use of a trim character other than the
space character; use of this argument may result in queries that are not
portable. Note that not all databases support the use of the third argument to
LOCATE; use of this argument may result in queries that are
not portable.
The MOD function takes two integer arguments and returns an
integer. The SIZE function returns an integer value, the
number of elements of the collection. If the collection is empty, the
SIZE function evaluates to zero. Numeric arguments to these functions
may correspond to the numeric Java object types as well as the primitive numeric
types.
The GROUP BY construct enables the aggregation of values
according to a set of properties. The HAVING construct
enables conditions to be specified that further restrict the query result. Such
conditions are restrictions upon the groups. The syntax of the GROUP
BY and HAVING clauses is as follows:
groupby_clause ::= GROUP BY groupby_item {, groupby_item}*
groupby_item ::= single_valued_path_expression | identification_variable
having_clause ::= HAVING conditional_expression
If a query contains both a WHERE clause and a GROUP
BY clause, the effect is that of first applying the where clause, and
then forming the groups and filtering them according to the HAVING
clause. The HAVING clause causes those groups to
be retained that satisfy the condition of the HAVING clause.
The requirements for the SELECT clause when GROUP
BY is used follow those of SQL: namely, any item that appears in the
SELECT clause (other than as an argument to an aggregate
function) must also appear in the GROUP BY clause. In forming
the groups, null values are treated as the same for grouping purposes. Grouping
by an entity is permitted. In this case, the entity must contain no serialized
state fields or lob-valued state fields. The HAVING clause
must specify search conditions over the grouping items or aggregate functions
that apply to grouping items.
If there is no GROUP BY clause and the HAVING
clause is used, the result is treated as a single group, and the
select list can only consist of aggregate functions. When a query declares a
HAVING clause, it must always also declare a GROUP
BY clause.
The SELECT clause denotes the query result. More than one
value may be returned from the SELECT clause of a query. The
SELECT clause may contain one or more of the following
elements: a single range variable or identification variable that ranges over an
entity abstract schema type, a single-valued path expression, an aggregate
select expression, a constructor expression. The SELECT
clause has the following syntax:
select_clause ::= SELECT [DISTINCT] select_expression {, select_expression}*
select_expression ::= single_valued_path_expression | aggregate_expression | identification_variable | OBJECT(identification_variable) | constructor_expression
constructor_expression ::= NEW constructor_name ( constructor_item {, constructor_item}*)
constructor_item ::= single_valued_path_expression | aggregate_expression
aggregate_expression ::= { AVG | MAX | MIN | SUM } ([DISTINCT] state_field_path_expression) | COUNT ([DISTINCT] identification_variable | state_field_path_expression | single_valued_association_path_expression)
For example:
SELECT pub.id, pub.revenue
FROM Publisher pub JOIN pub.magazines mag WHERE mag.price > 5.00
Note that the SELECT clause must be specified to return only
single-valued expressions. The query below is therefore not valid:
SELECT mag.authors FROM Magazine AS magThe
DISTINCT keyword is used to specify that duplicate values
must be eliminated from the query result. If DISTINCT is not
specified, duplicate values are not eliminated. Standalone identification
variables in the SELECT clause may optionally be qualified by
the OBJECT operator. The SELECT clause
must not use the OBJECT operator to qualify path expressions.
The type of the query result specified by the SELECT clause
of a query is an entity abstract schema type, a state-field type, the result of
an aggregate function, the result of a construction operation, or some sequence
of these. The result type of the SELECT clause is defined by
the the result types of the select_expressions contained in it. When multiple
select_expressions are used in the SELECT clause, the result
of the query is of type Object[], and the elements in this result correspond in
order to the order of their specification in the SELECT
clause and in type to the result types of each of the select_expressions. The
type of the result of a select_expression is as follows:
A single_valued_path_expression that is a state_field_path_expression results in an object of the same type as the corresponding state field of the entity. If the state field of the entity is a primitive type, the corresponding object type is returned.
single_valued_path_expression that is a single_valued_association_path_expression results in an entity object of the type of the relationship field or the subtype of the relationship field of the entity object as determined by the object/relational mapping.
The result type of an identification_variable is the type of the entity to which that identification variable corresponds or a subtype as determined by the object/relational mapping.
The