Chapter 33

Introduction to the Java Persistence API

In the previous lesson you learned about various types of Enterprise Java Beans in which you could program the business logic of your application. Now it’s time to talk about persisting data. If an online store allows users to place orders with session beans, there should be a mechanism for saving the data too. Typically, the data is persisted in DBMS.

The Java Persistence API (JPA) defines a standard way of mapping the Java classes to their relational database peers. This process is also known as object-relational mapping (ORM). One of the popular third-party ORM frameworks, Hibernate, will be introduced in Lesson 36, but this lesson is a brief introduction to the standard JPA 2.0 implemented by any Java EE 6–compliant server.

The Big Picture

In the past, J2EE specifications recommend using Entity EJB to provide all interactions with databases. Currently, entity beans are undergoing a pruning process, and you should use JPA instead to deal with your application’s data querying and persistence. As a matter of fact, JPA 2.0 can be used from Java SE applications too.

JPA enables you to specify and run queries and update data without needing to write SQL statements as you did in Lesson 22 while studying JDBC.

The Java Persistence API enables you to map Java classes to database tables using metadata and perform create, retrieve, update, and delete (CRUD) operations using Java Persistence Query Language (JPQL), the Persistence Criteria API, and native database queries in SQL language. The idea is to create an application-specific domain model as a set of interrelated Java classes and map it to the corresponding data storage (the DBMS).

If a Java class marked with the @Entity annotation has no argument constructor you can call it an entity. Each entity instance corresponds to a row in a database table, as shown here:

@Entity
public class Employee{
 ...
}

If you start with an empty database, JPA tools enable you to create database tables based on Java entities. You can also map Java entities to the existing database tables. Just like database tables, Java entities can have one-to-one relationships (such as an Employee entity with one corresponding OfficeAddress entity), one-to-many relationships (such as one Customer with many Orders), many-to-one relationships (the opposite of one-to-many relationships), and many-to-many relationships (for example, a UniversityClass has many enrolled Students, but each Student can enroll into multiple classes).

Every entity class must define a field containing a unique value, which is the equivalent of a primary key in a database table.

The EntityManager class will deal with objects. Before persisting data you can validate the values using the Bean Validation API.

While JPQL provides string-based SQL-like syntax for working with entities, the newer Criteria API enables you to dynamically construct queries from strongly typed objects.

Mapping Objects to Database Tables

You can map Java classes to database tables via annotations, XML configuration files, or both. For example, common fields can be mapped with annotations, and DBMS-specific mapping can be done in XML files. It does not have to be one-to-one mapping; one Java entity can be mapped to a set of columns from more than one database table.

Besides having fields mapped to table columns, Java entities can have embeddable classes, like Address in the Employee entity.

Typically a database table has a primary key — one or more columns that uniquely identify each row. Java entities must have one or more fields making each instance unique. Such an entity ID can be marked with the @Id annotation, and you can request your JPA provider to auto-generate the ID by adding the annotation @GeneratedValue. Listing 33-1 shows an example of an Employee entity.

download.eps

Listing 33-1: Employee entity

@Entity
public class Employee{
  
  @Id
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  
  @NotNull  
  @Size(max=10)   
  public String firstName; 
 
  @NotNull 
  @Size(min=2, max=20)  
  public String lastName; 
  
  @Column(name="boss_name")
  public String managerName;
 
  @OneToMany (mappedBy = "employee")
  public List<Address> addresses = new ArrayList<Address>();   
 
}

If you don’t specify the table name as a parameter of the annotation, JPA assumes that there is a corresponding database table with the same name as the entity class, which in our example is Employee. The specified strategy GenerationType.IDENTITY means that DBMS has an auto-generated primary key with auto-increment. Many database management systems support either identity columns or sequence objects with similar functionality.

The fields that must have values are marked as @NotNull. If an instance of the preceding Employee entity won’t have values in the firstName or lastName fields, Bean Validation can catch this and generate an error. The entity fields that don’t have to be persisted should be marked with the @Transient annotation.

If a table column name is not the same as the name of the entity field, you can specify the column name using @Column. According to Listing 33-1, the database column name boss_name corresponds to the field managerName of the entity Employee.

