ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Standardizing Java Persistence with the EJB3 Java Persistence API

by Debu Panda
05/17/2006

Persistence is critical for most enterprise applications because they require access to relational databases such as Oracle Database 10g. If you're developing applications using Java, you're probably responsible for mundane tasks such as database updates and retrieval, which you do by writing JDBC and SQL. Over the last few years, several object-relational (O-R) mapping frameworks such as Oracle TopLink and JBoss Hibernate have become popular because they make persistence challenges simpler, freeing Java developers to write JDBC code and let them concentrate on business logic. Java standards such as EJB 2.x container-managed persistence (CMP) entity beans have also tried to solve persistence challenges but have been less successful.

Although there are several options for building the persistence tier of applications, there is no single persistence standard for the Java platform that can be used in both the Java EE and Java SE environments. The good news is that the EJB3 Java Persistence API (JPA), which is part of the EJB 3.0 spec (JSR-220), standardizes persistence API for the Java platform. JSR-220 was widely received by O-R mapping vendors such as TopLink and Hibernate, as well as other leading application server vendors and JDO vendors. The EJB3 specification offers a compelling option for building the persistence tier of enterprise Java applications.

In this article, I'll introduce the EJB3 Java Persistence API by using a simple domain object model as an example.

The Domain Model

When you build an enterprise application, you first design the domain object model that you want to persist in the database; then, you work with database designers to come up with the database schema. The domain model is the representation of persistent objects or entities. An entity is a person, place, or thing about which you store data. It contains both data and behavior. A rich domain model has the characteristics of all object-oriented behavior, such as inheritance and polymorphism.

Our simple domain model (Figure 1) has a bidirectional one-to-many relationship between Department and Employee entities. The FullTime and Contractor entities inherit from the Employee entity.

Figure 1
Figure 1. Sample domain object model

The Basics of the O-R Framework and EJB3 JPA

If you've used an O-R mapping framework such as Oracle TopLink to build the persistence tier of your application, you'll notice that each framework provides three facilities:

  1. A declarative way to perform O-R mapping. This method, called O-R mapping metadata, lets you map an object to one or more database tables. Typically most O-R frameworks use XML for storing O-R mapping metadata.
  2. An API to manipulate entities (for example, to perform CRUD operations). The API lets you persist, retrieve, update, or remove objects. Based on the use of the API and the O-R mapping metadata, the O-R framework performs database operations on your behalf. The API shields you from writing JDBC or SQL code to persist your domain objects.
  3. A query language to retrieve objects. This is one of the most important aspects of persistence because improper SQL statements may slow down your database. This approach also shields your application from proprietary SQL being cluttered all over your applications. A query language lets you retrieve entities or objects and spares you from writing SQL SELECT statements.

The EJB3 Java Persistence API (JPA) standardizes the use of persistence for the Java platform by providing a standard mechanism for O-R mapping, an EntityManager API to perform CRUD operations, and a way to extend EJB-QL to retrieve entities. I'll discuss these three aspects of JPA later.

Metadata Annotation in Action

Java SE 5.0 introduced metadata annotations. All components of Java EE, including EJB3 JPA, heavily use metadata annotations to simplify enterprise Java development. To learn more about metadata annotations, see Bridging the Gap: J2SE 5.0 Annotations by Kyle Downey. In EJB3 JPA, annotations may be used to define objects, relationships, O-R mapping, and the injection of persistent contexts. JPA also provides an option to use XML descriptors instead. I'll primarily focus on using metadata annotations because they make development much simpler. However, you may prefer to use XML descriptors in a production deployment environment because you can use them to override annotations.

Enterprise JavaBeans 3.0

Related Reading

Enterprise JavaBeans 3.0
By Richard Monson-Haefel, Bill Burke

Standardizing O-R Mapping in JPA

Defining Persistent Objects: Entities

An entity is a lightweight domain object--a Plain Old Java Object (POJO) that you want to persist in a relational database. Like any POJO, an entity may be either an abstract or a concrete class, and it can extend another POJO. You can use the javax.persistence.Entity annotation to mark a POJO to be an entity.

