Chapter 4. Interacting with Databases through the Java Persistence API

The Java Persistence API (JPA) is an object-relational mapping (ORM) API. ORM tools help us to automate the mapping of Java objects to relational database tables. Earlier versions of J2EE used Entity Beans as the standard approach for ORM. Entity Beans attempted to always keep the data in memory synchronized with the database data, a good idea in theory, however, in practice this feature resulted in poorly performing applications.

Several ORM APIs were developed to overcome the limitations of Entity Beans, such as Hibernate, iBatis, Cayenne, and TopLink, among others.

Java EE 5 deprecated Entity Beans in favor of JPA. JPA took ideas from several ORM tools and incorporated them in the standard. As we will see in this chapter, NetBeans has several features that make development with JPA a breeze.

The following topics will be covered in this chapter:

  • Creating our first JPA entity
  • Interacting with JPA entities using EntityManager
  • Generating JPA entities from an existing database schema
  • JPA named queries and Java Persistence Query Language (JPQL)
  • Entity relationships
  • Generating complete JSF applications from JPA entities

Creating our first JPA entity

JPA entities are Java classes whose fields are persisted to a database by the JPA API. These Java classes are Plain Old Java Objects (POJOs), and as such, they don't need to extend any specific parent class or implement any specific interface. A Java class is designated as a JPA entity by decorating it with the @Entity annotation.

In order to create and test our first JPA entity, we will create a new web application using the JavaServer Faces framework. In this example, we will name our application jpaweb, and (as with all of our examples) we will use the bundled GlassFish application server.

Tip

Refer to Chapter 2, Developing Web Applications Using JavaServer Faces 2.2, for instructions on creating a new JSF project.

To create a new JPA entity, select the Persistence category from the new file dialog and select Entity Class as the file type.

Creating our first JPA entity

After doing so, NetBeans presents the New Entity Class wizard.

Creating our first JPA entity

At this point, we should specify the values for the Class Name and Package fields (Customer and com.ensode.jpaweb in our example).

Projects using JPA require a persistence unit. This persistence unit is defined in a file called persistence.xml. When we create our first JPA entity for the project, NetBeans detects that no persistence.xml exists and automatically checks the checkbox labeled Create Persistence Unit. The next step in the wizard allows us to enter the information necessary to create the persistence unit. This is shown in the following screenshot:

Creating our first JPA entity

The Provider and Database wizard will suggest a name for our persistence unit; in most cases the default can be safely accepted.

JPA is a specification for which several implementations exist. NetBeans supports several JPA implementations, including EclipseLink, TopLink Essentials, Hibernate, KODO, and OpenJPA. Since the bundled GlassFish application server includes EclipseLink as its default JPA implementation, it makes sense to take this default value for the Persistence Provider field when deploying our application to GlassFish.

Before we can interact with a database from any Java EE application, a database connection pool and data source need to be created in the application server.

A database connection pool contains connection information that allows us to connect to our database, such as the server name, port, and credentials. The advantage of using a connection pool instead of directly opening a JDBC connection to a database is that database connections in a connection pool are never closed, they are simply allocated to applications as they need them. This improves performance, since the operations of opening and closing database connections are expensive in terms of performance.

Data sources allow us to obtain a connection from a connection pool, and then invoke its getConnection() method to obtain a database connection from a connection pool. When dealing with JPA, we don't need to directly obtain a reference to a data source, it is all done automatically by the JPA API. However, we still need to indicate the data source to use in the application's persistence unit.

NetBeans comes with a few data sources and connection pools preconfigured. We can use one of these preconfigured resources for our application. However, NetBeans also allows us to create these resources "on the fly", which is what we will be doing in our example.

To create a new data source, we need to select the New Data Source... item from the Data Source combobox.

Creating our first JPA entity

A data source needs to interact with a database connection pool. NetBeans comes preconfigured with a few connection pools out of the box. However, as with data sources, it allows us to create a new connection pool "on demand". In order to do this, we need to select the New Database Connection... item from the Database Connection combobox.

Creating our first JPA entity