Not every Java class that corresponds to some data in the database has to be an entity. You can have embeddable classes that define a group of properties that belong to an entity. Let’s say a company gives to each employee a smartphone identified by a phone number and model number. You can create a Java class to represent such a device and mark it with @Embeddable:

@Embeddable
public class SmartPhone implements Serializable{
   
   @Size(max=10)
   public String phoneNumber;
 
   public String model;
}

Now the Employee entity can embed the property of the SmartPhone type along with other fields:

@Entity
public class Employee{
  
  @Id
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  
  @NotNull   
  public String firstName; 
 
 ...
  @Embedded
  public SmartPhone companyPhone; 
 
}

The code in Listing 33-1 illustrates the mapping of one-to-many relations between the entities Employee and Address (not shown). One employee can have multiple addresses, so Employee references a collection of the Address entities.

JPQL

JPQL is a SQL-like query language. The main difference is that SQL operates with the DBMS schemas, tables, and stored procedures, but JPQL manipulates with Java objects and their attributes from the domain model. You don’t need to know details of the underlying data storage objects to perform JPQL queries.

If you know the queries in advance they can be precompiled; otherwise you can build them dynamically during the run time. Similarly, in JDBC you can use either PreparedStatement or Statement.

JPQL includes pretty easy-to-remember (and case-insensitive) keywords: SELECT, FROM, WHERE, ORDER BY, GROUP BY, and so on. Here’s how you would write a JPQL query to find all managers who have subordinates with the last name Smith:

SELECT e.managerName, 
FROM Employee AS e
WHERE e.lastName='Smith'

The next query finds all employees who were given iPhones by the firm. Note the dot notation to find the phone model from the embedded class.

SELECT e.firstName, e.lastName 
FROM Employee AS e
WHERE e.companyPhone.model='iPhone'

To select all fields of certain entities (the equivalent of Select * in SQL) just specify the alias name of the entity right after the SELECT clause:

SELECT e FROM Employee AS e

The Employee and Address entities have a one-to-many relationship. If you’d like to find all employees who live in New York, this is the join written in JPQL:

SELECT DISTINCT e 
FROM Employee AS e JOIN e.addresses as a
WHERE a.cityl='New York'

EntityManager

EntityManager is the centerpiece of persistence — it will execute all your JPA requests to read from or write into a database. Each instance of EntityManager is associated with a set of entities. Such a set is called a persistence unit. In Java EE containers the EntityManager can be injected as a resource, for example:

@PersistenceContext
EntityManager em;

In Java SE applications you need to instantiate EntityManager programmatically using EntityManagerFactory:

private EntityManagerFactory factory;
 
private static final String PERSISTENCE_CONTEXT_NAME = "employees";
...
factory = Persistence.createEntityManagerFactory(PERSISTENCE_CONTEXT_NAME);
EntityManager em = factory.createEntityManager();

The entity manager can create, update, remove, and find entities by IDs or using a query. The code to find an Employee entity with the ID 1234 can look like this:

Employee employee = em.find(Employee.class, 1234);

To create a new row in the Employee database table, create an instance of the entity Employee and invoke the method persist() on the EntityManager. To delete a row, call remove(). Your application can explicitly begin and commit transactions when the persistence is successfully completed.

@PersistenceContext
EntityManagerFactory factory;
EntityManager em;
 
@Resource
UserTransaction userTransaction;
...
em=factory.createEntityManager();
 
Employee newEmployee = new Employee();
newEmployee.firstName="Mary";
newEmployee.lastName="Thompson";
...
try{
  userTransaction.begin();
  em.persist(newEmployee);
  em.remove(oldEmployee);
 
 userTransaction.commit();
 
 } 
 catch (SystemException e){ //several other exceptions can be thrown here
   e.printStackTrace(); 
   try{
    userTransaction.rollback();
   } catch(SystemException e1){e1.printStackTrace()}
 }

To select the manager name of the employee with the firstName Mary and the lastName Thompson, ask the EntityManager to run the following JPQL query:

EntityManager em;
List employees;
...employees = em.createQuery(
"SELECT e.managerName FROM Employee AS e WHERE e.firstName='Mary' " 
      + " AND e.lastName='Thompson'").getResultList();

