Chapter 5. Developing JPA Entities

Ambition is the path to success. Persistence is the vehicle you arrive in. Bill Bradley (a politician).

The EJB 3.0 specification includes a persistence specification called the Java Persistence API (JPA). It is an API for creating, removing, and querying Java objects called entities that can be used within both, a compliant EJB 3.0 container and a standard Java SE 5 environment.

In this chapter, we introduce the AppStore application, which will be a central theme of this book. The application includes a persistence layer designed around JPA, a session bean facade, and a frontend delivered with JavaServer Faces(JSF) web framework (we will see this in the next chapter).

In this chapter, we will discuss the following topics in more detail:

  • The key elements of JPA
  • How to create entities starting from database tables
  • How to manipulate the entities with a session bean
  • Delivering a standalone client for testing the application

Data persistence meets a standard

The arrival of an Enterprise Java Persistence standard, based on a "POJO" development model, fills a substantial gap in the Java EE platform. The previous attempt of EJB 2.x specification missed the mark and created the stereotype of EJB entity beans as awkward to develop and too heavy for many applications. Therefore, it never reached the level of widespread adoption or general approval in many sectors of the industry.

Software developers knew what they wanted, but many could not find it in the existing standards, so they decided to look elsewhere. What they found was proprietary persistence frameworks, both in the commercial and open source domains.

In contrast to EJB 2.x entity beans, the EJB 3.0 Java Persistence API is a metadata-driven POJO technology. That is, to save data held in Java objects into a database, our objects are not required to implement an interface, extend a class, or fit into a framework pattern.

Another key feature of JPA is the query language, called the Java Persistence Query Language(JPQL) that gives you a way to specify the semantics of queries in a portable way, independent of the particular database you are using in an enterprise environment. JPA queries resemble SQL queries in syntax, but operate against entity objects rather than directly with database tables.

Working with JPA

Inspired by object-relational mapping (ORM) frameworks, such as Hibernate, JPA uses annotations to map objects to a relational database. JPA entities are POJOs that neither extend a class nor implement an interface. You don't even need XML descriptors for your mapping. Actually, the Java Persistence API is made up of annotations and only a few classes and interfaces. For example, we would mark the class Company as entity, as follows:

@Entity
public class Company {
public Company () { }
@Id
String companyName;
}

The last code snippet shows the minimal requirements for a class to be persistent, which are:

  • It must be identified as an entity using the @javax.persistence.Entity annotation
  • It must have an identifier attribute with the @javax.persistence.Id annotation
  • It must have a no-argument constructor

I guess you would learn better from an example, so in the next section we will show how to create and deploy a sample JPA application on JBoss 5.

Creating a sample application

Our sample application will be a small store application, which tracks orders from a list of customers. The application will be developed using MySQL database, which is freely downloadable from http://dev.mysql.com/downloads/.

We suggest that you download MySQL 5.x, as well as MySQL Connector/J, which is used for Java Database Connectivity (JDBC). Once the download is complete, extract the file mysql-connector-java.jar and place it in the JBOSS_HOME/common/lib, thus making it available to all your server configurations.

Setting up the database

We will create a database named appstore, and then we will add a user named jboss assigning it all privileges on the schemas, as shown in the following code snippet:

CREATE DATABASE appstore;
USE appstore;
CREATE USER 'jboss'@'localhost' IDENTIFIED BY 'jboss';
GRANT ALL PRIVILEGES ON appstore.* TO 'jboss'@'localhost' WITH GRANT OPTION;

Our simple schema will be made up of two tables—the Customer table that contains the list of all customers and the Item table that holds the list of all orders. The two tables are in a 1-n relationship and the Item table hosts a Foreign key (customer_id) that relates to the id of the Customer table.

