Difference between revisions of "EclipseLink/Examples/JPA/DCN"

From Eclipsepedia

Jump to: navigation, search
(Running the example)
Line 32: Line 32:
 
* Database event example - [http://dev.eclipse.org/svnroot/rt/org.eclipse.persistence/trunk/examples/org.eclipse.persistence.example.jpa.databaseevent/org.eclipse.persistence.example.jpa.databaseevent.zip download link] - [http://dev.eclipse.org/svnroot/rt/org.eclipse.persistence/trunk/examples/org.eclipse.persistence.example.jpa.databaseevent/ SVN]
 
* Database event example - [http://dev.eclipse.org/svnroot/rt/org.eclipse.persistence/trunk/examples/org.eclipse.persistence.example.jpa.databaseevent/org.eclipse.persistence.example.jpa.databaseevent.zip download link] - [http://dev.eclipse.org/svnroot/rt/org.eclipse.persistence/trunk/examples/org.eclipse.persistence.example.jpa.databaseevent/ SVN]
  
==Configuring the example==
+
==Configuring Oracle Database Change Notification==
 
Database events can be configured using persistence unit properties (in your persistence.xml).  It can also be configured in code using a SessionCustomizer, or using System properties (which match the persistence unit properties).  This example will use persistence unit properties.
 
Database events can be configured using persistence unit properties (in your persistence.xml).  It can also be configured in code using a SessionCustomizer, or using System properties (which match the persistence unit properties).  This example will use persistence unit properties.
  
Line 55: Line 55:
 
GRANT CHANGE NOTIFICATION TO SCOTT
 
GRANT CHANGE NOTIFICATION TO SCOTT
 
</source>
 
</source>
 +
 +
==Limitations, and how Oracle Database Change Notification works==
 +
To register for Oracle DCN you must specify which table you wish to receive database change events for.  When ever a row is inserted, updated, or deleted from the table, the database raises an event.  The event contains the database ROWID of the affected rows.
 +
 +
The object cache in EclipseLink is on the entities Id, so EclipseLink must maintain an index on the ROWID in addition to the Id.  EclipseLink's cache index support (also new in 2.4) is used for this.  When objects are read, EclipseLink includes the ROWID in the select when using Oracle DCN.  For inserts EclipseLink must select the ROWID for the inserted object to allow the cache index to be maintained.
 +
 +
EclipseLink must also avoid invalidating objects in the cache for the transactions processed by the same server, as the cache is already up to date.  To do this EclipseLink selects the Oracle transaction id on each transaction that makes a modification, and ignores DCN events for the same transaction id.
 +
 +
This requires a little extra SQL, but it pretty minimal.  EclipseLink only tracks the ROWID for the primary table, so if an update only updates a secondary table, EclipseLink will not invalidate the object.  The same occurs when objects are removed or added to a OneToMany, ManyToMany or ElementCollection relationship, EclipseLink has no way to know this event results in a change to the entity object.  This is easily solved through the use of optimistic locking.  As long a you include an @Version in your entity, the version column in the primary table will always be updated, and the object will always be invalidated.
  
 
==Running the example==
 
==Running the example==

Revision as of 14:24, 24 April 2012

Contents

Overview

EclipseLink supports a shared (L2) object cache that avoids database access for objects and their relationships. This cache is enabled by default which is normally not a problem, unless the database is modified directly by other applications, or by the same application on other servers in a clustered environment.

There are many solutions to caching in a shared environment, including:

  • disable the shared cache
  • only cache read-only objects
  • set a cache invalidation timeout
  • use refreshing on objects/queries when fresh data is required
  • use optimistic locking (writes on stale data will fail, and will automatically invalidate the cache)
  • using a distributed cache (such as Oracle TopLink Grid with Oracle Coherence)
  • using cache coordination
  • using database events to invalidate changed data

This example gives an overview of the database events option.

EclipseLink 2.4 adds support for a DatabaseEventListener to receive database events. EclipseLink provides an OracleChangeNotificationListener to integrate with Oracle's Database Event Notification support (also known as Query Change Notification). The Oracle database added this support in the 10.2 release, but did not fully enable the JDBC support for it until the 11.2 release. The OracleChangeNotificationListener uses Oracle's DCN support to listen to database row changes and invalidate the cache for the objects that are changed on the database. This allows for caching to be used in JPA, even if other applications, even non-Java applications are accessing and updating the same database. This can also be used as an alternative to cache coordination in a cluster.

Integrated support for other databases is not currently provided. If another database supports an event mechanism, or allows triggers to raise events, then it is possible to implement your own DatabaseEventListener to perform cache invalidation. In previous versions of the Oracle database it is possible to perform cache invalidation through triggers and Oracle AQ.

This example demonstrates enabling database event driven cache invalidation using Oracle DCN with the Oracle 11.2 database. The example runs in Java SE, but any other Java EE or EclipseLink supported environment should also work.

If you encounter any issues in running this example, please discuss, here

Prerequisites

The following software is required to run this example:

Configuring Oracle Database Change Notification

Database events can be configured using persistence unit properties (in your persistence.xml). It can also be configured in code using a SessionCustomizer, or using System properties (which match the persistence unit properties). This example will use persistence unit properties.

<property name="eclipselink.cache.database-event-listener" value="org.eclipse.persistence.platform.database.oracle.dcn.OracleChangeNotificationListener" />

By default all entity classes are registered for database change notification. To exclude a class from change notification the @Cache annotation is used.

@Entity
@Cache(databaseChangeNotificationType=DatabaseChangeNotificationType.NONE)
public class Order {
  ...
}

The database user must have the CHANGE NOTIFICATION privilege granted on the database.

GRANT CHANGE NOTIFICATION TO SCOTT

Limitations, and how Oracle Database Change Notification works

To register for Oracle DCN you must specify which table you wish to receive database change events for. When ever a row is inserted, updated, or deleted from the table, the database raises an event. The event contains the database ROWID of the affected rows.

The object cache in EclipseLink is on the entities Id, so EclipseLink must maintain an index on the ROWID in addition to the Id. EclipseLink's cache index support (also new in 2.4) is used for this. When objects are read, EclipseLink includes the ROWID in the select when using Oracle DCN. For inserts EclipseLink must select the ROWID for the inserted object to allow the cache index to be maintained.

EclipseLink must also avoid invalidating objects in the cache for the transactions processed by the same server, as the cache is already up to date. To do this EclipseLink selects the Oracle transaction id on each transaction that makes a modification, and ignores DCN events for the same transaction id.

This requires a little extra SQL, but it pretty minimal. EclipseLink only tracks the ROWID for the primary table, so if an update only updates a secondary table, EclipseLink will not invalidate the object. The same occurs when objects are removed or added to a OneToMany, ManyToMany or ElementCollection relationship, EclipseLink has no way to know this event results in a change to the entity object. This is easily solved through the use of optimistic locking. As long a you include an @Version in your entity, the version column in the primary table will always be updated, and the object will always be invalidated.

Running the example

  1. Install Oracle database (or use existing database)
  2. Configure paths in <example>/build.xml (JDBC_LIB, JPA_LIB, ECLIPSELINK_LIB)
  3. Configure database URL and user/password in persistence.xml in <example>src/meta-inf/
  4. Ensure the database user has CHANGE NOTIFICATION privilege in the database
  5. Install ant (or uses existing ant install)
  6. Build the application
    1. Run "ant"
    2. this will compile the example code
  7. Run the example
    1. Run "ant example"
    2. This should give the following output.
     [java] Starting example.
     [java]
     [java]
     [java] Selecting all customer on application #2.
     [java]
     [java]
     [java] Inserting new customer on application #1.
     [java]
     [java]
     [java] Selecting new customer on application #2.
     [java]
     [java]
     [java] Customer name on application #1: Test1
     [java] Customer name on application #2: Test1
     [java]
     [java]
     [java] Updating customer name to 'Test2' on application #1.
     [java]
     [java]
     [java] Selecting customer on application #2.
     [java]
     [java]
     [java] Customer name on application #1: Test2
     [java] Customer name on application #2: Test2
     [java]
     [java]
     [java] Updating customer name to 'Test3' through direct native SQL query on application #1.
     [java]
     [java]
     [java] Selecting customer on application #2.
     [java]
     [java]
     [java] Finding customer on application #1.
     [java]
     [java]
     [java] Customer name on application #1: Test3
     [java] Customer name on application #2: Test3
     [java]
     [java]
     [java] Updating customer name to 'Test4' through batch update query on application #1.
     [java]
     [java]
     [java] Selecting customer on application #2.
     [java]
     [java]
     [java] Finding customer on application #1.
     [java]
     [java]
     [java] Customer name on application #1: Test4
     [java] Customer name on application #2: Test4
     [java]
     [java]
     [java] Deleting customer on application #1.
     [java]
     [java]
     [java] Selecting customer on application #2.
     [java]
     [java]
     [java] Customer not found on application #2.
     [java]
     [java]
     [java] Inserting new customer 'Test5' on application #2.
     [java]
     [java]
     [java] Selecting customer on application #1.
     [java]
     [java]
     [java] Updating customer name to 'Test6' on application #1.
     [java]
     [java]
     [java] Selecting customer on application #2.
     [java]
     [java]
     [java] Customer name on application #1: Test6
     [java] Customer name on application #2: Test6
     [java]
     [java]
     [java] Example finished.

Links