RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Master the New Persistence Paradigm with JPA

In this article you will learn how to persist objects using the Java Persistence API (JPA), customize their mapping with annotations, and create a one-to-one relationship. Using the entity lifecycle you will control persistency and use the query language (JPQL) to query your objects.

bject/relational mapping (ORM)—in other words, persisting Java objects to a relational database—has become a major topic recently, thanks, in part, to a proliferation of advanced methods that attempt to make it easier. Among these various technologies are Entity Beans 2.x, TopLink, Hibernate, JDO, and even JDBC with DAO. Faced with so many incompatible choices, the Java EE expert group took inspiration from these popular frameworks and created the Java Persistence API (JPA), which is suitable for use with Java EE or SE applications.

In a nutshell, the JPA's aim is to bring back the POJO programming model to persistence. Despite the fact that the specification is still bundled with EJB 3, JPA can be used outside the EJB container, and that's the way this article will use it: Plain Old Java Objects running in a Java SE application.

In this article I will model a simple address book application for a fictional music company called Watermelon, to store customers' home addresses in a database. Watermelon sells and delivers music articles such as instruments, amplifiers, scores, books and so on. I will use an incremental and iterative approach to develop and persist the business model (see Figure 1).

Author's Note: A follow-up to this article is now available. Please see "Persistence Pays Off: Advanced Mapping with JPA".

What You Need
To compile and execute the code that comes with this article you need the JDK 1.5 (http://java.sun.com/javase/downloads/index_jdk5.jsp) and Ant 1.7 (http://ant.apache.org/bindownload.cgi). Test classes are written in JUnit 4.1 (http://www.junit.org/index.htm). This article uses MySQL 5 (http://dev.mysql.com/downloads/mysql/5.0.html#downloads) and TopLink Essentials (http://www.oracle.com/technology/products/ias/toplink/jpa/download.html) as the implementation of JPA. Before running the code, you need to create a database called watermelonDB in MySQL with the root user and no password.

Figure 1. A class diagram for the Watermelon business model is shown.
How Does JPA Work ?
Inspired from ORM frameworks such as Hibernate, JPA uses annotations to map objects to a relational database. These objects, often called entities, have nothing in common with Entity Beans 2.x. JPA entities are POJOs that do not extend any class nor implement any interface. You don't even need XML descriptors for your mapping. If you look at the API you will see that JPA is made up of only a few classes and interfaces. Most of the content of the javax.persistence package is annotations. With that, let's look at some code.

public class Customer {

  private Long id;
  private String firstname;
  private String lastname;
  private String telephone;
  private String email;
  private Integer age;
  // constuctors, getters, setters
This bit of code (constructors, getters, and setters are not shown to make it easier to read) shows a simple Customer class. It has an identifier, a first name, last name, a telephone number, an email address and the age of the customer. To be persistent, this class has to follow some simple JPA rules:
  • It must be identified as an entity using the @javax.persistence.Entity annotation.
  • It must have an identifier attribute annotated with @javax.persistence.Id.
  • It must have a no argument constructor.
  • The code above shows you the minimum required to define a persistent object. Let's now manipulate this object. I want to persist my customer, update some of its attributes, and delete it from the database. These CRUD operations are made through the javax.persistence.EntityManager interface of JPA. For those of you who are familiar with the DAO pattern, the EntityManager can be seen as a DAO class providing you with a set of life cycle methods (persist, remove) and finders (find) (see Listing 1).

    After creating an EntityManager using a factory (EntityManagerFactory), I instantiate my Customer object (using the new operator like any other Java object) and pass to it some data such as the id, the last name, the email address, the age, and so on. I use the EntityManager.persist() method to insert this object into the database. I can then find this object by its identifier using the EntityManager.find() method and update the email address by using set methods. The EntityManager doesn't have an update method per se. Updates are made through setters. Next I delete the object using EntityManager.remove(). Note that this code uses explicit transactions. That's why persist, update, and remove methods are surrounded by transaction.begin() and transaction.commit().

    Author's Note: There are some differences when using the EntityManager in a Java EE application. You don't create it using the factory (EntityManagerFactory). Instead you inject the persistence unit to the EntityManager using the @PersistenceContext(unitName = "watermelonPU") annotation. In Java EE you can also avoid explicit transactions (begin, commit, rollback) and let the container deal with them.

    There is a piece of information missing in Listing 1: which database to use? The answer is in the EntityManagerFactory. It takes a parameter that refers to a specific persistence unit (watermelonPU in this case). Persistence units are declared in the persistence.xml file and contain information such as the database to use and the JDBC driver using some implementation-specific properties.

    <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence">
      <persistence-unit name="watermelonPU" transaction-type="RESOURCE_LOCAL">
          <property name="toplink.jdbc.url" 
          <property name="toplink.jdbc.user" value="root"/>
          <property name="toplink.jdbc.driver" value="com.mysql.jdbc.Driver"/>
          <property name="toplink.jdbc.password" value=""/>
          <property name="toplink.target-database" value="MySQL4"/>
          <property name="toplink.ddl-generation" value="create-tables"/>
    In the code above there is only one persistence unit, named watermelonPU (persistence.xml can contain several persistence units). You can see a persistence unit as a collection of entities (the class element) that share common properties. In this case, it refers to the watermelonDB database URL, JDBC driver, and credentials. Under the properties element you will find TopLink-specific properties such as toplink.ddl-generation. This property is used by TopLink to automatically generate the tables if they don't already exist. This means that once I've executed Listing 1, TopLink has created a table to store the customer information. Table 1 shows the DDL (Data Definition Language) of the customer table.

    Author's Note: As its name indicates, JPA is just an API that needs to be implemented by a persistence provider. For this article I used TopLink but there are others available such as Hibernate 3.2, OpenJPA, or Kodo. I also used MySQL but TopLink supports other databases such as Oracle, Sybase, and DB2.

    Table 1. DDL for the Customer Table.

    Field Type Null Key
    ID bigint(20) NO PRI
    LASTNAME varchar(255) YES  
    TELEPHONE varchar(255) YES  
    FIRSTNAME varchar(255) YES  
    EMAIL varchar(255) YES  
    AGE int(11) YES  

    This is the DDL that JPA automatically generated from the annotated Customer class. Thanks to the coding-by-exception approach that guides JPA (and Java EE 5 in general) I didn't have to do too much to get this DDL. Coding-by-exception makes life easier for the developer: You only need to add custom code when the default is inadequate. In my case, because I didn't specify the table or column name on the Customer class, JPA assumes that the name of the table is equal to the name of the class, and that the columns have the same names as the attributes. Data types are also mapped to defaults (eg. String is mapped to varchar(255)).

    Close Icon
    Thanks for your registration, follow us on our social networks to keep up-to-date