7 Using JPA with Coherence

In this exercise, you learn how to use Java Persistence API (JPA) to perform object-relational mapping. This chapter contains the following sections:

This exercise assumes that you have a working version of the Oracle Database 10g Express Edition (also known as the OracleXE database) installed on your system. If you do not already have database, you can download one here:

http://www.oracle.com/technology/software/products/database/xe/index.html

7.1 Introduction

A major enhancement in EJB technology is the addition of JPA, which simplifies the entity persistence model and adds capabilities that were not in the EJB 2.1 technology.

JPA deals with the way relational data is mapped to Java objects ("persistent entities"), the way that these objects are stored in a relational database so that they can be accessed at a later time, and the continued existence of an entity's state even after the application that uses it ends. In addition to simplifying the entity persistence model, the JPA standardizes object-relational mapping.

To determine how data is stored within a Coherence cluster, a backing map is used. By default, Coherence uses a memory-based backing map. To persist data, there are several backing map implementations.

You use the JPA implementation within this lesson. This implementation provides Object Relational Mapping (ORM) from the Java world to the database world, allowing you to use the standard Coherence get or put, and have the Coherence calls translated into database calls using JPA and EclipseLink.

7.2 Mapping Relational Data to Java Objects with JPA

In this exercise, you will use JDeveloper to perform the following:

  • Create a connection to the HR schema in the OracleXE database.

  • Automatically generate JPA objects for the EMPLOYEES table.

  • Modify cache-server.cmd to point to the sample JPA cache-config.xml.