NetBeans includes JDBC drivers for a few relational database management systems (RDBMS) such as JavaDB, MySQL, PostgreSQL out of the box. JavaDB is bundled with both GlassFish and NetBeans; therefore, we selected JavaDB for our example to avoid installing an external RDBMS.

Note

For RDBMS systems that are not supported out of the box, we need to obtain a JDBC driver and let NetBeans know of its location by selecting New Driver from the Driver Name combobox. Then, we need to navigate to the location of the JAR file that contains the JDBC driver. Consult your RDBMS documentation for details.

Click on Next > to enter your connection details as shown in the following screenshot:

Creating our first JPA entity

JavaDB is installed in our workstation. Therefore, use localhost as the server name. By default, JavaDB listens to port 1527, so that is the port we specify in the URL. We wish to connect to a database called jpaintro, so we specify it as the database name.

Every JavaDB database contains a schema named APP, since by default each user uses a schema named after his/her own login name. The easiest way to get going is to create a user named APP and selecting a password for this user.

Since the jpaintro database does not exist yet, we need to create it. We can do this by clicking on Connection Properties and entering a property named create with a value true.

Creating our first JPA entity

In the next step in the wizard, we need to select a schema to use for our application. The APP schema is the one typically used by applications using JavaDB as their RDBMS.

Creating our first JPA entity

In the next step, NetBeans asks us to enter a descriptive name for our connection.

Creating our first JPA entity

We can choose to do so or simply accept the default connection name. Once we have created our new data source and connection pool, we can continue configuring our persistence unit.

Creating our first JPA entity

It is a good idea to leave the Use Java Transaction APIs checkbox checked. This will instruct our JPA implementation to use the Java Transaction API (JTA) to allow the application server to manage transactions. If we uncheck this box, we will need to manually write code to manage transactions.

Most JPA implementations allow us to define a table generation strategy. We can instruct our JPA implementation to create tables for our entities when we deploy our application, to drop the tables and then regenerate them when our application is deployed, or to not create any tables at all. NetBeans allows us to specify the table generation strategy for our application by clicking on the appropriate value in the Table Generation Strategy radio button group.

Tip

When working with a new application, it is a good idea to select the Drop and Create table generation strategy in the wizard. This will allow us to add, remove, and rename fields in our JPA entity at will, without having to make the same changes in the database schema. When selecting this table generation strategy, tables in the database schema will be dropped and recreated every time we deploy our application. Therefore, any data previously persisted will be lost.

Once we have created our new data source, database connection, and persistence unit, we are ready to create our new JPA entity.

We can do so by simply clicking on the Finish button. At this point, NetBeans generates the source for our JPA entity.

Note

JPA allows the primary field of a JPA entity to map to any column type (VARCHAR, NUMBER, and so on). It is a best practice to have a numeric surrogate primary key, that is, a primary key that serves only as an identifier and has no business meaning in the application. Selecting the default Primary Key Type of Long will allow a wide range of values to be available for the primary keys of our entities.

The Customer class has some important things to consider, as highlighted in the following code:

package com.ensode.jpaweb;

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Customer implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    //Other generated methods (equals(), hashCode(), toString()) omitted for brevity
  }

As we can see, a JPA entity is a standard Java object. There is no need to extend any special class or implement any special interface. What differentiates a JPA entity from other Java objects are a few JPA-specific annotations.

The @Entity annotation is used to indicate that our class is a JPA entity. Any object we want to persist to a database via JPA must be annotated with this annotation.

The @Id annotation is used to indicate what field in our JPA entity is its primary key. The primary key is a unique identifier for our entity. No two entities may have the same value for their primary key field. This annotation can be used on the field that serves as a primary key; this is the strategy that the NetBeans wizard uses. It is also correct to annotate the getter method for the entity's primary key field.

The @Entity and the @Id annotations are the bare minimum two annotations that a class needs in order to be considered a JPA entity. JPA allows primary keys to be automatically generated; in order to take advantage of this functionality, the @GeneratedValue annotation can be used. As we can see, the NetBeans-generated JPA entity uses this annotation. This annotation is used to indicate the strategy to use to generate primary keys. All possible primary key generation strategies are listed in the following table:

Primary key generation strategy

Description

GenerationType.AUTO

This indicates that the persistence provider will automatically select a primary key generation strategy. This is used by default if no primary key generation strategy is specified.

GenerationType.IDENTITY

This indicates that an identity column in the database table the JPA entity maps to must be used to generate the primary key value.

GenerationType.SEQUENCE

This indicates that a database sequence should be used to generate the entity's primary key value.

GenerationType.TABLE

This indicates that a database table should be used to generate the entity's primary key value.

In most cases, the GenerationType.AUTO strategy works properly, so it is almost always used. For this reason, the New Entity Class wizard uses this strategy.

Note

When using the sequence or table generation strategies, we might have to indicate the sequence or table used to generate the primary keys. These can be specified by using the @SequenceGenerator and @TableGenerator annotations, respectively. Refer to the Java EE 7 JavaDoc at http://download.oracle.com/javaee/7/api/ for details.

Adding persistent fields to our entity

At this point, our JPA entity contains a single field: its primary key. This is admittedly not very useful. We need to add a few fields to be persisted to the database, as shown in the following code:

package com.ensode.jpaweb;

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Customer implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String firstName;
    private String lastName;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }   
}

In this modified version of our JPA entity, we added two fields to be persisted to the database: firstName and lastName, which will be used to store the user's first name and last name respectively. JPA entities need to follow standard JavaBean coding conventions. This means that they must have a public constructor that takes no arguments (one is automatically generated by the Java compiler if we don't specify any other constructors), and all fields must be private and accessed via public getter and setter methods.

Tip

Automatically generating getters and setters

In NetBeans, getter and setter methods can be generated automatically: simply declare new fields as usual, use the keyboard shortcut Alt + Insert, select Getter and Setter from the resulting pop-up window, click on the checkbox next to the class name to select all fields, and click on the Generate button.

Before we can use JPA to persist our entity's fields into our database, we need to write some additional code.

Creating a data access object

It is a good idea to follow the data access object (DAO) design pattern whenever we write code that interacts with a database. The DAO design pattern keeps all database access functionality in DAO classes. This creates a clear separation of concerns, leaving other layers in our application, such as the user interface logic and the business logic, free of any persistence logic.

NetBeans can help us generate JPA controller classes from existing entities. These JPA controller classes follow the DAO design pattern. To generate a JPA controller class, we simply need to go to File | New, select the Persistence category, and select the JPA Controller Classes from Entity Classes file type from the New File dialog.

Creating a data access object

In the next step in the wizard, we need to select the entity classes we wish to generate JPA controller classes for.

Creating a data access object

Then, we need to specify the project and package for our JPA controller classes.

Creating a data access object

After clicking on Finish, our JPA controller class is successfully generated, as shown here:

package com.ensode.jpaweb;
//imports omitted
public class CustomerJpaController implements Serializable {

    public CustomerJpaController(UserTransaction utx,
            EntityManagerFactory emf) {
        this.utx = utx;
        this.emf = emf;
    }
    private UserTransaction utx = null;
    private EntityManagerFactory emf = null;

    public EntityManager getEntityManager() {
        return emf.createEntityManager();
    }
    
