This lab introduces how to use Oracle TopLink Grid feature to grid enable a Java Persistence API (JPA) application with Oracle Coherence. TopLink Grid and Coherence can be combined using two distinct application architectures:
a "traditional" Coherence application architecture with TopLink providing database access, or
with Coherence backing TopLink in a "JPA on the Grid" architecture.
This lab will focuses on using TopLink Grid to build JPA on the Grid applications that provide a way to scale JPA applications through the use of Coherence.
This document includes the following sections:
This lab requires the following (minimum requirements):
Java 1.5 JDK/JRE
Coherence 3.5
http://www.oracle.com/technology/software/products/ias/htdocs/coherence.html
TopLink 11g Release 1 (11.1.1), which includes EclipseLink
http://www.oracle.com/technology/software/products/ias/htdocs/1111topsoft.html
Oracle Enterprise Pack for Eclipse (OEPE) 11g Release 1 (11.1.1)
http://www.oracle.com/technology/software/products/oepe/oepe_11gR1.html
Oracle XE Database
http://www.oracle.com/technology/products/database/xe/index.html
Before running the labs, you must install the necessary software and configure your environment.
You can download the lab samples from:
http://www.oracle.com/technology/products/ias/toplink/doc/11110/grid/labs/toplinkgridlab.zip
Use this procedure to install the necessary software for the labs.
If not already installed on your machine, download and install a Java 1.5 or higher JDK or JRE.
Download and unzip the lab .ZIP
file into a folder, referred to as LAB_ROOT. After unzipped unzipping the file, the folder will contain several sub-folders, including toplink and coherence.
Download Oracle TopLink 11g Release 1 (11.1.1) and unzip into LAB_ROOT/toplink.
Download Coherence 3.5 and unzip into LAB_ROOT. It will extract into the LAB_ROOT/coherence folder.
Note:
Confirm that the folder structure is correct; that you have LAB_ROOT/coherence and not LAB_ROOT/coherence/coherence.Download OEPE 11g Release 1 (11.1.1), based on Eclipse Galileo (3.5), and unzip into any folder, referred to as OEPE install.
Download and install Oracle XE. Follow the installation instructions included with the download. You can install XE in any folder, referred to as ORACLE_XE_ROOT.
Create or enable the Oracle database user scott with password tiger. Grant the connect and resource permissions to the user scott.
The labs are configured for username scott with password tiger. To use a different user, you must change the database login values used in the labs.
Use this procedure to configure your lab environment.
Open OEPE by running eclipse.exe
from the OEPE install directory.
You'll be prompted for a workspace. Select the LAB_ROOT folder, which contains your Eclipse workspace.
If you've unzipped TopLink and Coherence into the correct folders, the workspace will open with no errors in the Problems view.
If you do have errors, select Project > Clean from the menu and select Clean all projects to recompile and revalidate the labs. If errors remain, confirm that you have unzipped both TopLink and Coherence into the correct folders.
From the menu, select Window > Perspectives to open the JPA perspective.
In the JPA Perspective use the Data Source Explorer view to change the Oracle XE connection, if needed. By default, the Oracle XE connection uses scott/tiger @ localhost.
To use a different schema, right-click the Oracle XE connection in the Data Source Explorer and select Properties, as shown in Figure 12. Select the Driver Properties section and edit the values to reflect your connection as shown in Figure 13.
Note:
If you change the defaults, you must also edit the connection values in thepersistence.xml
configuration files used in the labs.Each of the three labs works with the same simple domain model shown in Figure 15. It consists of an Employee entity with an Address and a collection of PhoneNumbers.
The entities are all pre-mapped, allowing you to focus on the TopLink Grid configuration – not JPA mapping.
This document contains the following Labs:
The most basic configuration is using Coherence with EclipseLink as a shared (L2) cache. EclipseLink has a local, built-in shared-object cache that allows concurrent and successive transactions to benefit from reads and updates performed by other transactions. When entities are updated, after a successful database commit, the updates are then applied to the shared cache, allowing transactions can see the changes.
Replacing the local, built-in shared-object cache with Coherence maintains the same cache semantics. Applications do not need to be changed – they are unaware that the cache implementation has become a distributed cache rather than a local one.
Configuring Coherence as a shared cache involves:
Configuring entities to use Coherence
Defining a Coherence cache configuration for those entities
Lab 1 (see Figure 16) is provided as a ready-to-run JPA project in Eclipse.
Open the Employee, Address, and PhoneNumber entities and examine how they are mapped.
Use the following procedure once, before configuring Coherence as the shared cache, to confirm your environment is correct.
If your database connection differs from the defaults, you must update the following EclipseLink JDBC properties in the META-INF/persistence.xml
file:
eclipselink.jdbc.driver
eclipselink.jdbc.url
eclipselink.jdbc.user
eclipselink.jdbc.password
From the Project Explorer view, right-click the Lab 1-TopLink Grid Cache project and select JPA > Generate Tables from Entities… from the menu to drop and create the database tables used in the lab. You can safely ignore any errors due to failures to DROP non-existent tables. Generating tables will log you into the database.
Note:
f you encounter a Schema "null" cannot be resolved for table "<TABLE_NAME>" error, you've hit a known Eclipse bug. To resolve the issue, right-click the project and choose select Validate from menu. Eclipse will clear the errors.Run gridcache.example.InsertExample to populate the database with an Employee, Address, and a PhoneNumber. The createEmployee()
method, shown here, defines the entities:
public static Employee createEmployee() { Employee employee = new Employee(); employee.setFirstName("Bob"); employee.setLastName("Smith"); Address address = new Address(); address.setCity("Toronto"); address.setPostalCode("L5J2B5"); address.setProvince("ON"); address.setStreet("1450 Acme Cr., Suite 4"); address.setCountry("Canada"); employee.setAddress(address); employee.addPhoneNumber("Work", "613", "5558812"); return employee; }
The console output displays the following:
[EL Fine]: Connection(876215)--UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ? [EL Fine]: Connection(876215)-- bind => [50, SEQ_GEN] [EL Fine]: Connection(876215)--SELECT SEQ_COUNT FROM SEQUENCE WHERE SEQ_NAME = ? bind => [SEQ_GEN] [EL Fine]: Connection(876215)--INSERT INTO GRIDCACHE_ADDRESS (ID, POSTALCODE, STREET, PROVINCE, VERSION, COUNTRY, CITY) VALUES (?, ?, ?, ?, ?, ?, ?) [EL Fine]: Connection(876215)-- bind => [2, L5J2B5, 1450 Acme Cr., Suite 4, ON, 1, Canada, Toronto] [EL Fine]: Connection(876215)--INSERT INTO GRIDCACHE_EMPLOYEE (ID, LASTNAME, FIRSTNAME, VERSION, ADDRESS_ID) VALUES (?, ?, ?, ?, ?) [EL Fine]: Connection(876215)-- bind => [1, Smith, Bob, 1, 2] [EL Fine]: Connection(876215)--INSERT INTO GRIDCACHE_PHONE (ID, AREACODE, NUM, TYPE, VERSION, OWNER_ID) VALUES (?, ?, ?, ?, ?, ?) [EL Fine]: Connection(876215)-- bind => [3, 613, 5558812, Work, 1, 1] [EL Config]: Connection(14361585)--disconnect
EclipseLink updates an ID, generates the SEQUENCE table, and inserting the ID into three tables. All tables in this lab are prefixed with GRIDCACHE_.
Run gridcache.example.QueryExample1. This queries all Employees with JPQL and a single Employee with an EntityManager.find
call, then prints the results.
... List<Employee> employees = em.createQuery("select e from Employee e").getResultList(); ... Employee employee = em.find(Employee.class, employeeId); ...
The console output displays the following:
------------------JPQL Query [EL Fine]: Connection(876215)--SELECT ID, LASTNAME, FIRSTNAME, VERSION, ADDRESS_ID FROM GRIDCACHE_EMPLOYEE l2.model.Employee@e33e18(1: Smith, Bob) [EL Fine]: Connection(876215)--SELECT ID, POSTALCODE, STREET, PROVINCE, VERSION, COUNTRY, CITY FROM GRIDCACHE_ADDRESS WHERE (ID = ?) bind => [2] City: Toronto [EL Fine]: Connection(876215)--SELECT ID, AREACODE, NUM, TYPE, VERSION, OWNER_ID FROM GRIDCACHE_PHONE WHERE (OWNER_ID = ?) bind => [1] l2.model.PhoneNumber@21d23b(3: Work: 613-5558812) ------------------em.find Query l2.model.Employee@e33e18(1: Smith, Bob) City: Toronto l2.model.PhoneNumber@21d23b(3: Work: 613-5558812)
EclipseLink issues three SELECT statements:
To read all the Employees
To read the one Employee's Address
To read its PhoneNumbers
The EntityManager.find()
does not result in a SQL statement because the Employee is found in the local cache without needing a database query.
To instruct EclipseLink to cache in Coherence you need to configure a CoherenceInterceptor as the cache interceptor for an Entity. A Coherence interceptor will reroute all cache get
and put
operations to Coherence instead of the built-in EclipseLink shared cache. This configuration, shown in Figure 17, is referred to as "cache aside."
Add the @CacheInterceptor
annotation to all lab entities with the CoherenceInterceptor.class
as its sole parameter, as shown in this example:
import oracle.eclipselink.coherence.integrated.cache.CoherenceInterceptor; import org.eclipse.persistence.annotations.CacheInterceptor; @CacheInterceptor(CoherenceInterceptor.class) public class Employee implements Serializable { ...
The labs include a coherence-cache-config.xml
file. In the lab configuration file, all entities are configured identically. Although there is no need to edit the file, you should note and review the following elements:
<caching-scheme-mapping> <cache-mapping> <cache-name>*</cache-name> <scheme-name>eclipselink-distributed</scheme-name> </cache-mapping> </caching-scheme-mapping>
The asterisk, *, in the cache-name element will match any entity name. By default, TopLink Grid uses the entity name as the name of the associated cache. You can override this behavior by using the EclipseLink @Property
annotation on an entity, as shown in this example:
@Property(name="coherence.cache.name", value="Employee") @CacheInterceptor(CoherenceInterceptor.class) public class Employee implements Serializable { ...
In these labs, the cache names are default to the entity name.
In the Grid Cache lab, all entities use a cache scheme named eclipselink-distributed which is a distributed (partitioned) cache. The distributed backing map scheme eclipselink-distributed has a serializer configured to support serialization of entity relationships into Coherence, as shown in this example:
<caching-schemes> <distributed-scheme> <scheme-name>eclipselink-distributed</scheme-name> <service-name>EclipseLinkJPA</service-name> <!-- Configure a wrapper serializer to support serialization of relationships. --> <serializer> <class-name> oracle.eclipselink.coherence.integrated.cache.WrapperSerializer </class-name> </serializer> ...
Now that the entities are configured to be cached in Coherence and a coherence-cache-config.xml
file is defined, you can re-run the lab with Coherence.
Reset the database by right-clicking the Lab 1-TopLinkGrid Grid Cache project in the Project Explorer view and selecting JPA > Generate Tables from Entities… from the pop-up menu to drop and create the lab tables.
Run gridcache.example.StartCacheServer to start the Coherence cache server. After being configured to use Coherence, of the examples require a cache server to be running.
Tip:
When running multiple programs in Eclipse, use the drop-down list to switch between each program's console, as shown in Figure 18. When reviewing the console output, be aware of which console you are viewing: the cache server console or the example console.Run gridcache.example.InsertExample to create example entities in the database and populate the Coherence cache. In addition to Coherence boot messages and INSERT statements in the console you will also see messages showing entities being put into Coherence under their primary key, as shown in this example:
[EL Fine]: Coherence(PhoneNumber)::Put: 3 value: l2.model.PhoneNumber@59cbda(3: Work: 613-5558812) [EL Fine]: Coherence(Employee)::Put: 1 value: l2.model.Employee@11c55bb(1: Smith, Bob) [EL Fine]: Coherence(Address)::Put: 2 value: l2.model.Address@135133(Toronto)
Run gridcache.example.QueryExample1. In the console you will see a single SELECT statement for all Employees resulting from the JQPL query select e from Employee e
-- that's all. You will also see the messages logging Coherence interaction. Compare this output with the output generated when you ran QueryExample1 before enabling the Coherence cache.
Run gridcache.example.QueryExample2 to illustrate which database queries are produced when you have a series of EntityManagers, such as for an application deployed to an application server. This example is useful when compared to the output generated in subsequent labs.
In "Lab 1: Grid Cache Configuration" you learned how to configure Coherence as a shared entity cache. In this lab you will increase the use of Coherence by directing all read queries (i.e., select
and find
) to Coherence. In this "Grid Read" configuration, all read operations are directed to Coherence but write operations are handled by EclipseLink and directed to the database. This configuration is described as "read through," as shown in Figure 19. Use this configuration when you need highly available data and a database that is always up-to-date. This configuration supports JTA so you can participate in a distributed transaction, Coherence is only updated once the entire JTA transaction commits.
The Grid Read configuration extends the Grid Cache configuration. In addition to a cache interceptor, various read query types are also directed to Coherence. To simplify configuration, TopLink Grid provides an EclipseLink customizer class that performs the necessary configuration changes, including setting the cache interceptor you configured in Lab 1.
The CoherenceReadCustomizer is configured using the EclipseLink @Customizer
annotation, as shown here:
import oracle.eclipselink.coherence.integrated.config.CoherenceReadCustomizer; import org.eclipse.persistence.annotations.Customizer; @Customizer(CoherenceReadCustomizer.class) public class Employee implements Serializable {
Alternatively, you can also a Customizer in the persistence.xml
and eclipselink-orm.xml
.
From the Project Explorer view, right click the Lab 2-TopLinkGrid Grid Read project and select Open Project from the popup menu. Eclipse may report errors due to missing tables that correspond to the JPA entities in the project-we'll resolve the errors by generating the tables.
Add @Customizer(CoherenceReadCustomizer.class)
, as shown in the previous code example, to all the lab entities.
With all read queries directed to Coherence, the coherence-cache-config.xml
is different than in the Grid Cache configuration. In particular, it makes sense to configure a CacheLoader so that Coherence can query the database for an individual object if it doesn't contain it.
This example shows an excerpt of the coherence-cache-config.xml
from Lab 2:
<distributed-scheme> <scheme-name>eclipselink-distributed-readonly</scheme-name> <service-name>EclipseLinkJPAReadOnly</service-name> ... <backing-map-scheme> <read-write-backing-map-scheme> ... <!-- Define the cache scheme --> <cachestore-scheme> <class-scheme> <class-name> oracle.eclipselink.coherence.integrated.EclipseLinkJPACacheLoader </class-name> <init-params> <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>employee</param-value> </init-param> </init-params> </class-scheme> </cachestore-scheme> <read-only>true</read-only> </read-write-backing-map-scheme> </backing-map-scheme> <autostart>true</autostart></distributed-scheme>
We configure an EclipseLinkJPACacheLoader on the backing map from the oracle.eclipselink.coherence.integrated
package. Note the integrated package name -- use this CacheLoader when using TopLink Grid with a JPA front end, as in this lab.
When using a TopLink Grid CacheLoader in a traditional Coherence API based application, use the EclipseLinkJPACacheLoader in the oracle.eclipselink.coherence.standalone package
.
After configuring the entities with the CoherenceReadCustomizer and configuring the caches in the coherence-cache-config.xml
with a CacheLoader you can run the lab.
Stop any CacheServers that may still be running from the previous Lab.
If your database connection differs from the defaults, you must update the following EclipseLink JDBC properties in the META-INF/persistence.xml
file:
eclipselink.jdbc.driver
eclipselink.jdbc.url
eclipselink.jdbc.user
eclipselink.jdbc.password
From the Project Explorer view, right-click the Lab 2 project and select JPA > Generate Tables from Entities… from the pop-up menu to reset the database
Run gridread.example.StartCacheServer to start the a Coherence cache server.
Run gridread.example.InsertExample to create example entities in the database and populate the Coherence cache. In addition to Coherence boot messages and INSERT statements in the console you will also see messages showing entities being put into Coherence under their primary key, as shown in this example.
[EL Fine]: Connection(4889213)--INSERT INTO GRIDREAD_ADDRESS (ID, POSTALCODE, STREET, PROVINCE, VERSION, COUNTRY, CITY) VALUES (?, ?, ?, ?, ?, ?, ?) [EL Fine]: Connection(4889213)-- bind => [2, L5J2B5, 1450 Acme Cr., Suite 4, ONT, 1, Canada, Toronto] [EL Fine]: Connection(4889213)--INSERT INTO GRIDREAD_EMPLOYEE (ID, LASTNAME, FIRSTNAME, VERSION, ADDRESS_ID) VALUES (?, ?, ?, ?, ?) [EL Fine]: Connection(4889213)-- bind => [1, Smith, Bob, 1, 2] [EL Fine]: Connection(4889213)--INSERT INTO GRIDREAD_PHONE (ID, AREACODE, NUM, TYPE, VERSION, OWNER_ID) VALUES (?, ?, ?, ?, ?, ?) [EL Fine]: Connection(4889213)-- bind => [3, 613, 5558812, Work, 1, 1] [EL Fine]: Coherence(Employee)::Put: 1 value: read.model.Employee@948069(1: Smith, Bob) [EL Fine]: Coherence(Address)::Put: 2 value: read.model.Address@1b59919(Toronto) [EL Fine]: Coherence(PhoneNumber)::Put: 3 value: read.model.PhoneNumber@1588325(3: Work: 613-5558812)
The console is similar to the console output from Lab 1 with the Grid Cache configuration
Run gridread.example.QueryExample1. In the QueryExample1 console EclipseLink does not issue a SELECT statement from the JQPL query select e from Employee e
because the query was translated to a Coherence Filter and passed to Coherence for evaluation.
Compare this output with the output from gridcache.example.QueryExample1 in "Lab 3: "Grid Entity" Configuration":
In Lab 1, JPQL queries were always translated to SQL and executed on the database.
In Lab 2, the EntityManager.find()
, is evaluated against Coherence and produces no SQL.
The Grid Entity configuration extends the Grid Read configuration, redirecting all write queries (INSERT
, UPDATE
, and DELETE
) to Coherence instead of the database. This configuration is useful when you need quick response time and can use a write-behind strategy to periodically flush updates to the database. This is achieved through configuration of a CacheStore on the Coherence cache. This configuration may be described to as both "read through" and "write through" (Figure 20).
Potential downsides to this configuration are the loss of JTA transaction participation, EclipseLink write optimizations like batch writing, and the need to relax referential integrity rules due to the unpredictable order in which Coherence caches may write updates to the database.
Like the "Lab 2: "Grid Read" Configuration", Grid Entity is enabled by an EclipseLink Customizer, CoherenceReadWriteCustomizer, set on an individual entity as shown in the following example:
import oracle.eclipselink.coherence.integrated.config.CoherenceReadWriteCustomizer; import org.eclipse.persistence.annotations.Customizer; @Customizer(CoherenceReadWriteCustomizer.class) public class Employee implements Serializable {
From the Project Explorer view, right-click the Lab 3-TopLinkGrid Grid Entity project and select Open Project from the popup menu. As in the other labs, you may see errors due to missing tables that correspond to the JPA entities in the project. These will be resolved after generating the tables.
Add @Customizer(CoherenceReadWriteCustomizer.class)
, as shown in the previous code example, to all the lab entities.
To propagate updates to the database, a Coherence entity cache needs to be configured with a CacheStore. A CacheStore is configured like a CacheLoader. The difference is in the implementation which supports write operations in addition to read operations.
<distributed-scheme> <scheme-name>eclipselink-distributed-readwrite</scheme-name> ... <cachestore-scheme> <class-scheme> <class-name> oracle.eclipselink.coherence.integrated.EclipseLinkJPACacheStore </class-name> ...
As in "Lab 2: "Grid Read" Configuration", the EclipseLinkJPACacheStore is in the integrated package because we are using JPA as our programming API.
After configuring the your entities with the CoherenceReadWriteCustomizer and configuring the caches in the coherence-cache-config.xml
with a CacheStore, you can run the lab.
Stop any CacheServers that may still be running from the previous Lab.
If your database connection differs from the defaults, you must update the following EclipseLink JDBC properties in the META-INF/persistence.xml
file:
eclipselink.jdbc.driver
eclipselink.jdbc.url
eclipselink.jdbc.user
eclipselink.jdbc.password
From the Project Explorer view, right-click the Lab 3 project and select JPA>Generate Tables from Entities… from the pop-up menu to reset the database.
Run gridentity.example.StartCacheServer to start the Coherence cache server.
Run gridentity.example.InsertExample to create example entities in the database and populate the Coherence cache. In the InsertExample console you will see messages showing entities being put into Coherence under their primary key, but no INSERT statements.
[EL Fine]: Coherence(Employee)::ConditionalPut: 1 value: readwrite.model.Employee@b9b618(1: Smith, Bob) [EL Fine]: Coherence(PhoneNumber)::ConditionalPut: 3 value: readwrite.model.PhoneNumber@800aa1(3: Work: 613-5558812) [EL Fine]: Coherence(Address)::ConditionalPut: 2 value: readwrite.model.Address@169dd64(Toronto)
You will see database queries in the CacheServer console because with CoherenceReadWrite configured, all queries in the QueryExample client program are being directed to Coherence. When an entity is put into Coherence, the CacheStore determines if the put entity is new, which will produce an INSERT statement, or has been updated, which will produce an UPDATE statement.
Since the Grid Entity configuration has the same read behavior as the "Lab 2: "Grid Read" Configuration" configuration, running gridentity.example.QueryExample1 will produce the same results as gridread.example.QueryExample1. In the console, there are no SELECT statements resulting from the JQPL query select e from Employee e
.
TopLink Grid provides integration between EclipseLink JPA and Oracle Coherence. This lab demonstrated configurations ranging from using Coherence as a shared L2 cache to using Coherence as data source with JPQL queries translated to Filters executed in the grid.
As these labs illustrated, configuring Coherence with TopLink Grid is simple, straight forward, and introduces limited changes to JPA applications.