To use the Java Persistence API (JPA) to demonstrate data mapping with Coherence:

  1. Unlock the HR account in your pre-installed OracleXE database.

    It is assumed that you have the OracleXE database installed on your machine and can access the HR schema. To unlock the HR account, perform the following steps:

    1. Navigate to Start > All Programs> Oracle Database 10g Express Edition > Run SQL Command Line.

    2. Enter connect system as sysdba, and then enter welcome1 when prompted for the password. (Note this exercise assumes that your user name is system and password is welcome1).

      Figure 7-1 Connecting to the Database

      Connecting to the Database
      Description of "Figure 7-1 Connecting to the Database"

    3. Enter the command to unlock the account:

      alter user hr identified by hr account unlock;
      

      Figure 7-2 Unlocking the Database Account

      Unlocking the Database Account
      Description of "Figure 7-2 Unlocking the Database Account"

  2. Create a new project called Lab9.

    Figure 7-3 Creating a New Project

    Creating a New Project
    Description of "Figure 7-3 Creating a New Project"

    1. Set the default Java Options to the appropriate log level and to disable local storage.

      -Dtangosol.coherence.distributed.localstorage=false  -Dtangosol.coherence.log.level=3
      

    Figure 7-4 Setting Java Options

    Setting Java Options
    Description of "Figure 7-4 Setting Java Options"

  3. Create a new database connection to the HR schema.

    1. In the Application Resources section of the navigator, right-click Connections, select New Connection, and then Database Connection.

    2. Enter the details to connect to your HR schema and click OK.

      Connection Name: XE_HR

      Connection Type: Oracle (JDBC)

      Username: hr

      Password: hr

      —Click Test Connection.

      This should display "Success!"

      Click OK.

      Figure 7-5 Defining the Database Connection

      Defining the Database Connection
      Description of "Figure 7-5 Defining the Database Connection"

    3. Right-click the Lab9 project and select New. Under Business Tier, select EJB, and then select Entities from Tables. Click OK.

      Figure 7-6 Creating EJB Entity Beans

      Creating EJB Entity Beans
      Description of "Figure 7-6 Creating EJB Entity Beans"

    4. In the Create Entities from Tables window, select EJB 3.0 --JPA Entities, and then click Next.

      Figure 7-7 Specifying the EJB Version

      Specifying the EJB Version
      Description of "Figure 7-7 Specifying the EJB Version"

    5. Click New to create a new persistence unit. (Each persistence unit defines a set of classes and their mapping characteristics when persisting them.) Enter the following details and click OK.

      Name: JPA

      JTA Datasource Name: JPADS

      Non-JTA Datasource Name: JPADS_NOTJTA

      Database Platform: Oracle Server

      Platform: None

      After the Create Entities from Tables screen returns, click Next.

      Figure 7-8 Defining the Persistence Unit

      Defining the Persistance Unit
      Description of "Figure 7-8 Defining the Persistence Unit"

    6. Select the Online Database Connection option and click Next.

      Figure 7-9 Creating Entity Beans from Table Data

      Creating Entity Beans from Table Data
      Description of "Figure 7-9 Creating Entity Beans from Table Data"

    7. In the Database Connection Details window, click Next.

      Figure 7-10 Choosing the Database Connection

      Choosing the Database Connection
      Description of "Figure 7-10 Choosing the Database Connection"

    8. Query for the EMPLOYEES table and select it as shown in Figure 7-11. Click Next.

      Figure 7-11 Choosing the Table Data for the Entity Bean

      Choosing the Table Data for the Entity Bean
      Description of "Figure 7-11 Choosing the Table Data for the Entity Bean"

    9. Retain the General Options as they are and click Next.

      Figure 7-12 Choosing General Options for the Entity

      Choosing General Options for the Entity
      Description of "Figure 7-12 Choosing General Options for the Entity"

    10. Retain the Entity Class Details as they are and click Next.

      Figure 7-13 Specifying the Entity Details

      Specifying the Entity Details
      Description of "Figure 7-13 Specifying the Entity Details"

    11. You should see the following in the Summary page.

      Figure 7-14 Entity Bean Summary Page

      Entity Bean Summary Page
      Description of "Figure 7-14 Entity Bean Summary Page"

    12. Click Finish to complete the creation. You should now see the following in the navigator.

      Figure 7-15 Generating EJB Entity Beans—the EJB Log Window

      Generating EJB Entity Beans
      Description of "Figure 7-15 Generating EJB Entity Beans—the EJB Log Window"

    13. Replace the contents of persistence.xml with the code in Example 7-1 and save the file. Ensure that the connection details match your database connection details.

      Example 7-1 persistance.xml File Contents

      <persistence xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance" version="1.0" xmlns="http://java.sun.com/xml/ns/persistence">
      <persistence-unit name="JPA" transaction-type="RESOURCE_LOCAL">
          <provider>
              org.eclipse.persistence.jpa.PersistenceProvider
          </provider>
          <class>com.oracle.coherence.handson.Employees</class>
          <properties>
              <property name="eclipselink.logging.level" value="INFO"/>
              <property name="eclipselink.jdbc.driver" value="oracle.jdbc.OracleDriver"/>
              <property name="eclipselink.jdbc.url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
              <property name="eclipselink.jdbc.password" value="hr"/>
              <property name="eclipselink.jdbc.user" value="hr"/>
          </properties>
      </persistence-unit>
      </persistence>
      
  4. Create a cache configuration file for JPA.

    Open a text editor and create a file named jpa-cache-config.xml. Use the code illustrated in Example 7-2. Save the file in the home\oracle\labs\ directory.

    Example 7-2 Cache Configuration for JPA

    <?xml version="1.0" encoding="windows-1252" ?>
    <cache-config>
      <caching-scheme-mapping>
        <cache-mapping>
          <!-- Set the name of the cache to be the entity name  -->
          <cache-name>Employees</cache-name>
          <!-- Configure this cache to use the scheme defined below  -->
          <scheme-name>jpa-distributed</scheme-name>
        </cache-mapping>
      </caching-scheme-mapping>
      <caching-schemes>
        <distributed-scheme>
          <scheme-name>jpa-distributed</scheme-name>
          <service-name>JpaDistributedCache</service-name>
          <backing-map-scheme>
            <read-write-backing-map-scheme>
              <!--
          Define the cache scheme
          -->
              <internal-cache-scheme>
                <local-scheme/>
              </internal-cache-scheme>
              <cachestore-scheme>
                <class-scheme>
                  <class-name>com.tangosol.coherence.jpa.JpaCacheStore</class-name>
                  <init-params>
                    <!--
                This param is the entity name
                This param is the fully qualified entity class
                This param should match the value of the
                persistence unit name in persistence.xml
                -->
                    <init-param>
                      <param-type>java.lang.String</param-type>
                      <param-value>{cache-name}</param-value>
                    </init-param>
                    <init-param>
                      <param-type>java.lang.String</param-type>
                      <param-value>com.oracle.coherence.handson.{cache-name}</param-value>
                    </init-param>
                    <init-param>
                      <param-type>java.lang.String</param-type>
                      <param-value>JPA</param-value>
                    </init-param>
                  </init-params>
                </class-scheme>
              </cachestore-scheme>
            </read-write-backing-map-scheme>
          </backing-map-scheme>
          <autostart>true</autostart>
        </distributed-scheme>
      </caching-schemes>
    </cache-config>
    
  5. Copy the cache-server.cmd file and modify the server properties.

    1. Open a terminal window. Navigate to the /oracle/product/coherence/bin directory and copy the cache-server.cmd file to jpa-cache-server.cmd.

      cp cache-server.cmd jpa-cache-server.cmd
      
    2. Edit jpa-cache-server.cmd. Add the following switch to Java_OPTS:

      -Dtangosol.coherence.cacheconfig=\home\oracle\labs\jpa-cache-config.xml
      
    3. Add the following CLASSPATH to the -cp argument: C:\home\oracle\labs\Lab9\classes

      C:\home\oracle\labs\Lab9\classes
      
    4. You must also add the following jar files to the CLASSPATH:

      Coherence JPA libraries: C:\oracle\product\coherence\lib\coherence-jpa.jar

      JDBC libraries: C:\oracle\product\wlserver_10.3\server\lib\ojdbc5.jar

      javax.persistence.* libraries: C:\oracle\product\modules\javax.persistence_1.0.0.0_1-0.jar

      EclipseLink Libraries: C:\oracle\product\jdeveloper\modules\oracle.toplink_11.1.1\eclipselink.jar

      ...
      C:\oracle\product\coherence\lib\coherence-jpa.jar; C:\oracle\product\wlserver_10.3\server\lib\ojdbc5.jar; C:\oracle\product\jdeveloper\modules\oracle.toplink_11.1.1\toplink.jar;
      ...
      

      Example 7-3 illustrates a modified jpa-cache-server.cmd file:

      Example 7-3 Modified jpa-cache-server.cmd File

      @echo off
      @
      @rem This will start a cache server
      @
      setlocal
       
      :config
      @rem specify the Coherence installation directory
      set coherence_home=c:/oracle/product/coherence
       
      @rem specify the JVM heap size
      set memory=512m
       
      :start
      if not exist "%coherence_home%\lib\coherence.jar" goto instructions
       
      if "%java_home%"=="" (set java_exec=java) else (set java_exec=%java_home%\bin\java)
       
      :launch
       
      set java_opts="-Xms%memory% -Xmx%memory% -Dtangosol.coherence.cacheconfig=\home\oracle\labs\jpa-cache-config.xml"
       
      "%java_exec%" -server -showversion "%java_opts%" -cp "%coherence_home%\lib\coherence.jar;C:\home\oracle\labs\Lab9\classes;C:\oracle\product\coherence\lib\coherence-jpa.jar;C:\oracle\product\wlserver_10.3\server\lib\ojdbc5.jar;C:\oracle\product\jdeveloper\modules\oracle.toplink_11.1.1\eclipselink.jar;C:\oracle\product\modules\javax.persistence_1.0.0.0_1-0.jar" com.tangosol.net.DefaultCacheServer %1
       
      goto exit
       
      :instructions
       
      echo Usage:
      echo   ^<coherence_home^>\bin\cache-server.cmd
      goto exit
       
      :exit
      endlocal
      @echo on
      
    5. After modifying jpa-cache-server.cmd, make sure that all other cache servers are stopped. Run jpa-cache-server.cmd.

      C:\oracle\product\coherence\bin>jpa-cache-server.cmd
      
  6. Modify the Lab9 Project Properties.

    1. Edit the Lab9 Project Properties and modify the Run/Debug/Profile configuration. Append the following line to the existing Java Options.

      -Dtangosol.coherence.cacheconfig=\home\oracle\labs\jpa-cache-config.xml 
      

      Figure 7-16 Adding a JPA Cache Configuration to the Runtime Configuration

      Adding JPA Cache Configuration to Runtime Configuration
      Description of "Figure 7-16 Adding a JPA Cache Configuration to the Runtime Configuration"

    2. Add additional CLASSPATH entries to the existing project properties.

      Navigate to Tools > Project Properties > Libraries and Classpath. Use the Add JAR/Directory and Add Library buttons to add the following JAR files and libraries into CLASSPATH (Note: the coherence.jar file should already be present):

      eclipselink.jar for the EclipseLink API:

      C:\oracle\product\jdeveloper\modules\oracle.toplink_11.1\eclipselink.jar

      Coherence-eclipselink.jar for the CacheStore that can interact with JPA:

      C:\oracle\product\jdeveloper\modules\oracle.toplink_11.1\Coherence-eclipselink.jar

      — Java Persistence JAR file for the persistence API: C:\oracle\product\modules\javax.persistence_1.0.0.0_1-0.jar

      Oracle JDBC library for database connectivity

      Oracle XML Parser v2 library for interpreting XML

      The Libraries and Classpath screen should look similar to Figure 7-17:

    Figure 7-17 Adding JARs and Libraries to the Classpath

    Adding JARs to the Classpath
    Description of "Figure 7-17 Adding JARs and Libraries to the Classpath"

  7. In Lab9, create a new class to interact with the Employee object.

    1. Create a new class called RunEmployeeExample. Ensure that this has a main method.

      Figure 7-18 Creating a Java Class

      Creating a Java Class
      Description of "Figure 7-18 Creating a Java Class"

    2. Create the code to perform the following:

      — Get an employee using EMPLOYEE_ID. EMPLOYEE_ID should be of the long data type.

      — Display the salary.

      — Give them a 10% pay raise.

      — Get the value again to confirm the pay raise.

      Example 7-4 illustrates a possible solution.

      Example 7-4 Sample Employee Class File

      package com.oracle.coherence.handson;
      
      import com.tangosol.net.CacheFactory;
      import com.tangosol.net.NamedCache;
      
      public class RunEmployeeExample {
          public RunEmployeeExample() {
          }
      
          public static void main(String[] args) {
              long empId = 190L;  // emp 190 - Timothy Gates
              
              NamedCache employees = CacheFactory.getCache("Employees");
              
              Employees emp = (Employees)employees.get(empId);   
              
              System.out.println("Employee " + emp.getFirstName() + " " + 
                              emp.getLastName() + ", salary = $" + emp.getSalary() );
              
              // give them a 10% pay rise
              emp.setSalary( emp.getSalary() * 1.1);
              
              employees.put(empId, emp);
              
              Employees emp2 = (Employees)employees.get(empId);
              
              System.out.println("New Employee details are " + emp2.getFirstName() + " " + emp2.getLastName() + ", salary = $" + emp2.getSalary() );
          }
      }
      
    3. Run RunEmployeeExample. The output should look similar to the output illustrated Figure 7-19.

      Figure 7-19 Results from the RunEmployeeExample Application

      Results from the RunEmployeeExample Application
      Description of "Figure 7-19 Results from the RunEmployeeExample Application"

      Now that the Employees class has been annotated to persist to the database using JPA, and you have included the persistence.xml file to tell JPA where your database is, Coherence uses a CacheStore implementation that uses JPA to load and store objects to the database. When you use the get(Object key) method, the following happens:

      —Coherence looks for the entry with the key.

      —If the entry has not already been cached, or it is expired from the cache, Coherence asks the backing map, which uses JPA and EclipseLink to retrieve the data.

      —If the entry is in the cache, Coherence returns the entry directly to the application without going through EclipseLink. When you use put(Object Key,Object Value), Coherence uses JPA through EclipseLink to persist any changes to the database.