This static query works only for employees whose names are Mary Thompson. Note that the method getResultList() is invoked on the created query object. If you expect just one entity as a result, call the method getSingleResult() instead. To specify the first and last names dynamically, you should use parameters, for example:

EntityManager em;
List employees;
 
String firstName = "Mary";
String lastName = "Thompson";
...employees = em.createQuery(
"SELECT e.managerName FROM Employee AS e WHERE " +  
  "e.firstName= :fname AND lastName= :lname")
   .setParameter("lname", lastName)
   .setParameter("fname", firstName)    
   .getResultList();

One instance of EntityManager manages a persistence unit — a set of classes specified in the configuration file persistence.xml, which is located in the META-INF directory of the deployed EJB jar. If you package the application in the war file, this file has to be located either in the directory WEB-INF/classes/META-INF or in a jar under WEB-INF/lib.

The file persistence.xml specifies the name of the jar file that contains managed persistence classes and their names. It also contains the name of the JDBC data source (not the specific JDBC driver) used for communication with DBMS.

<persistence>
    <persistence-unit name="EmployeeManagement">
        <description>This unit manages Acme Employees </description>
        <jta-data-source>jdbc/HRDatabase</jta-data-source>
        <jar-file>MyEmployeeApp.jar</jar-file>
        <class>com.lesson33.Employee</class>
        <class> com.lesson33.Address</class>
    </persistence-unit>
</persistence>

A Brief Introduction to the Criteria API

JPQL is a string-based query language, but the Criteria API allows the creation of strongly typed object-based queries. On one hand it’s more verbose than JPQL, but on the other there is no need to do data-type conversion when processing query results.

These are some core interfaces in the Criteria API:

  • CriteriaBuilder: A utility class that can create criteria queries.
  • CriteriaQuery: This is an object that will contain all parts of the query, such as SELECT, FROM, and WHERE. It’s like a memory graph, in which each node represents some clause of the query.
  • Root: Represents the root of the query.
  • TypedQuery: A query prepared for execution.
  • Join: An object that represents a JOIN clause.

The next code fragment shows an equivalent of the JPQL query SELECT e FROM Employee AS e written using the Criteria API:

EntityManager em;
...
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Employee> crQuery = cb.createQuery(Employee.class);
Root<Employee> employee = crQuery.from(Employee.class);
crQuery.select(employee);
TypedQuery<Employee> tQuery= em.createQuery(crQuery);
List<Employee> employeess = tQuery.getResultList( );

Start with asking EntityManager to create CriteriaBuilder, which in turn creates the instance of CriteriaQuery. Note that via generics, the CriteriaQuery is typed based on the expected results. After that you add instances of required objects (SELECT, FROM, WHERE, and so on) to CriteriaQuery.

Finally, the EntityManager prepares the executable TypedQuery that produces strongly typed results by executing getResultList(). If you expect just one record back, use the getSingleResult() method. Adding several clauses to the query could look like this:

crQuery.select(employee).where(...).orderBy(...);

Because we are building the object graph, the order of the query classes in the preceding line is not important. The Root object can serve as a starting point for joining entities:

Root<Employee> employee = crQuery.from(Employee.class);
Join<Employee, Address> empJoin = employee.join(...);

The next example shows how to add the LIKE clause to get all employees with the last name Thompson:

Root<Employee> employee = crQuery.from(Employee.class);
crQuery.select(employee).where(
           cb.like(employee.<String>)get("lastName"), 
           cb.parameter(String.class, "lname"));
 
TypedQuery<Employee> tQuery= em.createQuery(crQuery);
tQuery.setParameter("lname", "%Thompson%");
List<Employee> employeess = tQuery.getResultList( );

Bean Validation

The Bean Validation framework (JSR-303) is a Java API for ensuring that the values in entities are correct. Validation can be performed at different stages of the entity life cycle. Define the constraints on your entities and the validation can be automatically initiated when you are about to create, update, or remove an entity.

Listing 33-1 includes the @NotNull and @Size constraints defined in the package javax.validation.constraints, but you can create your own validation rules as well.

The life-cycle callback methods marked with the annotations @PrePersist and @PreRemove are invoked on the entity before the EntityManager persists or removes this entity. Accordingly, another pair of annotations, @PostPersist and @PostRemove, are invoked after these operations.

