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.
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.