    public void create(Customer customer) throws
            RollbackFailureException, Exception {
        EntityManager em = null;
        try {
            utx.begin();
            em = getEntityManager();
      em.persist(customer);
            utx.commit();
        } catch (Exception ex) {
            try {
                utx.rollback();
            } catch (Exception re) {
                throw new RollbackFailureException(
                        "An error occurred attempting to roll back the transaction.",
                        re);
            }
            throw ex;
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public void edit(Customer customer) throws
      NonexistentEntityException, RollbackFailureException, Exception {
        EntityManager em = null;
        try {
            utx.begin();
            em = getEntityManager();
     customer = em.merge(customer);
            utx.commit();
        } catch (Exception ex) {
            try {
                utx.rollback();
            } catch (Exception re) {
                throw new RollbackFailureException(
                        "An error occurred attempting to roll back the transaction.",
                        re);
            }
            String msg = ex.getLocalizedMessage();
            if (msg == null || msg.length() == 0) {
                Long id = customer.getId();
                if (findCustomer(id) == null) {
                    throw new NonexistentEntityException(
                            "The customer with id " + id
                            + " no longer exists.");
                }
            }
            throw ex;
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public void destroy(Long id) throws NonexistentEntityException,
      RollbackFailureException, Exception {
        EntityManager em = null;
        try {
            utx.begin();
            em = getEntityManager();
            Customer customer;
            try {
                customer = em.getReference(Customer.class, id);
                customer.getId();
            } catch (EntityNotFoundException enfe) {
                throw new NonexistentEntityException(
                        "The customer with id " + id
                        + " no longer exists.", enfe);
            }
     em.remove(customer);
            utx.commit();
        } catch (Exception ex) {
            try {
                utx.rollback();
            } catch (Exception re) {
                throw new RollbackFailureException(
                        "An error occurred attempting to roll back the transaction.",
                        re);
            }
            throw ex;
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public List<Customer> findCustomerEntities() {
        return findCustomerEntities(true, -1, -1);
    }

    public List<Customer> findCustomerEntities(int maxResults,
            int firstResult) {
        return findCustomerEntities(false, maxResults, firstResult);
    }

    private List<Customer> findCustomerEntities(boolean all, int maxResults,
     int firstResult) {
        EntityManager em = getEntityManager();
        try {
     CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
     cq.select(cq.from(Customer.class));
     Query q = em.createQuery(cq);
            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }
     return q.getResultList();
        } finally {
            em.close();
        }
    }

    public Customer findCustomer(Long id) {
        EntityManager em = getEntityManager();
        try {
     return em.find(Customer.class, id);
        } finally {
            em.close();
        }
    }

    public int getCustomerCount() {
        EntityManager em = getEntityManager();
        try {
     CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
     Root<Customer> rt = cq.from(Customer.class);
     cq.select(em.getCriteriaBuilder().count(rt));
     Query q = em.createQuery(cq);
     return ((Long) q.getSingleResult()).intValue();
        } finally {
            em.close();
        }
    }

}

As we can see, NetBeans generates methods to create, read, update, and delete JPA entities.

The method to create a new entity is called create(), and it takes an instance of our JPA entity as its sole argument. This method simply invokes the persist() method on EntityManager, which takes care of persisting the data on the JPA entity to the database.

For read operation, several methods are generated. The findCustomer() method takes the primary key of the JPA entity we wish to retrieve as its sole parameter, invokes the find() method on EntityManager to retrieve the data from the database, and returns an instance of our JPA entity. Several overloaded versions of the findCustomerEntities() method are generated, and these methods allow us to retrieve more than one JPA entity from the database. The version of this method that does all the real work is the one that contains the following signature:

private List<Customer> findCustomerEntities(boolean all, int maxResults,
    int  firstResult)

The first parameter is a Boolean, which we can use to indicate if we want to retrieve all values in the database; the second parameter allows us to specify the maximum number of results we wish to retrieve; the last parameter allows us to indicate the first result we wish to retrieve. This method uses the Criteria API that was introduced in JPA 2.0 to build a query programmatically. If the value of the all parameter is false, then this method sets the maximum number of results and the first result by passing the appropriate parameters to the setMaxResults() and setFirstResult() methods in the Query object.

The edit() method is used to update existing entities. It takes an instance of our JPA entity as its sole parameter. This method invokes the merge() method on EntityManager, which updates the data in the database with the data in the JPA entity it receives as a parameter.

The destroy() method is used to delete an entity. It takes the primary key of the object to be deleted as its sole parameter. It first checks to see if the object exists in the database. If it doesn't exist, this method throws an exception, otherwise it deletes the corresponding row from the database by invoking the remove() method on EntityManager.

At this point, we have all the code we need to persist our entity's properties in the database, and all we need to do to perform CRUD (short for Create, Read, Update, and Delete) operations involving our JPA entity is invoke these methods on the generated JPA controller from our code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.17.40