Setting up the database
CREATE TABLE appstore.customer (
`ID` int(10) unsigned NOT NULL auto_increment,
`NAME` varchar(45) NOT NULL,
`COUNTRY` varchar(45) NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB;
CREATE TABLE appstore.item (
`ID` int(10) unsigned NOT NULL auto_increment,
`PRODUCT` varchar(45) default NULL,
`PRICE` int(11) default NULL,
`QUANTITY` int(11) default NULL,
`CUSTOMER_ID` int(10) unsigned NOT NULL,
PRIMARY KEY (`ID`),
KEY `FK_orders` (`CUSTOMER_ID`),
CONSTRAINT `FK_orders` FOREIGN KEY (`CUSTOMER_ID`) REFERENCES `customer` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;

Rolling the EJB project

Once we are done with the data layer, we will create our entities. A preamble is necessary before we begin. Depending on the release of your Eclipse and JBoss Tools, you can model your project in several ways. In this example, we will create our entities using a wizard, so you will not need to write mapping classes manually. However, if you don't have the latest JBoss Tools installation, you still have other options available. For example, you can use Hibernate Tools to automatically generate mapping classes (this will be covered in Chapter 8,Developing Applications with JBoss and Hibernate).

Here we go. This project will contain both session beans and entities. Therefore, the simplest way to start is by creating a new EJB project and adding a JPA nature to it.

From the File menu, select New | Other | EJB | EJB Project, and name it AppStore—an application for handling a store. (I have to admit sometimes IT guys don't have much imagination!)

Now we will add JPA capabilities to our project. Right-click on the project and select Properties. The property we are interested in is Project Facets, which is the collection of capabilities added to our project. Select Java Persistence, as indicated in the following screenshot, and click OK.

Rolling the EJB project

Now your AppStore has got some new interesting options. Let's see where you can find them. Move to the root of your project and right-click on AppStore. Select New | Entities From Tables (see the following screenshot):

Rolling the EJB project

The JPA wizard will start. The process of reverse engineering the database into Java entities can be roughly divided into two steps as follows:

  1. Configuring the database connection: This step involves the definition of a database connection that needs to be performed just the first time you use the JPA wizard.
  2. Generating entities: This second step is the actual process of reverse engineering.

These steps are discussed in more detail as follows:

Configuring the database connection

As we don't have any configured connection, you have to create a new one by clicking on the shiny little button at the top-right corner of your wizard, which is shown in the following screenshot:

Configuring the database connection

In the next window, you have to select the database that contains your planned entities. Select MySQL (or whichever database you are using) from the next wizard and choose an appropriate label for our connection; here we select MySQL Connection.

Configuring the database connection

Hit Next. A new window will let you specify a driver and connection details. As we do not have any JDBC driver configured yet, you should first click on the little icon (+) in the top-right corner.

Configuring the database connection

The New Driver Definition dialog appears. First, select the driver template from the list of available options; in our case select MySQL 5.0. Then in the Jar List tab, point to the MySQL connector, which we downloaded earlier.

Configuring the database connection

Okay, we are almost done. The last thing you need to select is the JDBC Driver properties in the New Connection Profile, which should match with your appstore database.

Configuring the database connection

Before clicking Finish, we suggest you verify the connection with the Test Connection button. If for some reason the test fails, verify the General Properties and, of course, that the database is up and running.

Generating entities

Once the connection configuration is complete, it's time to roll your entities. You will be taken back to the first JPA wizard window, where you can now select the Schema as appstore and the Tables as customer and item:

Generating entities

Selecting Synchronize classes listed in persistence.xml will insert the entity definition in the main JPA configuration file—persistence.xml. This is not a mandatory step; however, Eclipse will complain later if the entities are not synchronized with the configuration file.

Click Next. In the subsequent wizard you have to deal with Table Associations. As you can see in the following screenshot, the JPA facet correctly recognizes the 1-n association between customer and item. In terms of entities, this will mean that the Customer entity will contain a list of Item entities ordered and the Item class will contain a reference to the Customer.

Click on the association diagram and choose names for the entity fields that will describe the relation.

Generating entities

In the last screenshot, customerFK will be a reference to the Customer (in the Item class) and items will hold the list of items ordered by a single customer. You are just one step away from finishing. Click Next. In the next wizard, you have to customize your entities.

The options that override the defaults are framed in the following screenshot:

Generating entities

First, we want our entities to use an Eager fetching strategy. What does this mean? By default, when we have an association between two entities, the referenced objects are lazy loaded, that is, fetching and loading the data from the persistent storage is done only when it is needed. In contrast to lazy loading, eager loading loads the full object tree at once. Lazy loading contributes a lot to improving the performance of an application, by limiting the number of objects that will be needed. However, if you need to traverse the tree of objects from the client side (as in our example), you will need to use the eager fetching strategy.

Then, select the class java.util.List as Collection properties type, which will be just fine for returning a vector of items and customers.

Lastly, choose the target package for the entities, that is, com.packtpub.jpa.example3. If you want to preview the entities that are going to be generated, have a look at the next windows. Otherwise, you can complete the JPA wizard by clicking on the Finish button.

Reverse engineering aftermath

From the Project Explorer window, let's explore what the wizard has created for you:

Reverse engineering aftermath

If you have successfully completed all the wizard steps, your entities will be correctly packaged in the com.packtpub.jpa.example3 folder.

Let's have a look at the Customer entity:

package com.packtpub.jpa.example3;
EJB projectcustomer entityimport java.io.Serializable;
import javax.persistence.*;
import java.util.List;
import static javax.persistence.FetchType.EAGER;
@Entity [1]
@Table(name="customer") [2]
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
@Id [3]
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="ID") [4]
private int id;
@Column(name="COUNTRY")
private String country;
@Column(name="NAME")
private String name;
//bi-directional many-to-one association to Item
@OneToMany(mappedBy="customerFK", fetch = EAGER) [5]
private List<Item> items;
public Customer() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public String getCountry() {
return this.country;
}
public void setCountry(String country) {
this.country = country;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public List<Item> getItems() {
return this.items;
}
public void setItems(List<Item> items) {
this.items = items;
}
}

The first meaningful annotation is @Entity [1] that declares the class as an entity. The @Table [2] annotation is used to map the bean class with a database table.

The @Id annotation [3] is mandatory; it describes the primary key of the table. Along with @Id, there's the @GeneratedValue annotation. This is used to declare that the database is in charge of generating the value.

Moving along, the @Column [4] annotation is used to map the Java field with the corresponding database column. You can leave out this annotation, if the two elements are equal.

The @OneToMany annotation [5] defines an association with one-to-many multiplicity. Actually, the Customer class has many orders. The corresponding orders are contained in a List collection.

In the JPA wizard, we have chosen the EAGER attribute to the @OneToMany annotation so that all orders are populated at the same time when we issue a query on the Customer entity.

At this point we have inspected the Customer entity. Let's have a look at the Item entity:

package com.packtpub.jpa.example3;
EJB projectitem entityimport java.io.Serializable;
import javax.persistence.*;
@Entity
@Table(name="item")
public class Item implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="ID")
private int id;
@Column(name="PRICE")
private int price;
@Column(name="PRODUCT")
private String product;
@Column(name="QUANTITY")
private int quantity;
//bi-directional many-to-one association to Customer
@ManyToOne [1]
@JoinColumn(name="CUSTOMER_ID") [2]
private Customer customerFK;
public Item() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public int getPrice() {
return this.price;
}
public void setPrice(int price) {
this.price = price;
}
public String getProduct() {
return this.product;
}
public void setProduct(String product) {
this.product = product;
}
public int getQuantity() {
return this.quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public Customer getCustomerFK() {
return this.customerFK;
}
public void setCustomerFK(Customer customerFK) {
this.customerFK = customerFK;
}
}

As you can see, the Item entity has the corresponding @ManyToOne [1] annotation, which naturally complements the @OneToMany relationship. The @JoinColumn [2], which has the same syntax of the @Column annotation, notifies the JPA engine that the customerFK field is mapped through the foreign key of the database customer_id.

Configuring persistence

The entity API looks great and very intuitive, but how does the server know which database is supposed to store/query the entity objects? The persistence.xml file (located in the META-INF folder of your project) is the standard JPA configuration file. Believe it, this is a huge leap towards application server compatibility. By configuring this file, you can easily switch from one persistence provider to another and thus, also from one application server to another.

At the beginning, the persistence.xml file contains just the mapped entities we created. We have to specify the persistence provider and the underlying datasource used.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.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_1_0.xsd">
<persistence-unit name="AppStore" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/MySqlDS</jta-data-source>
<class>com.packtpub.jpa.example3.Customer</class>
<class>com.packtpub.jpa.example3.Item</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
</properties>
</persistence-unit>
</persistence>

The highlighted attributes need to be added to persistence.xml. The attribute name is a mandatory element, which will be used to reference the persistence unit from our Enterprise JavaBeans. Then, we have specified the provider factory, which will be used (in our case, it's HibernatePersistence). Another key attribute is the jta-data-source that needs to point to a datasource component. The last property, hibernate.dialect, will specify the O/R dialect class.

The only thing we have missed out is adding a datasource to the JBoss configuration. Beginning with the templates in docsexamplesjca folder of JBoss, we will create a MySQL datasource, which points to our appstore schema:

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
<jndi-name>MySqlDS</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/ appstore</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>jboss</user-name>
<password>jboss</password>
<exception-sorter-class-name>org.jboss.resource.adapter. jdbc.vendor.MySQLExceptionSorter
</exception-sorter-class-name>
<metadata>
<type-mapping>mySQL</type-mapping>
</metadata>
</local-tx-datasource>
</datasources>

Save this file as mysql-ds.xml in the deploy folder of your JBoss configuration.

Creating a Session Bean client

With the classes that we just saw, we have completed the entity layer but we have not finished with the EJB project. Actually, if you allow client applications to directly access the entity, then the client requires knowledge of the entity implementation that goes beyond what clients should have. For instance, manipulating an entity requires knowledge of the entity relationships (such as associations and inheritance) that are involved, inappropriately exposing the client to all of the details of the business model.

The best practice advocated by Java EE architects is to provide a business interface to the entity subsystem; the standard interfaces for the persistence entities are Stateless Session Beans.

In our example, we are going to create two interfaces for our EJB clients—one for remote clients and another for local clients. Java SE clients will connect to our session beans through the remote interface, whereas web clients (described in the next chapter) will conveniently use the local interface.

At this stage you should be comfortable with stateless bean. Create a new session EJB 3 from the File menu: New | Other | EJB | EJB 3 Session Bean. The suggested name for this example is com.packtpub.ejb.example3.StoreManager. This will also create the implementing class com.packtpub.ejb.StoreManagerBean. Here is its interface contract:

package com.packtpub.ejb.example3;
import java.util.List;
import javax.ejb.Local;
import com.packtpub.jpa.example3.Customer;
import com.packtpub.jpa.example3.Item;
public interface StoreManager {
public void createCustomer(String country,String name);
public List<Customer> findAllCustomers();
public Customer findCustomerByName(String name);
public Customer findCustomerById(int id);
public void saveOrder(int idCustomer, int price,
int quantity,String product);
public List<Item> findAllItems(int customerId);
}

We have defined one method createCustomer that will be used to add new customers to our store. Then we have added some finder methods: findAllCustomers, findAllCustomerByName, and findCustomerById that can be used to retrieve the single Customer or the whole list.

Items ordered can be persisted by means of the saveOrder method and queried with the findAllItems method.

The interface we just saw will be extended by the StoreManagerLocal and StoreManagerRemote interfaces, which will provide respectively the local and remote view of the EJB:

package com.packtpub.ejb.example3;
import javax.ejb.Local;
@Local
public interface StoreManagerLocal
extends StoreManager {
}

And here's the StoreManagerRemote interface:

package com.packtpub.ejb.example3;
import javax.ejb.Remote;
@Remote
public interface StoreManagerRemote
extends StoreManager{
}

The concrete implementation class is contained in the StoreManagerBean class:

package com.packtpub.ejb.example3;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.*;
import org.jboss.ejb3.annotation.LocalBinding;
import org.jboss.ejb3.annotation.RemoteBinding;
import com.packtpub.jpa.example3.*;
@Stateless
@RemoteBinding(jndiBinding="AppStoreEJB/remote")
@LocalBinding(jndiBinding="AppStoreEJB/local")
public class StoreManagerBean implements StoreManagerLocal,
StoreManagerRemote {
@PersistenceContext(unitName="AppStore")
private EntityManager em;
public void createCustomer(String country,String name) {
Customer customer = new Customer();
customer.setCountry(country);
customer.setName(name);
em.persist(customer);
}
public void saveOrder(int idCustomer, int price,
int quantity, String product) {
Customer customer = findCustomerById(idCustomer);
Item order = new Item();
order.setCustomerFK(customer);
order.setPrice(price);
order.setQuantity(quantity);
order.setProduct(product);
em.persist(order);
}
public List<Item> findAllItems(int customerId)
{
Query query = em.createQuery("FROM Customer where id=:id");
query.setParameter("id", customerId);
Customer customer = (Customer)query.getSingleResult();
List <Item>customerOrders = customer.getItems();
return customerOrders;
}
public Customer findCustomerByName(String customerName)
{
Query query = em.createQuery("FROM Customer where name=:name");
query.setParameter("name", customerName);
Customer customer = (Customer)query.getSingleResult();
return customer;
}
public Customer findCustomerById(int id)
{
Query query = em.createQuery("FROM Customer where id=:id");
query.setParameter("id", id);
Customer customer = (Customer)query.getSingleResult();
return customer;
}
public List<Customer> findAllCustomers() {
Query query = em.createQuery("FROM Customer");
List<Customer> customerList = query.getResultList();
return customerList;
}
}

The @PersistenceContext [1] annotation added to the EntityManager field, injects a container-managed persistence context. You might think of this as an object-oriented connection to the RDBMS. The following diagram illustrates the whole sequence:

Creating a Session Bean client

As you can see, the injected resource AppStore references the persistence unit defined in persistence.xml. This in turn points to the jta datasource named MySqlDS. The datasource (defined in the mySQL-ds.xml configuration file) contains the connection details of the MySQL appstore schema.

The first method, createCustomer, illustrates how you can perform the equivalent of a CREATE SQL statement using JPA. As you can see, it's all about creating object instances [2]. Until you persist [3] your objects, however, all changes are held in memory.

The method saveOrder works quite the same. Moving to the finder methods, we meet the findAllItems method. If you have already worked with Hibernate, this should sound very familiar to you. In fact, JPA also uses a database-independent language, Java Persistence Query Language, to issues queries. It is a rich language that allows you to query any complex object's model (associations, inheritance, abstract classes, and so on) using common built-in database functions. There are functions that deal with strings (LOWER, UPPER, TRIM, CONCAT, LENGTH, and SUBSTR), numbers (ABS, SQRT, and MOD), or collections (COUNT, MIN, MAX, and SUM). Like SQL, you can also sort the results (ORDER BY) or group them (GROUP BY).

In our sample method, we issue a query [4], which is filtered by the id parameter [5]. The use of parameters here is quite similar to plain PreparedStatements bound variables. The EJB contains additional finder methods (findCustomerById, findCustomerByName, and findAllCustomers) that are modeled using the same steps as in findAllItems.

At this point, our EJB layer is completed. Here's a screenshot of the Project Explorer that depicts the complete AppStore project:

Creating a Session Bean client

It's now time to deploy the EJB project to our application server. Follow the same steps described in the session bean examples, that is, from the JBoss AS perspective right-click on the server node and choose Add and remove Project; then in the same window choose Full Publish in order to deploy the project. Verify from the server console that your EJB has bound correctly in the JNDI tree:

AppStoreEJB/remote - EJB3.x Default Remote Business Interface
AppStoreEJB/local - EJB3.x Default Local Business Interface

The next section is about creating the client interface for this application.

Creating a test client for our AppStore

Clients for session beans can be simple J2SE classes or server-side components such as JSP-servlets. We will now create a very simple Java class for interacting with the session remote interface. Add a new Java class to the project from File | New | Class and choose a name for it. Here's the com.packtpub.client.example3.TestAppStore class:

package com.packtpub.client.example3;
import java.util.*;
import javax.naming.*;
import com.packtpub.ejb.example3.*;
import com.packtpub.jpa.example3.*;
public class TestAppStore {
public static void main(String[] args) throws Exception {
Hashtable hash = new Hashtable();
hash.put("java.naming.factory.initial"," org.jnp.interfaces.NamingContextFactory");
hash.put("java.naming.provider.url","jnp://localhost:1099");
hash.put("java.naming.factory.url.pkgs"," org.jnp.interfaces");
Context ctx = new InitialContext(hash);
StoreManager storeManager = (StoreManager)ctx. lookup("AppStoreEJB/remote");
// Create a Customer [1]
storeManager.createCustomer("Usa","Clint Eastwood");
// Retrieve the Customer [2]
Customer customer = storeManager.findCustomerByName ("Clint Eastwood");
// Save an order for an Item
storeManager.saveOrder(customer.getId(), 1000,5, "Bycycle");
// Find all Items ordered by the Customer [3]
List<Item> items = storeManager.findAllItems (customer.getId());
System.out.println("Listing orders for " +customer.getName());
Iterator <Item> iter = items.iterator();
while (iter.hasNext()) {
Item item = iter.next();
System.out.println("----------------");
System.out.println("id #" +item.getId());
System.out.println("product #" +item.getProduct());
System.out.println("qty #" +item.getQuantity());
System.out.println("$ #" +item.getPrice());
}
}
}

The Java class should be self-explanatory at this stage. We have created a customer from Usa [1] who places an order for an item [2]. The list of pending items ordered is queried by the findCustomerByName method [3]. That's all folks!

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

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