22 Using Database Events to Invalidate the Cache

This chapter describes TopLink Database Change Notification (DCN), which allows you to use caching with a shared database in JPA.

This chapter includes the following sections:

Use Case

Users want to use a shared cache with their JPA application, however, external applications update the same database data, or the cache is in a clustered environment. The cache may retain stale data.

Solution

TopLink provides an API which allows the database to notify TopLink of database changes. The changed objects can then be invalidated in the TopLink shared cache. Stale data can be discarded, even if other applications access the same data in the database.

Components

  • Oracle 11gR2 (11.2) (or higher) database

  • TopLink 12c (12.1.2.0.0) or later.

    Note:

    TopLink's core functionality is provided by EclipseLink, the open source persistence framework from the Eclipse Foundation. EclipseLink implements Java Persistence API (JPA), Java Architecture for XML Binding (JAXB), and other standards-based persistence technologies, plus extensions to those standards. TopLink includes all of EclipseLink, plus additional functionality from Oracle.
    • EclipseLink library: eclipselink.jar

    • JDBC library: ojdbc6.jar.

    • JPA library: persistence.jar.

Sample

For sample files that illustrate the use of Database Change Notification and shared caching in an application that uses JPA, see "EclipseLink/Examples/JPA/DCN" in the EclipseLink documentation.

http://wiki.eclipse.org/EclipseLink/Examples/JPA/DCN

22.1 Introduction to the Solution

EclipseLink provides a shared (L2) object cache that can reduce database access for objects and their relationships. This cache is enabled by default which is normally not a problem, unless the contents of the database are modified directly by other applications, or by the same application on other servers in a clustered environment. This may result in stale data in the cache.

If the contents of the database are modified, then a mechanism is needed to ensure that the contents of the cache are synchronized with the database. That mechanism is provided by EclipseLink Database Change Notification. DCN allows shared caching to be used in the JPA environment.

Note:

Database Change Notification extends the functionality provided by the Oracle Database Continuous Query Notification feature. For more information, see "Continuous Query Notification" in Oracle Database JDBC Developer's Guide.

EclipseLink Database Change Notification extends the functionality provided by the Oracle Database Continuous Query Notification. One of the features of Continuous Query Notification is that it allows database events to be raised when rows in a table are modified.

To detect modifications, EclipseLink DCN uses the ROWID to inform of row level changes in the primary table. EclipseLink includes the ROWID in all queries for a DCN-enabled class. EclipseLink also selects the object's ROWID after an insert operation. EclipseLink maintains a cache index on the ROWID, in addition to the object's Id. EclipseLink also selects the database transaction ID once for each transaction to avoid invalidating the cache on the server that is processing the transaction.

EclipseLink DCN is enabled through the OracleChangeNotificationListener (org.eclipse.persistence.platform.database.oracle.dcn.OracleChangeNotificationListener) listener class. This listener integrates with Oracle JDBC to receive database change events. To enable the listener, specify the full path to the OracleChangeNotificationListener class as the value of the eclipselink.cache.database-event-listener property in the persistence.xml file.

By default, all entities in the domain are registered for change notification. However, you can selectively disable change notification for certain classes by tagging them in the Java files with the databaseChangeNotificationType (org.eclipse.persistence.annotations.DatabaseChangeNotificationType) attribute of the Cache annotation. The value of this attribute determines the type of database change notification an entity should use. The default value of the databaseChangeNotificationType attribute is Invalidate. To disable change notification for a class, set the value of the attribute to None.

The databaseChangeNotificationType attribute is relevant only if the persistence unit has been configured with a database event listener, such as the OracleChangeNotificationListener class, that receives database change events. This allows the EclipseLink cache to be invalidated or updated from database changes.

Oracle strongly suggests that you use optimistic locking (writes on stale data will fail and automatically invalidate the cache) in your transactions. If you include an @Version annotation in your entity, then the version column in the primary table will always be updated, and the object will always be invalidated.

22.2 Implementing the Solution

This section contains the following tasks to enable shared caching in a JPA environment:

22.2.1 Task 1: Set up the Database and Tables

The solution presumes that you are working with an Oracle 11gR2 (11.2) or higher database that contains the tables that you are interested in.

22.2.2 Task 2: Grant User Permissions

Among other permissions, the database user must be granted the CHANGE NOTIFICATION privilege. To do this, you must have a DBA privilege, such as SYS, or have your database administrator apply it:

grant change notification to user

The following example illustrates granting the change notification privilege to user SCOTT.

...
define user="SCOTT"
define pass="tiger"
grant create session, alter session to &&user
/
grant resource, connect to &&user                               
/
grant select any dictionary to &&user
/
grant select any table to &&user
/
grant change notification to &&user
/
...

22.2.3 Task 3: Set the Classpath

Ensure that the eclipselink.jar EclipseLink library, the ojdbc6.jar JDBC library, the persistence.jar JPA library, and the domain classes are present on the classpath.

