Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
The Java Persistence API was introduced as part of the Enterprise JavaBeans (EJB) 3.0 specification in Java Platform, Enterprise Edition 5 (Java EE). The Java Persistence API draws on ideas from leading persistence frameworks and APIs such as Hibernate, Oracle TopLink, Java Data Objects, (JDO) and EJB container-managed persistence.
The highlights of the Java Persistence API are:
This story walks you through the Java Persistence API's features, using several examples for illustration.
The Java Persistence API is a lightweight, POJO-based framework for object-relational mapping. The mapping between Java objects and a relational database is achieved with the help of Java language metadata annotations and/or XML deployment descriptors (usually XML descriptors are optional). The Java Persistence API heavily depends on the metadata annotations.
The API consists of:
An entity is a lightweight persistence domain object, coded as a POJO, and marked as an entity with the @Entity (javax.persistence.Entity) annotation. To write entity classes, the javax.persistence package is used.
An entity represents a table in a relational database. Each entity instance corresponds to a row in the table. An entity's state is represented either through persistent fields or persistent properties. The entity fields or properties represent the columns in the table.
An entity's persistence state can be accessed either through the entity's instance variables or through the property accessor
methods (JavaBeans-style getter/setter methods) in the entity classes. These instance variables/methods use annotations to
describe the relationships among the entities - such as, the relationships in the relational database. By default, all properties/fields
are persisted into the datastore, except those marked with the @Transient annotation.
Let's take the example of a Dept table that has three columns, ID, name and location, out of which the column id is a primary key. The following code shows the corresponding entity for the domain object Dept:
package university;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Column;
import javax.persistence.Id;
@Entity
@Table (name="Dept")
public class Department implements Serializable {
@Id
@Column(name="id", nullable=false)
private String deptId;
@Column(name="name")
private String deptName;
private String location;
public void setDeptId(String deptId) {
this.deptId = deptId;
}
public String getDeptId() {
return this.deptId;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public String getDeptName() {
return this.deptName;
}
public void setLocation(String location) {
this.location = location;
}
public String getLocation() {
return location;
}
..................
..................
}
In the above example, to mark a simple POJO-based class, the @Entity annotation is used. The table represented by the entity is denoted using the @Tableannotation. If the entity name is the same as that of the table name, @Table is not required. In many cases, defaults are assumed when an annotation is not specified. Entity classes must have a public/protected
no-argument constructor.
The @Column annotation specifies the database column that corresponds to the property/field name. Again, if a property/field is not annotated
with @Column, the column name is assumed to be the same as that of the property/field name in the persistence storage.
The primary key in the entity class can be specified by marking an appropriate persistent property/field with the @Id annotation. The above example uses field-based access. That means the annotation @Id has been used with the deptIdfield. To use property-based access, the annotation @Id has to be placed with the getter/setter methods as demonstrated below:
..................
@Id
public String getDeptId() {
return this.deptId;
}
public void setDeptId(String deptId) {
this.deptId = deptId;
}
..................
When entities have composite primary keys, a primary key class must be defined and annotated using @EmbeddedId (javax. persitence.EmbeddedId) and @IdClass (javax.persistence.IdClass ) annotations.
An entity class may use other fine-grained classes to represent the entity state. These classes are called embeddable classes and do not have any persistent entity of their own. They exist only as embedded objects of the entity to which they belong.
Embeddable classes are similar to entity classes, but are annotated with @Embeddable (javax.persistence.Embeddable). For example, the object Address can be considered as an embeddable object for the entity Student.
In a domain model, relationships depict association among entities. Specifying relationships in EJB 2.1 technology is quite complex with all the container-managed relationship fields being declared in the deployment descriptor. In the Java Persistence API, defining relationships is simpler; annotations are used. These annotations are helpful in specifying multiplicity in relationships as well as directions in relationships.
The relationship multiplicities among entities may be:
@OneToOne(javax.persistence.OneToOne) is used@OneToMany(javax.persistence.OneToMany) is used
@ManyToOne(javax.persistence.ManyToOne) is used
@ManyToMany (javax.persistence.ManyToMany) is used
You can also mention how to fetch the data from the database with the fetch attribute of the relationship annotation. "Lazy" fetching causes the related entity instances to be fetched only when required,
where as "eager" fetching causes the related entity instances to be fetched all at once.
To define how the dependent entities need to be treated with respect to persistence in case of relationships, the cascade
attribute value of the relationship annotation can be set. For example, a value of CascadeType.REMOVE means when an entity instance is removed, the related entity instances are removed as well.
Relationships are characterized by direction — either bidirectional or unidirectional. A bidirectional relationship has both an owning side and an inverse side. A unidirectional relationship has only an owning side. The owning side of the relationship determines the updates to the relationship in the database. For example, for a university department that offers many courses, a many-to-one relationship exists between the entity course and the entity department. Course is the owning side, and department is the inverse side.
The following example illustrates the bidirectional many-to-one relationship between Course and Department:
@Entity
public class Course implements Serializable {
private Department department;
@ManyToOne
public Department getDepartment() {
return Department;
}
public void setDepartment (Department department){
this.department = department;
}
.........
}
@Entity
@Table (name="Dept")
public class Department implements Serializable {
.........
private Collection<Course> courses = new HashSet();
.........
@OneToMany(mappedBy="department", fetch=FetchType.EAGER)
public Collection<Course> getCourses() {
return courses;
}
public void setCourses(Collection<Course> courses) {
this.courses = courses;
}
.........
}
Unlike EJB 2.1 technology (container-managed persistence), the application using the Java Persistence API is responsible for
managing relationships. The Java Persistence API requires the inverse reference in a bidirectional relationship. For example,
if the entity Department has a bidirectional relationship to the entity Course, then the Java Persistence API requires the references to be set on both the sides of the relationship. So, for a bidirectional
relationship, the element mappedBy can be used as the part of the annotation specifying multiplicity in the relationship's inverse side by pointing to the name
of the property/field that owns the relationship. The mappedBy element designates the property/field in the entity that is the owner of the relationship. The default mappings can be used
to simplify the coding of relationship mapping.
The Java Persistence API has an important new feature: support for inheritance and polymorphism. An entity may inherit from another entity class. A hierarchy of entities can be created by one entity subclassing from the other, and queries can be submitted against the base class. By default, the queries are polymorphic and are applicable against the entire entity hierarchy.
An entity can be either an abstract class or a concrete class. Entities can extend non-entity classes, and the inverse is also possible. An abstract entity cannot be directly instantiated, which is the only difference as compared with a concrete entity.
The following example demonstrates the use of an abstract entity class:
@Entity
@Inheritance(strategy=JOINED)
public abstract class Course {
@Id
protected String courseId;
protected String courseName;
protected String duration;
.........
}
@Entity
@Table(name="REG_Course")
@DiscriminatorValue("REG")
@PrimaryKeyJoinColumn(name="REG_CourseID")
public class RegularCourse extends Course {
protected Integer duration;
.........
}
@Entity
@Table(name="PT_Course")
@DiscriminatorValue("PT")
@PrimaryKeyJoinColumn(name="PT_CourseID")
public class PartTimeCourse extends Course {
protected Integer numClasses;
.........
}
There may be a case where a super class provides common state, behavior, and mapping definitions to a set of its subclasses,
but this class itself may not need to be coded as an entity class. Such a class is referred to as a mapped super class. This
class identified using the @MappedSuperClass annotation and has no separate table defined for it. Its mapping information is applied to entities that inherit from it.
The following example demonstrates the use of a mapped super class:
@MappedSuperclass
public class Course {
@Id
protected String courseId;
protected String courseName;
protected String duration;
public String getCourseId() { ... }
public void setCourseId(String courseId) { ... }
.........
}
@Entity
public class RegularCourse extends Course {
protected Integer duration;
.........
public Integer getDuration() {
.........
}
public void setDuration(Integer duration) {
.........
}
.........
}
There may also be cases where an entity class has a non-entity super class (either concrete or abstract) and is used just to provide certain behavior for the inheriting class. The state of a non-entity superclass is not persistent. By default, any state inherited from the non-entity superclass is not persistent in an inheriting entity class and is also not managed.
The @Inheritance annotation is used to define a mapping strategy for an entity class hierarchy. The Java Persistence API defines the following
three basic strategies:
Accordingly, the value for @Inheritance's strategy element could be any one of the three: single-table, joined, table-per-class.
In the single-table-per-class hierarchy strategy, all the entity classes in a hierarchy are mapped to a single table. The table has an additional column that serves as a discriminator column, that is, a column whose value differentiates the different subclass instances corresponding to the data row. This is the default inheritance strategy.
In the joined-subclass strategy, the root of the entity class hierarchy is represented by a single table and separate table for each of the subclasses. The tables representing the root and the subclasses are related by a primary key/foreign key concept. This strategy supports polymorphic relationships among entities, though it requires join operations to collate subclass entity instances along with the root instance. Hence, it is not a recommended strategy with deep entity hierarchies.
In the table-per-concrete-entity-class mapping strategy, each class is mapped to a separate table. All properties of the class, including inherited properties, are mapped to columns of the table for the class. Support for this strategy has been made optional to the persistence providers because it provides poor support for polymorphic relationships.
Entity classes are managed by the entity manager represented by the javax.persistence.EntityManager API. This API manages the lifecycle of the entity instances, retrieves entities, queries entities, and manages the entities'
persistent state.
An EntityManager instance is associated with a persistence context, a set of entity instances in which for any persistent entity identity,
there is a unique entity instance. The entity instances and their lifecycles are managed within the persistence context.
Some of the important methods available in the javax.persistence.EntityManager interface that are used for managing the entities are:
public void persist (Object entity): Makes the entity instance persistent
public void remove (Object entity): Removes the entity instance
public <T>T find (Class <T> entityClass, Object primaryKey): Finds an instance of the entity using a primary key
public Query createQuery (String qlString): Creates an instance of the query object for executing a JPQL statement
public Query createNamedQuery (String name): Creates an instance of the query object for executing a named query
public void flush(): Synchronizes the persistence context to the underlying database
Apart from these methods, the EntityManager contains additional methods such as lock (for setting the lock mode for an entity object) and merge (for merging the state
of the given entity into the current persistence context), among others.
Because the EntityManager interface of the Java Persistence API is used for creating, removing and finding operations on the entities, Java Naming
and Directory Interface (JNDI) lookup is no longer required for retrieving references to resources and other objects. Instead,
annotations can be used, which inject a resource using dependency injection, thereby simplifying the retrieval of resource
and other environmental references.
A persistence unit defines a set of related entity classes required for an application and is managed by the EntityManager instance. These entities in a persistence unit must refer to a single database.
The EntityManager can be used either inside or outside the EJB container, but should be invoked within a transaction context. This allows freedom
to try the instance in Web applications without using EJB.
An EntityManager can be one of the two types: container-managed or application-managed. Java EE containers (Web and EJB) support both container-managed
and application-managed entity managers and their persistence contexts. In case of Java SE environments and Java EE client
containers, only application-managed entity managers are supported.
An entity manager for which the container manages the persistence context is known as a container-managed entity manager;
persistence context is automatically propagated by the container to all the components that use the EntityManager instance within a single Java Transaction API (JTA) transaction. A container-managed entity manager's life cycle is managed
by the Java EE container.
When container-managed entity managers are used (in Java EE environments), the application does not interact with the entity
manager factory. The entity managers are obtained directly through dependency injection using the @PersistenceContext annotation, and the container transparently manages the interaction with the entity manager factory to the application:
@PersistenceContext(unitName="university")
private EntityManager em;
An entity manager used by the application to create and destroy a persistence context is called an application-managed entity
manager; the persistence context is not propagated to the application components, and the application manages the lifecycle
of EntityManager instances.
When application-managed entity managers are used, the application must use the entity manager factory to manage the entity
manager and persistence context lifecycle. Applications create EntityManager instances using the createEntityManager() method of the javax.persistence.EntityManagerFactory. To obtain an EntityManager instance, an EntityManagerFactory instance is injected into the application component using the @PersistenceUnit annotation:
@PersistenceUnit(unitName="university")
private EntityManagerFactory emf;
private EntityManager em = emf.createEntityManager ();
When the application finishes using the entity manager factory, the application should close it.
In the Java Persistence API, transactions involving entity manager operations may be controlled using two transaction management techniques:
javax.persistence.EntityTransaction API is termed a resource-local entity manager
A container-managed entity manager must be a JTA entity manager. An application-managed entity manager may be either a JTA entity manager or a resource-local entity manager. The type of entity manager is specified at the time of configuring/creating the underlying entity manager factory (in persistence.xml). Both JTA entity managers and resource-local entity managers are supported by Java EE Web containers and EJB containers. JTA entity managers are typically used in Java EE environments, and resource-local entity managers are typically used in Java SE environments. Annotations come to the rescue to specify the transaction management type and transaction attributes.
EntityManager methods are used to manage the entity instances and their life cycles. Entity instances can be in one of the following four
states:
A new entity instance becomes both managed and persistent by invoking the EntityManager's persist()method or by the cascading persist operation. The entity will be stored in the database when the transaction associated with
the persist operation is completed:
@PersistenceContext
EntityManager em;
.........
public Course addCourse(int courseId, String courseName) {
Course crs = new Course();
crs.setId(courseId);
crs.setName(courseName);
em.persist(crs);
return crs;
}
.........
The EntityManager's find() method finds an entity. The method takes the class of the entity to be found and the primary key of the entity class as parameters.
When the call successfully executes, a managed entity is returned. A null value returns if no such entity exists and no exception is raised:
@PersistenceContext
EntityManager em;
.........
public void addCourse(int courseId, String courseName) {
Course crs = em.find(Course.class, courseId);
if(crs != null) {
crs.setId(courseId);
crs.setName(courseName);
}
em.persist(crs);
}
.........
A managed instance is removed by invoking the EntityManager's remove() method or by a cascading remove operation invoked from related entities that have the cascade=REMOVE or cascade=ALL elements set in the relationship annotation:
.........
public void removeCourse(int courseId) {
try {
Course crs = em.find(Course.class, courseId);
em.remove(crs);
}...
.........
The state of the persistent entities is synchronized to the database when the transaction with the associated entity successfully
executes. This synchronization involves writing to the database the updates on the persistent entities and their relationships.
Apart from this, synchronization can be forced using the EntityManager's flush() method.
A detached entity lives outside the persistence context in which it was persisted; its state is not synchronized with the
database. A detached entity may arise when the transaction in a transaction-scoped container-managed entity manager commits,
when transaction rollback occurs, if the EntityManager's clear() method is invoked, or if the close() method of the application-managed entity manager is invoked.
Applications must ensure that an entity instance is managed only in a single persistence context. The contains() method can be used to determine whether an entity instance is managed in the current persistence context contains() returns a Boolean value true if the entity instance has not been removed or detached, and if the persist() method has been called on the entity.
The Java Persistence query language (JPQL) defines queries on entities and their persistent states. It provides a portable query language independent of the database in the enterprise environment.
The JPQL is an extension of the EJB QL, but overcomes EJB QL's limitations and provides more features such as:
:))
The JPQL supports both static and dynamic queries. Queries are defined using annotations. The JPQL is not SQL, but is compiled into a target language such as a database's SQL, allowing for optimization and portability of queries.
The JPQL is SQL-like, works on the object model made up of entities and their relationships, instead of the database model.
The Java Persistence API provides the Query interface to execute queries on the entities. There are two types of queries:
EntityManager's createNamedQuery() method. Queries are defined using the @NamedQuery (javax.persistence.NamedQuery) annotation, whose name element specifies the name of the query used in the createNamedQuery() method. The @NamedQuery annotation's query element specifies the query. The @NamedQueries annotation can be used to specify more than one named query:
@NamedQuery(
name="findAllCoursesWithName",
query="SELECT c FROM Course c WHERE c.name LIKE
:courseName")
@PersistenceContext
public EntityManager em;
........
courses =
em.createNamedQuery("findAllCoursesWithName")
.setParameter("courseName", "Java Programming")
.getResultList();
EntityManager's createQuery() method and are defined dynamically by passing the query string to the createQuery() method:
public List findWithName(String name) {
return em.createQuery(
"SELECT c FROM Course c WHERE c.name LIKE :courseName")
.setParameter("courseName", name)
.setMaxResults(10)
.getResultList();
}
The Query interface provides several methods for executing a query. To do bulk update and delete operations, the executeUpdate() method can be used. To retrieve results from the datastore, the getResultList() method is used. This method returns a collection of results, and if only a single result is expected, getSingleResult() method will be used.
Entities are packaged in a persistence unit. A persistence unit is a logical grouping that includes an entity manager factory and its entity managers, a set of managed classes managed by the entity managers of the entity manager factory, and mapping metadata that specifies the mapping of the classes to the database.
Within Java EE environments, persistence units can be packaged
A persistence unit is defined by the persistence.xml file. The jar file or directory whose META-INF directory contains the
persistence.xml file is known as the root of the persistence unit. The persistence unit's scope is determined by the persistence
unit's root. The persistence.xml file is located in the META-INF directory of the persistence unit's root.
In Java EE, the persistence unit's root may be one of the following:
A persistence unit must have a unique name. Only one persistence unit with a given name can be defined in a single EJB jar file, within a single war file, within a single application client jar file, or within an ear file:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="University" >
<provider> Name of the persistence provider </provider>
<jta-data-source>jdbc/university</jta-data-source>
<jar-file>University.jar</jar-file>
<properties>
<property name="....." value="....."/>
</properties>
</persistence-unit>
</persistence>
The persistence.xml defines a persistence unit named University, which uses a JTA-aware datasource jdbc/university. The jta-data-source specifies the global JNDI name of the datasource used by the container. The provider element specifies the entity manager factory name of the persistence provider. The properties element specifies the vendor-specific properties that apply to the persistence unit and its entity manager factory configuration.
The Java Persistence API standardizes the persistence API for the Java platform. It simplifies the development of persistent entities through a simple POJO-based persistence model. The usage of annotations for specifying metadata instead of XML deployment descriptors makes entity coding and deployment easier.
The added features such as support for inheritance and polymorphism, simplistic entity relationship declaration, and feature-rich JPQL, which were not available in the earlier persistent models, are going to add to developer productivity and make entity persistence much more manageable.
Although the Java Persistence API is part of Java EE platform specification, it can be used for Java SE environments too. This is possible because of the Java Persistence API's support outside the EJB container, whereby developers can test the application code outside the container by developing simple test cases to validate the behavior of the entities and the code that interacts with them. This is considered as one of the most significant advantages of the new API.
Thus, the Java Persistence API looks very promising for enterprise Java developers and is likely to be an API of choice for object-relational mapping and persistence in Java applications.
S. Sangeetha is a technical architect at the E-Commerce Research Labs at E&R, Infosys Technologies. She has more than eight years of experience in design and development of Java/J2EE applications. She has also coauthored a book on J2EE architecture.
Anupama Nithyanand is a senior technical architect with E-Commerce Research Labs at E&R, Infosys. She has more than 17 years of experience in education, research, and consulting. Her research interests include databases and middleware technologies. She has been working on Java/Java EE technologies for the past five years.
Read more about Enterprise Java in JavaWorld's Enterprise Java section.
Archived Discussions (Read only)