Here's how you make the Department object in the domain model an entity:

package onjava;
import java.io.Serializable;
import java.util.Collection;
import javax.persistence.*;
@Entity
@NamedQuery(name="findAllDepartment", query="select o from Department o")
@Table(name="DEPT")
public class Department implements Serializable {
    @Id
    @Column(nullable=false)
    protected Long deptNo;
    @Column(name="DNAME")
    protected String name;
    @Column(name="LOC")
    protected String location;
    @OneToMany(mappedBy="department")
    protected Collection<Employee> employees;
    public Department() {
    }
    ...
    public Collection<Employee> getEmployees() {
        return employees;
    }
      public void setEmployees(Collection<Employee> employees)    {
        this.employees = employees;
    }
    public Employee addEmployee(Employee employee) {
        getEmployees().add(employee);
        employee.setDepartment(this);
        return employee;
    }
    public Employee removeEmployee(Employee employee) {
        getEmployees().remove(employee);
        employee.setDepartment(null);
        return employee;
    }
}

Every entity has a primary key; you can use the Id annotation on a persistent field or property to mark it to be the primary key. An entity maintains its state by using either fields or properties (via setter and getter methods). This depends on where you use O-R mapping annotations. The above example uses field-based access; we've used Id annotation with the deptNo field. To use property-based access, you'd mark annotations such as Id with the properties as follows:

@Id
public Long getDeptNo() {
        return deptNo;
 }
 public void setDeptNo(Long deptNo) {
        this.deptNo = deptNo;
 }

Remember that the same access type, either field or property, must be used for all entities in an entity hierarchy.

Every field defined in an entity is, by default, persistent in nature; if you don't want to save the state for a field/property, you must define the field/property as a transient by marking it with the @Transient annotation or by using a transient modifier.

Embeddable Objects

An embeddable object is a persistent object with no identity of its own; it's part of another entity. For example, we can assume that Address has no identity of its own and is stored as a part of the Employee entity. Thus, Address is a candidate for an embeddable object.

You can create an embeddable object like this:

@Embeddable 
 public class Address { 
 protected String streetAddr1; 
 protected String streetAddr2; 
 protected String city; 
 protected String state; 
..
}

Here's how to define the object as an embedded object in the target entity:

    @Entity
    public class Employee {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    protected Long id;                                              
    ...
    @Embedded
    protected Address address;                                      
    ...
}

Relationships

In a typical domain model, entities are associated with each other or may have relationships between them. The relationship between two entities can be one-to-one, one-to-many, many-to-one, and many-to-many. These relationships can be expressed using OneToOne, OneToMany, ManyToOne, or ManyToMany annotations, respectively. Our example has a bidirectional OneToMany relationship between the Department and Employee entities.

Since we're using field-based access in our entities, we've specified the annotation on the relationship field of the Department entity as follows:

@OneToMany(mappedBy="department") 
protected Collection<Employee> employees ;

For a bidirectional relationship, you must specify the mappedBy element (as above) in the inverse side of the relationship by pointing to the name of the field or property that owns the relationship.

Standardizing O-R Mapping

You can either use Java metadata annotations or XML to do the O-R mapping for the entities. The EJB3 JPA defines several annotations such as Table, SecondaryTable, Column, JoinColumn, and PrimaryKeyJoinColumn for O-R mapping. Refer to the EJB3 JPA specification for all annotations.

As in our example, you can use the Table annotation to define what table the entity is mapped to, as follows:

