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
unless the query uses a projection on owning class
to the embedded instance. For example, if Address
is
embedded in Company
, then
a query "SELECT a FROM Address a"
will never return the
embedded Address
of Company
;
but a projection query such as
"SELECT c.address FROM Company c"
will.
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 the enhancer. 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.3, “
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 4, “ 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.6, “ 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 within the actual entity 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
java.time.LocalDate
java.time.LocalDateTime
java.time.LocalTime
java.time.OffsetDateTime
java.time.OffsetTime
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
java.sql.Time
Enums
Entity types (relations between entities)
Embeddable types
java.util.Collection
s of entities
java.util.Set
s of entities
java.util.List
s of entities
java.util.Map
s 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.