For example, you can put the validation code in the method transferEmployee()to ensure that the transfer has been approved by the employee’s manager. Throwing an exception invalidates the operation.

@PrePersist
public void validateTransfer(){
   if (!transferApproved()){
        throw new TransferException("Manager didn't approve transfer"); 
  }
}

Try It

The GlassFish v3 server includes the binaries of the JPA provider EclipseLink (see www.eclipse.org/eclipselink). In this exercise you’ll be using EclipseLink to auto-generate Java entities based on the existing database. This will serve as an example of object-relational mapping.

Lesson Requirements

You should have Java, the Eclipse Java EE IDE, and GlassFish v3 installed.

note.ai

You can download the code and resources for this Try It from the book’s web page at www.wrox.com. You can find them in the Lesson33 folder in the download.

Step-by-Step

1. Create a new JPA project in Eclipse by selecting File New Other JPA JPA project. Name it Lesson33 and keep the default settings of GlassFish Server Open Source Edition 3 as a target and Minimal JPA 2.0 Configuration as the configuration, as in Figure 33-1.

2. A pop-up suggesting the name of the default output folder displays. Don’t change anything there; click Next.

3. The JPA Facet window (see Figure 33-2) will enable you to select the JPA provider — the libraries that implement JPA specification.

The User Library box will initially be empty. Press the little preferences icon next to it and then click New in the Preferences window. You’ll need to add the following six jars from the folder glassfishv3/glassfish/modules to your new user library:

org.eclipse.persistence.antlr.jar

org.eclipse.persistence.jpa.jar

org.eclipse.persistence.asm.jar

org.eclipse.persistence.jpa.modelgen.jar

org.eclipse.persistence.core.jar

javax.persistence.jar

4. Start your Derby database server using the startNetworkServer command, as explained in Lesson 22.

5. In the connection drop-down (refer to Figure 33-2) select Sample JavaDB Database and click the Connect link. You’ll see that Eclipse has opened the Data Source Explorer view showing the sample database that comes with Derby.

6. Right-click Sample JavaDB Database in the Database Connection view. Configure your new connection in the popup window (see Figure 33-3) to the database Lesson22 that you created in Lesson 22. Click Test Connection to ensure you did it right.

7. Figure 33-4 is a snapshot of Eclipse view Data Source Explorer, which shows you the table Employee from Lesson 22. It’s located in the APP schema.

8. JPA requires database tables to have primary keys, but our table Employee didn’t define one. You’ll need to fix this by making the column empno a primary key. You can do it from the command line via the ij utility (see Lesson 22) by issuing the following command:

alter table APP.Employee add primary key (empno); 

9. Right-click the name of project Lesson33 and select JPA Tools Generate Entities from Tables. In the pop-up window select the table Employee and click Finish.

10. Open the folder src in your Eclipse project and you’ll find there a freshly generated class called Employee that will look as follows:

import java.io.Serializable;
import javax.persistence.*;
/**
 * The persistent class for the EMPLOYEE database table.
 * 
 */
 
@Entity
public class Employee implements Serializable {
      private static final long serialVersionUID = 1L;
 
      @Id
      private int empno;
 
      private String ename;
 
 @Column(name="JOB_TITLE")
 private String jobTitle;
 
 public Employee() {}
 
 public int getEmpno() {
   return this.empno;
 }
 
 public void setEmpno(int empno) {
   this.empno = empno;
}
 
 public String getEname() {
   return this.ename;
 }
 
 public void setEname(String ename) {
   this.ename = ename;
}
 
 public String getJobTitle() {
   return this.jobTitle;
}
 
 public void setJobTitle(String jobTitle) {
   this.jobTitle = jobTitle;
 }
}

11. Finally, open the folder META-INF and you’ll find generated there the file persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" 
xmlns="http://java.sun.com/xml/ns/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">
  
  <persistence-unit name="Lesson33">
    <class>Employee</class>
  </persistence-unit>
 
</persistence>
cd.ai

Please select Lesson 33 on the DVD with the print book, or watch online at www.wrox.com/go/fainjava to view the video that accompanies this lesson.

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

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