@Table(name="DEPT") 
public class Department implements Serializable {

EJB3 JPA depends heavily on defaults and hence, if you don't define the table mapping, the persistence provider will assume that the entity is mapped to a table with the same name as the entity class (DEPARTMENT, in our example). If your entity is mapped to more than one table, you can use the SecondaryTable annotation.

You can use the Column annotation to map a persistent field or property to a database column, like this:

@Column(name="DNAME") 
protected String name;

Here, DNAME is the name of the column to which the persistent field name is mapped. If you don't define O-R mapping with the Column annotation, the persistence engine will try to save its state in a column with the same name as the field or property name.

Entity Inheritance

EJB3 JPA supports several methods of entity inheritance. It requires two types of inheritance table-mapping strategies: Single-table-per-entity hierarchy strategy and Joined-Subclass strategy. The optional table-per-class hierarchy is best avoided.

The single-table-per-entity (SINGLE_TABLE) hierarchy strategy allows all entities in the hierarchy to map to a single table. In our example, FullTime and Contractor extend Employee, and all of these can be mapped to a single table named EMP. In other words, all data related to Employee, FullTime, and Contractor is stored in the same table.

If you use the Joined Subclass strategy, you can store the common persistent data in the table to which the superclass is mapped (such as Employee), and you can create tables for each subclass in the hierarchy to store persistent fields specific to the subclass.

You must use the Inheritance annotation in the superclass to specify the inheritance type, as in the following code. This example shows the entity hierarchy using the single-table-per-entity hierarchy strategy.

 @Entity 
 @Table(name="EMP") 
 @Inheritance(strategy=InheritanceType.SINGLE_TABLE) 
 @DiscriminatorColumn(name="EMPLOYEE_TYPE",
                discriminatorType=DiscriminatorType.STRING, length=1) 
 public abstract class Employee implements Serializable { 
 ...
 }

Each subclass must specify the discriminator value used for that entity type, as follows:

@Entity 
@DiscriminatorValue(value="F") 
public class FullTime extends Employee { 
@Column(name="SAL") 
protected Double salary; 
@Column(name="COMM") 
protected Double commission; 
@Column(name="DESIG")
protected String designation;
...
}

The Entity Manager API: Standard API for Entity operations

The javax.persistence.EntityManager manages entity lifecycles and exposes several methods to perform CRUD operations on entities.

The EntityManager API is invoked in a transaction context. You can invoke it outside of the EJB container--for instance, from a Web application--without needing a session bean façade.

You must grab an instance of EntityManager before you can perform any entity operation. You can use either a container-managed or an application-managed entity manager, and you can use either JNDI lookup or dependency injection to grab an instance of EntityManager. As the name suggests, the Java EE container manages the lifecycle of a container-managed entity manager. It's probably used most in enterprise Java applications.

You can grab an instance of a container-managed entity manager by using the PersistenceContext injection, as follows:

 @PersistenceContext(unitName="onjava")
  private EntityManager em;

If you use an application-managed entity manager, you must manage its lifecycle. You can create an instance of an application-managed entity manager like this:

  @PersistenceUnit(unitName="onjava")
  private EntityManagerFactory emf;
  private EntityManager em = emf.createEntityManager();

You can then use the EntityManager instance to perform CRUD operation on entities. To close the instance of an application-managed entity manager, invoke the em.close() method once you're done.

As mentioned earlier, an entity manager operation that involves any database changes must be done in a transactional context.

The following table lists some key methods of the EntityManager interface that are used to perform entity operations.

Method

Purpose

public void persist(Object entity);

Persist an entity instance.

public <T> T merge(T entity);

Merge a detached entity instance.

public void remove(Object entity);

Remove an entity instance.

public <T> T find(Class<T> entityClass, Object primaryKey);

Retrieve entity instance by its primary key.

public void flush();

Synchronize entity state with database.

You can use the persist() method to persist an instance of entity. For example, if you want to persist an instance of Contractor, use the following code:

@PersistenceContext(unitName="onjava")
private EntityManager em; 
... 
Contractor pte = new Contractor(); 
pte.setName("Nistha")
pte.setHourlyRate(new Double(100.0)); 
em.persist(pte);

If you persist an entity, any state changes to associated entities will be persisted as well if CascadeType for the relationship is set to PERSIST or ALL. Unless you're using an extended persistent context, the entities will become detached after the end of the transaction. The merge operation lets you merge a detached entity instance with the persistent context; the state of a detached entity will be synchronized with the database. This helps you get rid of the Data Transfer Object (DTO) anti-pattern prevalent in EJB 2.x because entities, being POJOs, can be transferred between tiers. The only requirement is that the entity class must implement the java.io.Serializable interface.

The Query API

Retrieving entities is an important aspect of persistence. When using EJB3 JPA, queries are expressed using Java Persistence Query Language (JPQL). JPQL is an extension of EJBQL, which was introduced as part of the EJB 2.0 specification. EJB3 JPA, however, addresses all the limitations of EJBQL and adds many new features, making it a powerful query language.

JPQL Enhancements over EJBQL 2.x

Here are the new features of JPQL in EJB3 JPA:

In addition, if you want to benefit from database-specific query extensions, you must use native SQL to query entities.

Dynamic vs. Named Queries

You can use either a dynamic or named query. A named query is stored with the entity and can be reused from applications.

To create a dynamic query, use the createQuery method of the entity manager interface, like this:

