2

I have two Java 1.7 applications using Eclipselink 2.4.2 for persistence. One application is a JEE application running in Glassfish 3.1.2.2 and the other is a Java SE application. These applications read and write the same data, so there is the possibility of stale JPA cache entries. I am attempting to use Oracle DCN to solve the stale cache problem.

I have configured my persistence.xml as described in the link above:

<?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
             http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
  <persistence-unit name="drms-persistence-unit" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>jdbc/DRMS</jta-data-source>
    <properties>
      <property name="eclipselink.cache.database-event-listener"
                value="org.eclipse.persistence.platform.database.oracle.dcn.OracleChangeNotificationListener"/>
      <property name="eclipselink.target-server" value="SunAS9"/>
      <property name="eclipselink.target-database" value="Oracle"/>
      <property name="eclipselink.logging.level" value="INFO"/>
      <property name="eclipselink.logging.parameters" value="true"/>
      <property name="eclipselink.jdbc.native-sql" value="true"/>
      <property name="eclipselink.jdbc.batch-writing" value="Oracle-JDBC"/>
      <property name="eclipselink.jdbc.cache-statements" value="true"/>
      <property name="eclipselink.jdbc.cache-statements.size" value="200"/>
    </properties>
  </persistence-unit>
</persistence>

And yet, I still get stale cache entries. If one application commits a change to the database, the second application still sees its old stale cache entry, rather than the new change. Using a debugger, I have confirmed that the OracleChangeNotificationListener is receiving database events, but it never seems to actually invalidate anything in the cache.

The Eclipselink OracleChangeNotificationListener registers the following listener to receive Oracle database change events:

public void onDatabaseChangeNotification(DatabaseChangeEvent changeEvent) {
    databaseSession.log(SessionLog.FINEST, SessionLog.CONNECTION, "dcn_change_event", changeEvent);
    if (changeEvent.getTableChangeDescription() != null) {
        for (TableChangeDescription tableChange : changeEvent.getTableChangeDescription()) {
            ClassDescriptor descriptor = OracleChangeNotificationListener.this.descriptorsByTable.get(new DatabaseTable(tableChange.getTableName()));
            if (descriptor != null) {
                CacheIndex index = descriptor.getCachePolicy().getCacheIndex(fields);                                
                for (RowChangeDescription rowChange : tableChange.getRowChangeDescription()) {
                    CacheId id = new CacheId(new Object[]{rowChange.getRowid().stringValue()});
                    CacheKey key = databaseSession.getIdentityMapAccessorInstance().getIdentityMapManager().getCacheKeyByIndex(
                            index, id, true, descriptor);
                    if (key != null) {
                        if ((key.getTransactionId() == null) || !key.getTransactionId().equals(changeEvent.getTransactionId(true))) {
                            databaseSession.log(SessionLog.FINEST, SessionLog.CONNECTION, "dcn_invalidate", key.getKey(), descriptor.getJavaClass().getName());
                            key.setInvalidationState(CacheKey.CACHE_KEY_INVALID);
                        }
                    }
                }
            }
        }
    }
}

This listener does get invoked anytime we make a change to the database. However, the CacheKey value that gets looked up is always null, and so the invalidation code never runs.

If I inspect the IdentityMapManager object in the debugger, I can see that the expected entity is in the cache. And yet the look up by CacheId fails every time (returns null).

Any help is appreciated.

Jeff
  • 227
  • 4
  • 17
  • Can you provide more info, such as how you are verifying the entity is stale? Invalidation applies to the second level cache, but not to the EntityManager cache as this is isolated like a unit of work. If keeping and reusing EMs for instance, make sure you call clear at logical points – Chris Mar 03 '14 at 20:20
  • I make a change to an entity in one application, and verify that it gets committed to the database. Then I retrieve that same entity in other application. The second application doesn't actually hit the database, and the data displayed is the old, pre-update values of the entity. – Jeff Mar 03 '14 at 20:33

1 Answers1

6

Problem has been resolved. It was caused by the value of the "eclipselink.target-database" property in persistence.xml. We had it set to "Oracle", and changing it to "Oracle11" solved the issue. Apparently DCN capability was not added until Oracle version 11, and Eclipselink is behaving differently depending on that value.

Jeff
  • 227
  • 4
  • 17