22.2.4 Task 4: Identify Classes that will Participate in Change Notification

By default, all entities in the domain will participate in change notification. There are several different ways to limit the entities that will participate. For example, the entity classes can be indicated by the <entity class ...> element in the orm.xml file, indicated with the <exclude-unlisted-classes> element in the persistence.xml file, or contained in a JAR file.

Note:

The <exclude-unlisted-classes> element is not intended for use in the Java SE environment.

Entity classes can also be excluded by using a Cache annotation attribute in the Java files. For more information, see Section 22.2.6.2, "Exclude Classes from Change Notification (Optional)."

Another way to identify the entity classes is to use the <class> element in the persistence.xml file. The following example indicates that the Order, OrderLine, and Customer classes in the model package will participate in change notification. For an example of a complete persistence.xml file, see Example 22-1.

...
<class>model.Order</class>
<class>model.OrderLine</class>
<class>model.Customer</class>
...

22.2.5 Task 5: Add the Database Event Listener

Use the eclipselink.cache.database-event-listener property to identify the database event listener. The org.eclipse.persistence.platform.database.oracle.dcn.OracleChangeNotificationListener class is the listener for EclipseLink Database Change Notification. This allows the EclipseLink cache to be invalidated by database events.

The following example illustrates the eclipselink.cache.database-event-listener property configured with the OracleChangeNotificationListener class. For an example of a complete persistence.xml file, see Example 22-1.

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

Note that you can also use:

<property name="eclipselink.cache.database-event-listener" value="DCN"> 

Example 22-1 illustrates an example of a complete persistence.xml file. The classes that will participate in change notification are the Order, OrderLine, and Customer classes from the model package. The eclipselink.cache.database-event-listener property is set to the full path of the OracleChangeNotificationListener class.

Note:

A <provider> tag is optional if running in a container where EclipseLink is the default provider.

Example 22-1 Sample persistence.xml File

<?xml version="1.0" encoding="UTF-8"?>
<persistence 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 persistence_2_0.xsd"
                version="2.0">
    <persistence-unit name="acme" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <class>model.Order</class>
        <class>model.OrderLine</class>
        <class>model.Customer</class>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="eclipselink.cache.database-event-listener" value="DCN"/>
        </properties>
    </persistence-unit>
</persistence>

22.2.6 Task 6: Edit the Java Files

Typically, to participate in change notification, no changes are needed to the Java classes which correspond to database tables. However, setting optimistic locking with the @Version annotation is strongly suggested.

If you want to exclude classes that are listed in the persistence unit, you can tag them in the Java files. EclipseLink tracks changes only to the primary table. If you want changes to secondary tables to also be tracked, you can indicate this in the Java files.

22.2.6.1 Set Optimistic Locking

Oracle strongly suggests that you use optimistic locking: writes on stale data will fail and automatically invalidate the cache. Include an @Version annotation in your entity; the version column in the primary table will always be updated, and the older version of the object will always be invalidated.

In Example 22-2 the @Version annotation is defined for the entity Customer. Note that getters and setters are defined for the version variable.

Example 22-2 Defining the @Version Annotation

...
@Entity
@Table(name="DBE_CUSTOMER")
public class Customer implements Serializable {
    @Id
    @GeneratedValue(generator="CUST_SEQ")
    @TableGenerator(name="CUST_SEQ")
    @Column(name="CUST_NUMBER")
    private long id;

 @Version
    private long version;
 
 ...
    public long getVersion() {
        return version;
    }
 
    public void setVersion(long version) {
        this.version = version;
    }
...

22.2.6.2 Exclude Classes from Change Notification (Optional)

Use the databaseChangeNotificationType attribute of the Cache annotation to identify the classes for which you do not want change notifications. To exclude a class from change notification, set the attribute to DatabaseChangeNotificationType.NONE, as illustrated in the following example.

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

22.2.6.3 Track Changes in Secondary Tables (Optional)

EclipseLink tracks changes only to the primary table. If any updates occur in a secondary table, EclipseLink will not invalidate the object. If you want changes to secondary tables to be tracked as well, add the @Version annotation to the entity.

Oracle DCN listens only for events from the primary table. It does not track changes in secondary tables, or relationships tables. The reason for this is that Oracle DCN only tracks the ROWID, so there is no correlation from the ROWID of the primary, secondary and relationship tables. Thus, to receive events when a secondary or relationship table changes, the version in the primary table must change so that the event is returned.

22.3 Limitations on the Solution

EclipseLink Database Change Notification has the following limitations:

  • Changes to an object's secondary tables will not trigger it to be invalidate unless a @Version annotation is used and updated in the primary table.

  • Changes to an object's OneToMany, ManyToMany, and ElementCollection relationships will not trigger it to be invalidate, unless an @Version annotation is used and updated in the primary table.

22.4 Additional Resources

See the following resources for more information about the technologies and tools used to implement the solutions in this chapter: