JPA (the *Java Persistency Architecture) is a JEE standard to map Java objects to relational databases (ORM). It abstract most of the details of interacting with an underlying data access layer such JDBC. Applications are provided with operations to save, get, update, delete, and find Java objects from/to the data-base, without writing query statements (SQL). The JPA/ORM runtime takes care of connection to the underlying database, and creating the appropriated SQL statements. The JPA/ORM runtime can also create the database scheme automatically from meta-data provided by Java reflection and JPA specific Java annotations.
An entity is a java class that represents a domain concept and whose instances have a persistent identity. An entity always have one or more database table where the instances state are stored, but depending on the mapping scheme used a table may be shared by several entities under the same class hierarchy. Each entity instance as a corresponding row in each of the tables used to represent the entity. The simplest case is when a single table is used to represent an entity. In this case, each instance will have a row in that table with entity fields or properties mapped to table column. This limit cases occurs only in there are not other entities in the same class hierarchy or we set in configuration that entities should not share tables. Additionally, if an entity is in related with other entities or support classes than this requires further tables to store the relations.
A java class is declared an entity by using the class-level annotation @Entity
. For this entity classes, fields and properties are mapped to the artifacts in the database. It also possible to provide field-level and method-level annotations how the mapping is done.
Below, we summarize the full requirements that a java class need to comply to be used as a JPA entity:
The class must be annotated with the javax.persistence.Entity
annotation.
The class must have no-argument constructor, with public
or private
modifiers. This is because the JPA run-time need to create by reflection instances of entities to be filled with state values. Other constructor are allowed to be used by the application, but they will not be use directly by the JPA run-time.
The class must not be declared final
. Likewise, persistent fields and all methods (not only property setters and getters) may not be declared final
. This is because to implement some features of JPA, the run-time may create proxied instance that extend the class of the entity. This is used to intercept method invokation and lazily load entities state.
public
. That is, they need to be declared with modifiers private
or private
, or be package-private (no modifiers). Furthermore, access to instance fields can be done directly only by the methods declared in the same class. Other classes need to access instance state only with property getter and setter methods and other business methods. Again, this is because dynamically created proxy objects can only intercept method invokations and not direct field accesses.A further requirement on entity classes is that one, and only one, field or property needs to be declared as primary key. This is done with annotation @Id
. An entity class may also inherit the field/property primary key from a super-class that is mapped to the data-base.
Below, we show an example of a valid entity class representing a User
in some application domain:
@Entity
public class User {
@Id
private long id;
private String name;
private String email;
}
Entity classes may be abstract
or concrete (non-abstract). Abstract entity classes are annotated with @Entity
as concrete classes. However, abstract entities can not be instantiated directly.
Like concrete entity classes, abstract entities can be queried. If a query is performed on a abstract entity, the query operates on all the concrete entity subclasses of the abstract entity.
Entity classes can be related by class inheritance. Entity classes may extend both entity and non-entity classes. Super classes that are entities will provide additional persistent state to entity instances. Super-classes that are not entity themselves may or may not provide additional persistent state. When the non-entity super-class provides persistent state is said to be mapped.
Non-entity classes may also extend entity classes. But the fields or properties defined in this class do not have a persistent state, unless they are mapped classes and are further derived by an entity class.
Below, we show an example of three entity classes related by inheritance:
@Entity
public class A {
@Id
private Integer id;
private String a;
...
}
@Entity
public class B extends A {
private Integer b;
...
}
@Entity
public class C extends A {
private Float c;
}
Class A
defines common fields to the class hierarchy, including the primary key. Derived entity classes B
and C
provide additional fields specific to thoses classes and their derived classes.
Entities may inherit from super-classes that provide persistent state but are not defined as entities. This super-classes are said to be mapped super-classes and are declared with annotation @MappedSuperclass
. Mapped super-classes are useful when their is persistent data common multiple entity classes.
Mapped super-classes, like entity classes, can be abstract or concrete. If a mapped super-class is not abstract it can be instance from a Java, but its instances can not be made persistent. Thus, it is more common to have abstract mapped super-classes, rather than concrete mapped super-classes.
Below, we show a set of classes related by inheritance but with the top-level class being a mapped superclass:
@MappedSuperclass
public abstract class A {
@Id
private Integer id;
private String a;
...
}
@Entity
public class B extends A {
private Integer b;
...
}
@Entity
public class C extends A {
private Float c;
}
Mapped super-classes do not have any corresponding tables in the underlying database. Entities that inherit directly from the mapped superclass introduce the tables used to store the persistent data defined in the mapped supper-class. For the example above, entity classes B
and C
will have each a table in the database. Mapped superclass A
, on the other hand, will not have a corresponding table in the database.
Mapped super-classes can not be queried, thus they can not by used in EntityManager
or Query operations. Only derived entity classes can by queried. Furthermore, mapped super-classes can’t be targets of entity relationships. (This is because the absence of a corresponding database table for mapped super-classes makes the implementation of queries or relations impossible.)
Entities may have also extend non-entity super-classes. These super-classes can be either abstract or concrete. The state of non-entity super-classes is always non-persistent. Thus, any state inherited from the non-entity superclass by an entity class is non-persistent.
Similarly to mapped super-classes, non-entity super-classes may not be used subject to queries. Mapping and relationship annotation present in a non-entity super-classes are ignored. Again, this is because there is not corresponding database table to which the querying operations or relationships can be applied.
The persistent state of an entity can be defined and accessed through either the entity’s instance variables (data-fields) or properties access methods (getters and setters). Entities may use persistent fields, persistent properties, or a combination of both. If the mapping annotations are applied to the field the JPA run-time accesses directly the fields. If the mapping annotations are applied to the getter methods, the JPA run-time uses the property access methods. Due to this equivalence, it is common to use the term property access, also when access is made through fields directly.
Property access methods, if available, should follow JavaBean convenctions, as shown below:
Property Type | Method Category | Method Signature |
---|---|---|
non-boolean | getter | Type getXX() |
setter | void setXX(Type) | |
boolean | getter | Type isXX() |
setter | void setXX(Type) |
Java Bean Conventions for Property Accessors&Modifiers
Above, XX
is the name of the property, Type
is the type of the property, and type
is the name of the setter parameter. Note that for boolean fields (boolean
or Boolean
types), an alternative convention exists.
All entity fields are by default persistent, that is, they state is stored and feteched from the database. A field can by made non-persistent, by annotating it with @Transient
. If a field has a java modifier transient
it is also made non-persistent. No other mapping annotations can be applied to non-persistent fields or properties.
The fields or properties must be of the following Java language types:
Basic Java types:
java.lang.String
Other serializable types:
java.math.BigInteger
java.math.BigDecimal
java.util.Date
java.util.Calendar
java.sql.Date
java.sql.Time
java.sql.TimeStamp
User-defined serializable types
byte[]
Byte[]
char[]
Character[]
Other entities and/or collections of entities:
Collection-valued persistent fields and properties must use the standard Java collection interfaces, show below:
All these types are defined as Generics. This, collections with type-checked elements may also be used by providing the erasure type. Setter and getter methods for collection property follow the same conventions as non-collection properties. As example is shown below:
import java.util.Set;
@Entity
public class User {
...
private Set<Preferences> preferences;
public Set<Preferences> getPreferences() {
return preferences;
}
public void setPreferences(Set<Preferences> preferences) {
this.preferences = preferences;
}
}
All entity instances have a ``special field'' called the primary key, whose value is used to uniquely identify and distinguish between different instance of the same entity class. Thus, each entity class needs to define a persistent field for identifier, or inherit it from an entity or mapped superclass. This requirement is brought about by relational database tecnologies, that required that at least one column on an entity table be used as a unique primary key. Primary keys that take a single column are named simple primary key, while keys that take more than one column are said to be composite primary keys.
A field or property that is a primary key, should be annotated with @Id
. Furthermore, a primary key field or property, must have one ofthe following Java types:
int
, long
, short
, byte
, char
Integer
, Long
, Short
,JPA run-time Byte
, Char
java.lang.String
java.util.Date
java.sql.Date
java.math.BigDecimal
java.math.BigInteger
Floating-point types can never be used as primary keys. Boolean types are also not very useful as primary keys, since they can only take two values. Integer types are the mostly used primitive types for primary keys. If you use a generated primary key, only integral types will be portable.
Composite primary keys are primary keys defined with more than one field or property (and database table column). In JPA, composite primary keys need to defined in a dedicated primary key class. A primary key class should be annotated with IdClass
. Furthermore, a entity field that is a composite primary key should be annotated with @EmbeddedId
.
A composite primary key classes must meet the following requirements:
public
.hashCode()
and equals(Object)
methods.java.io.Serializable
).The fields and properties of a composite primary key class must have the types as simple primary keys. A composite primary key must be represented and mapped to multiple fields or properties of the entity class or must be represented and mapped as an embeddable class.
If the class is mapped to multiple fields or properties of the entity class, the names and types of the primary key fields or properties in the primary key class must match those of the entity class.
Below, we show an example of composite primary key class. The field userId
and jobId
when combined uniquely identify an entity:
public final class PreferenceKey implements Serializable {
private long userId;
private int preferenceId;
public PreferenceKey() {}
public PreferenceKey(long userId, int preferenceId) {
this.userId = userId;
this.preferenceId = preferenceId;
}
public boolean equals(Object obj) {
if (this==obj) return true;
if (!(obj instanceof PreferenceKey)) return false;
PreferenceKey jk = (PreferenceKey) obj;
return userId==jk.userId && preferenceId==jk.preferenceId;
}
public int hashCode() {
return (int) userId ^ preferenceId;
}
public String toString() {
return userId + " " + preferenceId;
}
}
Entities may be related not only by inheritance, but also by association such as in a owner/owned or a contains/part-of relation. A the term entity relation is usually only used for association, to avoid confusion with inheritance relations. This is the convention that we use in this document.
In JPA, relations are defined at the field or property level such that related entity instances are accessible by java references. If an entity class A
is related to other entity class B
, then entity A
defines or inherits a field or property of type B
or a collection whose elements have type B
.
Relations are characterized by the multiplicity and directionality, such that an entity instance can be related to a collection of other entities instances and this relationship might be explict in only on direction or both. JPA annotations are use to qualify the specific kind of relation.
Most kinds of entity relations (all except one-to-one relations), required that additional database artifacts be used to store information about the related entities. This are called joint tables
or relationship tables
, whose columns store the primary keys of the related entities. Thus, is a entity class A
is related to a collection of entities of class B
then this is represented in persistent store as a joint table A-B
, whose columns are the primary keys of A
and B
. This is because in RDBMS, tables need to have a fixed number of columns and column values should not be lists of values. Thus joint tables are the only (reasonable) away to map relations to collections of entity instances.
The multiplicity of a relation specifies throws many instances of an entity in one side of the relation can be related to how many instances in the opposite side of the relation. This concept is already worked out and addressed by RDBMS, and is not specific to ORM or JPA. The different possibilities are described below:
The directionality of a relationship determines if the existence of related entities can be learn from one side or both sides of the relation. In a bidirectional relationship both sides have references to the other side. One of the sides is defined as the owning side, while the opposite side is the owned side or non-owning side. Declaration of bidirectional relation is asymmetric. Only the owned/non-owning side provides information about directionality by setting attribute mappedBy
in the relation specific annotation. For example, @OneToMany(mappedBy="a")
or @ManyToMany(mappedBy="a")
. The value of the annotation’s attribute is the name of the field or property in owning side of the relation that reference the entity in the owned side.
Consider the example:
import java.util.Set;
@Entity
public class User {
@Id
Long id;
@OneToMany(mappedBy="user")
Set<Jobs> jobs;
}
@Entity
public class Job {
@Id
Long id;
@ManyToOne
User user;
...
}
Entity class User
is in a one-to-many relation to entities of class Job
. The relationship is bidirectional since a User
knows about its set of Job
, and Job
knows about its User
by means of field user
. Field User.jobs
is annotated with @OneToMany(mappedBy="user")
thus specifing that the User
is the owning side of the relation.
Below, we summarize the rules for bidirectional relations:
mappedBy
attribute in annotations @OneToOne
, @OneToMany
, or @ManyToMany
. The mappedBy
attribute designates the field or property in the entity that is the owner of the relationship, that refers to the owned side.mappedBy
attribute. The many side is always the owning side of the relationship, that is, the side that has the @OneToMany
annotation.The owning side of a relationship is relevant has it determines how the JPA runtime makes updates to the relationship in the database. Operation on entities may be set to propagated to related entities. However, only the owning side propagates operations.
In a unidirectional relationship, only one entity has a relationship field or property that refers to the other. Examples of unidirectional relations include: SalesItem--Product
, Employee--Category
, and Voter--Candidate
. In all cases, only the first side of the relation contains a reference to the opposite side. For unidirectional relation the attribute mappedBy
is meaningless and should not provided, since there is not field or property on the opposite side that can be specified.
Operation performed on entity may be setup to propage (cascade) to related entities. This is done by setting attribute cascade
of the annotation defining the relation type, such as: @OneToMany(cascade="operation")
. Operation propagation can narrowed to a single type of operation or to all operations. The set of possible operations is defined by enumerate type javax.persistence.CascadeType
enumerated type. Table below summarizes the cascade options defined by JPA. Note that cascade=ALL
is the same as setting cascade={DETACH, MERGE, PERSIST, REFRESH, REMOVE}
. The details on these operation are explained elsewhere.
Cascade Operation | Description |
---|---|
PERSIST | Persist operations are propagate to related entities |
MERGE | Merge operations are propagate to related entities |
REFRESH | Refresh operations are propagate to related entities |
REMOVE | Remove operations are propagate to related entities |
DETACH | Detach operations are propagate to related entities |
ALL | All the above operations propagate to related entities |
Summary cascade options.
As an example, suppose we want persistent operation on instances of entity class User
to be cascade/propagate to a set of related Job
instances. This means that when a User
instance is made persistent, the set of related Job
instance are automatically made persistent. Then we setup the annotation @OneToMany(cascade="PERSIST")
in field of class User
coding the relation, as show below:
@Entity
public class User {
@OneToMany(cascade=PERSIST)
private Set<Job> jobs;
}
In the code fragment below we illustrate the use of cascading, by leaving out the explicit persist operation of created Job
instances.
EntityManager entityManager = ...
User u = new User(..);
u.addJob(new Job(u, "job1"));
u.addJob(new Job(u, "job2"));
//persist user; job are automatically persisted
entityManager.persist(u);
Operation cascading is performed in a recursive away. Thus, if class A
declares that operations should cascade to a related instances of class B
, and class B
class declared that operations should be cascaded to related instances of class C
, then performing the operation on an instance of A
will trigger the same operation on instances of B
and related instances of C
. Formally, one says that operations are defined as transitive.
When a target entity in one-to-one or one-to-many relationship is removed from its relationship, it is often desirable to cascade the remove operation to the target entity. Such target entities are considered orphans, and the orphanRemoval attribute can be used to specify that orphaned entities should be removed.
The orphanRemoval attribute in @OneToMany and @oneToOne takes a Boolean value and is by default false.
For example, if an order has many line items and one of them is removed from the order, the removed line item is considered an orphan. If orphanRemoval is set to true, the line item entity will be deleted when the line item is removed from the order.
The following example will cascade the remove operation to the orphaned customer entity when it is removed from the relationship:
@OneToMany(mappedBy="customer", orphanRemoval="true")
public List<Order> getJobs() { ...
}
@Entity
public class User {
@OneToMany(mappedBy="customer", )
private Set<Job> jobs;
}
Entity class can be related to other classes that have persistent state, but are not entities themselves. These are called embeddable classes
. These classes share the same persistent identiy as the related identity, meaning that the tables and table rows used to store the state of the entity class intances is the same as the related entity class. In JPA, embedded class should be decorated with annotation Embeddable
(but not @Entity
). Furthermore, the field or property getter method for the embedded class in the entity class may be decorated with the @Embedded
annotation, although this is not strictly required. (Because the embedding can be learn from the definition of the embedded class).
Below, we shown an example a entity class User
that has a reference to a embedded class Contact
:
@Entity
public class User {
@Id
private long id
@Embedded
Contact c;
...
}
@Embeddable
public class Contact {
String email;
String address;
String phone;
String country;
...
}
Contact
persistent state (email
, address
, …) shares the database identity of the owning User
. Meaning, that the same table and row is used store a user instance and its contact information (stored in additional columns).
An embeddable classes may have any number of fields or properties and the values may be single-valued or collection-valued. Additionally, embeddable classes may themselves use other embeddable classes to represent their state, which in tur may have other Java types or embeddable classes as field or properties. Embeddable classes may also contain relationships to other entities or collections of entities. If the embeddable class has such a relationship, the relationship is made from the target entity or collection of entities to the entity that owns the embeddable class(es).
Whether the association should be lazily loaded or must be eagerly fetched. The EAGER
strategy is a requirement on the persistence provider runtime that the associated entity must be eagerly fetched. The LAZY
strategy is a hint to the persistence provider runtime.
Entity instances are in one of four states:
Abstract entities can be queried. If an abstract entity is the target of a query, the query operates on all the concrete subclasses of the abstract entity.
Mapped super-classes cannot be queried and can not be used in EntityManager
or Query operations. You must use entity subclasses of the mapped superclass in EntityManager
or Query operations. Mapped super-classes can not be targets of entity relationships. Mapped super-classes can be abstract or concrete.
Java Persistence query language and Criteria API queries often navigate across relationships. The direction of a relationship determines whether a query can navigate from one entity to another. For example, a query can navigate from LineItem to Product but cannot navigate in the opposite direction. For Order and LineItem, a query could navigate in both directions because these two entities have a bidirectional relationship. Cascade Operations and Relationships
Entities that use relationships often have dependencies on the existence of the other entity in the relationship. For example, a line item is part of an order; if the order is deleted, the line item also should be deleted. This is called a cascade delete relationship.
Table below summaries the annotations defined by JPA for classes.
Annotation | Attributes | Description |
---|---|---|
@Entity | Defines an entity | |
@MappedSuperclass | Defines a mapped superclass | |
@Embeddable | Defines a mapped superclass | |
@Table | name schema catalog uniqueConstraints | Define table properties |
JPA annotations for classes.
Table below summaries the annotations defined by JPA to fields or property getter and setters.
Annotation | Attributes | Description |
---|---|---|
@Basic | fetch optional | Basic field/property |
@Id | Primary key field/property | |
@GeneratedValue | strategy | Id generation strategy |
@Transient | field/prop without persistent state | |
@Column | name | column name in table |
@Embedded | embedded field/prop | |
@OneToOne | mappedBy cascade fetch optional targetEntity | one-to-many relationship |
@OneToMany | – | one-to-many relationship |
@ManyToOne | – | reverse of one-to-many bidirectional relationship |
@ManyToMany | – | many-to-many relationship |
JPA annotations for fields and property setters and getters.
Possible id generation strategies:
enum GenerationType {
AUTO,
IDENTITY,
SEQUENCE,
TABLE
}
Id Generation Strategy | Description |
---|---|
AUTO | JPA run-time automaticaly selects the strategy |
IDENTITY | Primary keys retrieved from the identity column |
SEQUENCE | Primary keys retrieved from the sequence column |
TABLE | Primary keys retrieved from a dedicated table |
Summary of id generation strategy defined by enumaration type GenerationType
.
Below, we shown an example of an entity definition with explict identifier generation strategy:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
long id;
private String name;
}
The Java API for bean validation (JSR303) defines the annotations and mechanism for validating application data. Bean validation can be used in several tiers of the application not only the data-access and persistence layer.
Bean Validation constraints are annotations applied to the fields or properties of Java programming language classes. Bean Validation provides a set of constraints as well as an API for defining custom constraints. Custom constraints can be specific combinations of the default constraints, or new constraints that don’t use the default constraints. Each constraint is associated with at least one validator class that validates the value of the constrained field or property. Custom constraint developers must also provide a validator class for the constraint.
Bean Validation constraints are applied to the persistent fields or properties of persistent classes. When adding Bean Validation constraints, use the same access strategy as the persistent class. That is, if the persistent class uses field access, apply the Bean Validation constraint annotations on the class’s fields. If the class uses property access, apply the constraints on the getter methods.
Bean Validation constraints may be applied to persistent entity classes, embeddable classes, and mapped super-classes. By default, the Persistence provider will automatically perform validation on entities with persistent fields or properties annotated with Bean Validation constraints immediately after the PrePersist, PreUpdate, and PreRemove lifecycle events.
Bean Validation will throw a validation error. Similarly, if a previously created instance of Contact has been modified so that firstName or lastName are null, a validation error will be thrown.
The @NotNull
annotation specifies that a reference to an object can not be null (is required). Converselly, the annotation @NotNull
specifies that a reference to an object need to be null (is not allowed).
For integer numbers range contraints are specified with annotations @Max
and @Min
. @Max
specifies that the property value needs to be less-than or equal to specified value, while @Min
specifies that the property value needs to be greater-than or equal to specified value. The combination of the two contraints can be used to specify that the property value should be within an inclusive integer range $[min,max]$.
Similarly, for decimal numbers range contraints are specified with annotations @DecimalMax
and @DecimalMin
. @DecimalMax
specifies that the property value needs to be less-than or equal to specified value, while @DecimalMin
specifies that the property value needs to be greater-than or equal to specified value. The combination of the two contraints can be used to specify that the property value should be within an inclusive numeric range $[min,max]$.
For decimal numbers it also possible to contraint the maximum number of digits for the integer and fractional part of the number. This is done with annotation @Digits
. Attribute integer
specifies the maximum number of integer digits. Attribute fraction
specifies the maximum number of fractional digits.
Date values may be contraint in relation to present time and date. Annotation @Past
specifies that the value needs to be before the current date (i.e. represents an event that occured in the past). Annotation @Future
specifies that the value needs to be after current date (i.e. represents an event that will occur in the future).
The email field has a @Pattern constraint applied to it, with a complicated regular expression that matches most valid email addresses. If the value of the email does not match this regular expression, a validation error will be thrown.
Table below summarizes the annotations to constraint field/property values defined in JSR303. All this annotations are defined in package javax.validation.constraints
.
Annotation | Type(s) | Description |
---|---|---|
@NotNull | Object | Value must not be null |
@Null | Object | Value must be null |
@Max | Numeric | Maximum allowed integer value |
@Min | Numeric | Minumum allowed integer value |
@DecimalMax | Numeric | Decimal value less-than or equal to specified value |
@DecimalMin | Numeric | Decimal value greater-than or equal to specified value |
@Digits | Numeric | Max. number of integer and fraction digits |
@Past | Date | Date must be in the past. |
@Future | Date | Date must be the future |
@AssertFalse | boolean | Value must be false |
@AssertTrue | boolean | Value must be true |
@Size | String|Collection|Map|Arrray | Min. and Max. length/size |
@Pattern | String | Value must match the regular expression |
Summary of annotations to constraint field/property values (JSR303).
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
//Value must not be null
@NotNull String name;
//Value must be null
@Null String oldName;
//Decimal value less-than-or-equal and greater-than-or-equal specified values
@DecimalMin(1000.00)
@DecimalMax(5000.00)
BigDecimal salary;
//Max. number of integer and fraction digits
@Digits(integer=6, fraction=2)
BigDecimal budget;
//Minimum and Maximum allowed integer value
@Min(0) @Max(10) int n;
//Date must be in the past
@Temporal(javax.persistence.TemporalType.DATE)
@Past Date born;
//Date must be the future
@Future Date event;
//Min. and Max. length/size
@Size(min=6, max=10)
String password;
//Value must be false
@AssertFalse
boolean isUnsupported;
//Value must be true
@AssertTrue
boolean isActive;
//Value must match regular expression: 10 digit phone number like: (xxx)xxx-xxxx
@Pattern(regexp="\\(\\d{3`\\)\\d{3`-\\d{4`", message="{invalid.phonenumber`")
String phoneNumber;
}
All the built-in constraints defined in JSR303 have a corresponding annotation for grouping multiple constraints of the same type on the same field/property. For a constraint annotation named Constraint
, the list annotation is name Constraint.List
since is defined as a inner annotation. If a list of contraints is present only one of the contraints needs to be valid.
Below, we show an example of a group of @Pattern
constraints:
@Pattern.List({
@Pattern(regexp="..."),
@Pattern(regexp="...")
`)
Entities are mapped to database tables. For each class or class hierarchy tree a different table is used. Entity fields and properties are mapped to column in the respective table. The naming of tables and column is automatically selected by the JPA implementation, but can also be controlled with annotations. For entites the class annotation @Table("name")
is used to specify the table name to use. For fields and property getter the class annotation @Columns("name")
should be used.
Explicity selection of table and column names is in many cases unnecessary. However, there are cases in which explicit naming is necessary, such as:
Consider a example of a class User
as shown below:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column("key")
long id;
String name;
String email;
@Column("dob")
Date born;
BigDecimal salary;
@Transient
boolean loggedIn;
List<Job> jobs;
}
Consider also that we create a few instance of class User
. Then the database representation of the User
entity will be a table something like the one shown below:
key | name | dob | salary | |
---|---|---|---|---|
1 | Joe Slow | joe@jpa-lovers.org | 10-10-1970 | 1100.5 |
2 | Mary Wise | mary@java-fans.net | 11-12-1975 | |
3 | Albert Jr. | albert@kindernet.net | 1-1-2008 |
Scheme and actual table mapping an entity class User
.
Entity fields and other persistant state is stored in the database as table columns. The JPA run-time automatically assigns or assumes a name to for the table column from the name of the field or property. However, this can be modified by decorating the field or property with annotation @Column("name")
. The reasons to be explicity about the naming of a column are the same as the ones one might want to be explicity about the name of a table.
If a field or property of an entity consists of a collection of basic types or embeddable classes, use the javax.persistence.ElementCollection annotation on the field or property.
The two attributes of @ElementCollection are targetClass and fetch. The targetClass attribute specifies the class name of the basic or embeddable class and is optional if the field or property is defined using Java programming language generics. The optional fetch attribute is used to specify whether the collection should be retrieved lazily or eagerly, using the javax.persistence.FetchType constants of either LAZY or EAGER, respectively. By default, the collection will be fetched lazily.
The following entity, Person, has a persistent field, nicknames, which is a collection of String classes that will be fetched eagerly. The targetClass element is not required, because it uses generics to define the field.
@Entity
public class User {
...
@ElementCollection(fetch=EAGER)
private Set<String> emails = new HashSet();
...
}
Collections of entity elements and relationships may be represented by java.util.Map collections. A Map consists of a key and a value.
When using Map elements or relationships, the following rules apply.
If the key type of a Map is a Java programming language basic type, use the annotation javax.persistence.MapKeyColumn to set the column mapping for the key. By default, the name attribute of @MapKeyColumn is of the form RELATIONSHIP-FIELD/PROPERTY-NAME_KEY
. For example, if the referencing relationship field name is image, the default name attribute is IMAGE_KEY
.
If the key type of a Map is an entity, use the javax.persistence.MapKeyJoinColumn annotation. If the multiple columns are needed to set the mapping, use the annotation javax.persistence.MapKeyJoinColumns to include multiple @MapKeyJoinColumn annotations. If no @MapKeyJoinColumn is present, the mapping column name is by default set to RELATIONSHIP-FIELD/PROPERTY-NAME_KEY
. For example, if the relationship field name is employee, the default name attribute is EMPLOYEE_KEY
.
If Java programming language generic types are not used in the relationship field or property, the key class must be explicitly set using the javax.persistence.MapKeyClass annotation.
If the Map key is the primary key or a persistent field or property of the entity that is the Map value, use the javax.persistence.MapKey annotation. The @MapKeyClass and @MapKey annotations cannot be used on the same field or property.
If the Map value is a Java programming language basic type or an embeddable class, it will be mapped as a collection table in the underlying database. If generic types are not used, the @ElementCollection annotation’s targetClass attribute must be set to the type of the Map value.
If the Map value is an entity and part of a many-to-many or one-to-many unidirectional relationship, it will be mapped as a join table in the underlying database. A unidirectional one-to-many relationship that uses a Map may also be mapped using the @JoinColumn annotation.
If the entity is part of a one-to-many/many-to-one bidirectional relationship, it will be mapped in the table of the entity that represents the value of the Map. If generic types are not used, the targetEntity attribute of the @OneToMany and @ManyToMany annotations must be set to the type of the Map value.
Information about entity relations is stored explicitilty in the database artifacts.
For one-to-one relation, entity tables include a column for the primary key of the related entity, designated as a foreign key
. If the relation is bidirectional only one table of the two related entites, has a forign key. If the relation is bidirectional, both table have a forign key.
Consider the example below with a bidirectional one-to-one relation between entities User
and Profile
:
@Entity
public class User {
@Id
@Column("userId")
private int id;
private String name;
...
@OneToOne(mappedBy="user")
private Profile profile;
}
@Entity
public class Profile {
@Id
@Column("profileId")
private int id;
private String education;
private User user;
...
}
This produces the following database schemas for the table for entity User
and Profile
:
userId K | NAME | … | profileId FK |
---|---|---|---|
1 | Joe | … | 1 |
2 | Jenny | … | 3 |
3 | Albert | … | 5 |
Tables for entities User
and Profile
with foreign keys mapping a bidirectional one-to-one relation.
profileIdK | EDUCATION | … | userIdFK |
---|---|---|---|
1 | B.Sc. | … | 1 |
3 | Ph.D. | … | 2 |
4 | M.Sc. | … | 3 |
Tables for entities User
and Profile
with foreign keys mapping a bidirectional one-to-one relation.
For convenience, primary keys are marked with superscript <sup>K</sup>
and foreign keys are marked with superscript <sup>FK</sup>
.
For other one-to-many/many-to-one and many-to-many relations, information is stored using joint tables whose columns are the primary keys of related entities. For embeddable classes the persistent state is also stored in the joint tables.
Consider the example below of a one-to-many bidirectional relation between entities User
and Job
:
@Entity
public class User {
@Id
Long id;
...
@OneToMany(mappedBy="user")
Set<Jobs> jobs;
}
@Entity
public class Job {
@Id
@Column("jobId")
Long id;
...
@ManyToOne
User user;
}
Then the following joint-table is setup or assumed by the JPA provider:
userId | profileId |
---|---|
1 | 1 |
1 | 22 |
2 | 3 |
3 | 4 |
Joint table for entities User
and Job
.
When entities are not related with other entities by inheritance, there is a simple mapping between entities and database tables. Each entity gets a separeted table in the database. Things become more elaborated when entities are related by inheritance. The way the JPA provider maps a set of entities related by inheritance is knows as the inheritance mapping strategy. The strategy to use is defined with class annotation @Inheritance
, that should be setup only at the root of entity tree. The mapping strategies known to JPA are defined by enumerate type InheritanceType
, whose values are shown below:
public enum InheritanceType {
SINGLE_TABLE,
JOINED,
TABLE_PER_CLASS
`;
The meaning of each of values is explained below:
The default strategy is InheritanceType.SINGLE_TABLE
. This is the value use if the @Inheritance
annotation is not specified on the root class of the entity hierarchy.
With this strategy, which corresponds to the default InheritanceType.SINGLE_TABLE
, all classes in the hierarchy are mapped to a single table in the database. This table has a discriminator column containing a value that identifies the subclass to which the instance represented by the row belongs.
The discriminator column can be specified by using the javax.persistence.DiscriminatorColumn annotation on the root of the entity class hierarchy.
Table below summarizes the attributes of the @DiscriminatorColumn
annotation.
Name | Type | Default | Description |
---|---|---|---|
name | String | DTYPE | Name of discriminator column |
discriminatorType | DiscriminatorType | STRING | Type of dicriminator column |
columnDefinition | String | (Impl.Dep.) | SQL fragment to create discriminator column |
length | String | 31 | length of String discriminator column |
Summary of attribute of @DiscriminatorColumn
annotation.
The definition of the enumeration type DiscriminatorType
is shown below:
public enum DiscriminatorType {
STRING,
CHAR,
INTEGER
};
If @DiscriminatorColumn is not specified on the root of the entity hierarchy and a discriminator column is required, the Persistence provider assumes a default column name of DTYPE and column type of DiscriminatorType.STRING.
The javax.persistence.DiscriminatorValue annotation may be used to set the value entered into the discriminator column for each entity in a class hierarchy. You may decorate only concrete entity classes with @DiscriminatorValue.
If @DiscriminatorValue is not specified on an entity in a class hierarchy that uses a discriminator column, the Persistence provider will provide a default, implementation-specific value. If the discriminatorType element of @DiscriminatorColumn is DiscriminatorType.STRING, the default value is the name of the entity.
This strategy provides good support for polymorphic relationships between entities and queries that cover the entire entity class hierarchy. However, this strategy requires the columns that contain the state of subclasses to be nullable.
In this strategy, which corresponds to InheritanceType.TABLE_PER_CLASS
, each concrete class is mapped to a separate table in the database. All fields or properties in the class, including inherited fields or properties, are mapped to columns into the class’s table.
This strategy provides poor support for polymorphic relationships and usually requires either SQL UNION queries or separate SQL queries for each subclass for queries that cover the entire entity class hierarchy.
Support for this strategy is optional and may not be supported by all Java Persistence API providers. The default Java Persistence API provider in the GlassFish Server does not support this strategy.
In this strategy, which corresponds to InheritanceType.JOINED, the root of the class hierarchy is represented by a single table, and each subclass has a separate table that contains only those fields specific to that subclass. That is, the subclass table does not contain columns for inherited fields or properties. The subclass table also has a column or columns that represent its primary key, which is a foreign key to the primary key of the superclass table.
This strategy provides good support for polymorphic relationships but requires one or more join operations to be performed when instantiating entity subclasses. This may result in poor performance for extensive class hierarchies. Similarly, queries that cover the entire class hierarchy require join operations between the subclass tables, resulting in decreased performance.
Some Java Persistence API providers, including the default provider in the GlassFish Server, require a discriminator column that corresponds to the root entity when using the joined subclass strategy. If you are not using automatic table creation in your application, make sure that the database table is set up correctly for the discriminator column defaults, or use the @DiscriminatorColumn annotation to match your database schema. For information on discriminator columns, see The Single Table per Class Hierarchy Strategy.
The EntityManager
Interface:
String PERSISTENCE_UNIT_NAME = "Demo1";
EntityManagerFactory factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
EntiryManager entityManager = factory.createEntityManager();
entityManager.getTransaction().begin();
//...do real work inside transactional contex
entityManager.getTransaction().commit();
entityManager.close();
An EntityManager
provides the API need to manage entity instance, including: creation, removal, of persistent context.
New entities are made persistent by invoking method EntityManager.persist()
. From this point on, the entity will have a persistent entity on the data store.
If the persist method is called on a entity that is already in a managed and persistent state the operation has not effect on that entity, but can affect related entities (see below). If the persist method is called on a removed entity instance, the entity becomes managed again. If the entity is detached, the exception IllegalArgumentException
is thrown if there no on-going transaction. If there is an on-going transaction the commit operation will fail.
A simple example is shown below, where an entity instance of class User
is made persistent.
EntityManager entityManager = ... ;
public User createUser(String name, String email) {
User u = new User(name, email);
entityManager.persist(u);
return u;
}
Another example is shown below, where a Job
entity is related to a owning User
entity. Note that the owning User
is updated to reference the created job. However, it the User
entity does not need to be made persistent again. Modifications to the User
entity will be propagate to the database at transaction commit time or flushing time.
EntiryManager entityManager = ... ;
public Job createJob(User u, String jobName, String descr) {
Job j = new Job(u, jobName, descr);
u.getJobs().add(j);
entityManager.persist(j);
return j;
}
When EntityManager.persist()
is invoked on an entity that as related entities, and the cascade
attribute is set to PERSIST
or ALL
in the relationship annotation, then the persist action is propagate to those related entities. This process applies recursively so that if a related entity $A$ is related to yet another entity $B$, $B$ is also made persistence provided that that cascade option set appropriately for that relation. This means that a persist operation might affect the state of a large tree of entities. (Formally, the persistence operation with the cascade options is said to be transitive.).
Note that if an entity is already in a managed and persistent state the operation persist()
has not effect on the entity, but the recursive propagation rule still applies to that entity and its related entities.
As example of persist propagation, consider relation defined below:
@OneToMany(cascade=ALL, mappedBy="user")
public List<Job> getJobs() {
return jobs;
}
Makes persist actions on a User
entity to propagate to all related Job
entities.
User u = ...;
u.addJob(new Job(u, "job1"));
u.addJob(new Job(u, "job2"));
entityManager.persist(u);
The EntityManager.find()
method is used to look up entities in the data store by the entity’s primary key:
EntiryManager entityManager = ... ;
public void addJob(int id, Job j) {
User u = entityManager.find(User.class, id);
j.setUser(u);
u.getContacts().add(c);
}
Managed entity instances are removed from the database by invoking the method EntityManager.remove
. An entity instance set into the removed state, is scheduled for removal. A remove operation is mapped to a SQL DELETE statment. The statement is executed when the on-going transaction commits or a flush is performed. If remove is invoked on a detached entity, the remove operation throws an IllegalArgumentException
exception if there is not on-going transcation. If a transaction is open the commit operation will fail.
When a entity is deleted all related entities for which the cascade
option is set to REMOVE
or ALL
are also removed. As in the persist operation, the remove propagation is applied recursively. If instance asked for removal is a new entity and is not yet in a managed state, the operation is ignored for that entity since there is no persistante state to be removed from the store. However, the propagation rule still applies if the instance is related to other managed entities set with the cascade option appropriatelly.
public void removeUser(long id) {
try {
User u = entityManager.find(User.class, id);
entityManager.remove(u);
} catch(..) {
}
}
In the example above, a user is searched and removed from the store. All related Job
instance will also be scheduled for removal, assuming the cascade
option is set to REMOVE
or ALL
.
Table below summarizes the methods of the EntityManager
class to manage individual entities.
Method | Returns | Description |
---|---|---|
persist(Object entity) | Make entity instance persistent | |
merge(T entity) | Merge the state of the entity | |
refresh(Object entity) | Refresh the in-memory state of the instance | |
remove(Object entity) | Remove the entity instance | |
contains(Object entity) | boolean | Check if the instance exists |
find(Class<T> type, Object key) | T | Find by primary key |
getReference(Class<T> type, Object key) | T | Get lazily fetched instance (proxy) |
Summary of EntityManager
methods to manage individual entities.
The state of persistent entities is synchronized to the database when an on-going transaction associated with the entity commits. It also possible to force synchronization of managed entities to the data store, by invoking method EntityManager.flush()
.
If the entity is related to another entity and the relationship annotation has the cascade element set to PERSIST or ALL, the related entity’s data will be synchronized with the data store when flush is called.
If the entity is removed, calling flush will remove the entity data from the data store.
If a managed entity is in a bidirectional relationship with another managed entity, the data will be persisted, based on the owning side of the relationship.
Table below summarizes the methods of the EntityManager
class.
Method | Returns | Description |
---|---|---|
flush() | Synchronize the persistence context | |
setFlushMode(FlushModeType mode) | Set the flush mode | |
getFlushMode() | FlushModeType | Get the flush mode |
Summary of EntityManager
flushing methods.
Table below summarizes the methods of the EntityManager
class for management of the full persistence context.
Method | Description |
---|---|
clear() | Clears the persistence context (detach all entitied) |
isOpen() | Check if EntityManager is open |
close() | Close the EntityManager |
getDelegate() | Get provider object for the EntityManager |
Summary of EntityManager
methods for management of the full persistence context.
A persistence unit defines a set of all entity classes that are managed by EntityManager
instances in an application. This set of entity classes represents the data contained within a single data store.
Persistence units are defined in a XML configuration file that should be named persistence.xml
. This file should be located in a directory named META-INF
. In eclipse IDE this is located under directory src
.
Below we show an example:
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="UserJobs" transaction-type="RESOURCE_LOCAL">
<description>
This a demo persistent unit with the description of users and jobs.
</description>
<class>demojpa.User</class>
<class>demojpa.Job</class>
<jar-file>UserJobs.jar</jar-file>
<jta-data-source>jdbc/MyOrderDB</jta-data-source>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" />
<property name="javax.persistence.jdbc.url"
value="jdbc:derby:db/demo1;create=true" />
<property name="javax.persistence.jdbc.user" value="test" />
<property name="javax.persistence.jdbc.password" value="test" />
<!-- EclipseLink specific properties -->
<property name="eclipselink.ddl-generation" value="create-tables" />
<property name="eclipselink.ddl-generation.output-mode"
value="database" />
</properties>
</persistence-unit>
</persistence>
Table below summarizes the methods of the EntityManager
class to create Query
objects.
Method | Description |
---|---|
createNamedQuery(String name) | Create named Query (JPQL or SQL) |
createNativeQuery(String sql) | Creates SQL query (UPDATE or DELETE) |
createNativeQuery(String sql, Class resultType) | Create SQL query (SELECT) |
createNativeQuery(String sql, String resultSetMapping) | Creates a SQL query |
createQuery(String ql) | Create JPQL query |
Summary of EntityManager
methods for creationg of Query
objects.
Table below summarizes the methods of the EntityManager
class for management of the full persistence context.
Method | Returns | Description |
---|---|---|
getTransaction() | EntityTransaction | Get resource-level transaction object |
joinTransaction() | Indicate that a JTA transaction is active | |
lock(Object entity, LockModeType lockMode) | Set the lock mode for entity |
Summary of EntityManager
methods for transactions and locking.
@Singleton
public clas MyService {
@PersistenceContext
private EntityManagerFactory emf;
private EntiryManager entityManager;
@Resource
UserTransaction tx;
public void doService() {
entityManager = emf.createEntityManager();
try {
tx.begin();
...
tx.commit();
} catch (Exception e) {
utx.rollback();
}
}
}
Comments and Discussion