 Query query = em.createQuery( 
 "select e from Employee e where e.empNo > ?1"); 
 query.setParameter(1,100); 
 return query.getResultList();

If you want to use this query as a named query, use the NamedQuery annotation in the entity, as follows:

@Entity 
@NamedQuery(name="findAllEmployee", 
   query="select e from Employee e where e.empNo > ?1")
 public abstract class Employee implements Serializable { 
 }

To execute a named query, first create a Query instance using the createNamedQuery method on the EntityManager interface, like this:

query = em.createNamedQuery(" findAllEmployee"); 
query.setParameter(1,100); 
return query.getResultList();

Named Parameters

You can use a named parameter in an EJBQL query instead of a positional parameter. For example, you can rewrite the above query as follows:

"select e from Employee e where e.empNo > :empNo "

If you use a named parameter in your query, you must set the parameter as follows:

 query = em.createNamedQuery("findAllEmployee"); 
 query.setParameter("empNo",100); 
 return query.getResultList();

Packaging

EJB3 JPA is standardizing POJO persistence. Thus, entities aren't limited to EJB modules; they can be packaged in a Web module, ejb-jar module, library module in the EAR level, or a standard jar file. You can also use entities in Java SE. You must package a descriptor (persistence.xml), like the following, in the archive that contain entities:

<persistence> 
<persistence-unit name="onjava"> 
<provider>oracle.toplink.essentials.PersistenceProvider</provider>
<jta-data-source>jdbc/OracleDS</jta-data-source> 
...
</persistence-unit> 
</persistence>

This descriptor identifies the persistence provider, persistent units, and the data source used by a persistent unit. As the name suggests, a persistent unit is a collection of entities that are managed together. If you have a single persistent unit defined in a specific module, you don't have to identify the entity classes in the persistence.xml; it will be dynamically discovered by the persistence provider.

TopLink Essentials: The Reference Implementation

TopLink Essentials, which is derived from the leading commercial O-R mapping framework Oracle TopLink, is the reference implementation for EJB3 JPA. It's available in Sun Microsystems' GlassFish open source project at the Java Persistence API implementation home page.

You can use the code in this article with the Reference Implementation Server or any other application server that complies with the EJB3 JPA.

Tools for EJB3 JPA

Development tools really do help you build better applications--and if you're using XML for O-R mapping, things can get pretty hairy. The Eclipse Dali O-R mapping project aims to make EJB3 JPA easier to use by providing comprehensive tools through the Eclipse Web Tool Project. This project is led by Oracle and supported by JBoss, BEA, and Versant. Learn more about Dali at its home page.

Also, tools such as Oracle JDeveloper 10.1.3 and BEA Workshop studio support EJB3 JPA.

Conclusion

The EJB3 Java Persistence API standardizes the persistence API for the Java platform. It simplifies the use of transparent persistence by using metadata annotations and the configuration by exception approach. Several application servers, including Oracle Application Server 10g (10.1.3), Sun's open-source GlassFish Application Server, and JBoss Application Server 4.0, provide early support for the EJB3 specification. With the Java EE 5.0 and EJB 3.0 specifications finalized, you'll soon see many leading application server and persistence providers implementing EJB3 Java Persistence API. You can get a jump-start on EJB3 Persistence by using the Reference Implementation from the GlassFish project.

Resources

Debu Panda is a Senior Principal Product Manager of the Oracle Application Server development team.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.