0

I am trying to implement an oracle DCN with eclipselink 2.6 and Oracle 11.2 with the ojdbc6 driver.

Currently, through the setup, i am receiving correctly the table change notifications through my receiver.

I have extended the org.eclipse.persistence.platform.database.oracle.dcn.OracleChangeNotificationListener in order to fix several problems that i noticed on my project.

First of i have overridden the register method in order to unwrap correctly the connection :

OracleConnection connection = null;
        try {

            Connection unwrapConnection = databaseSession.getServerPlatform().unwrapConnection(accessor.getConnection());
            try {
                if (unwrapConnection.isWrapperFor(OracleConnection.class)) {
                    connection = unwrapConnection.unwrap(OracleConnection.class);
                } else {
                    // recover, not an oracle connection
                    connection = (OracleConnection) databaseSession.getServerPlatform().unwrapConnection(accessor.getConnection());
                }
            } catch (SQLException e) {
                e.printStackTrace();
                throw DatabaseException.sqlException(e, databaseSession.getAccessor(), databaseSession, false);
            }

I am also storing each table's object_id (tableNameIdMap) in order to correctly find the table from each notification.

Here is the DatabaseChangeListener method :

public void onDatabaseChangeNotification(final DatabaseChangeEvent changeEvent) {
        if (changeEvent.getTableChangeDescription() != null) {
            for (TableChangeDescription tableChange : changeEvent.getTableChangeDescription()) {
                String tableName = this.tableNameIdMap.get(tableChange.getObjectNumber());
                if (tableName != null)
                {
                    ClassDescriptor descriptor = DatabaseChangeNotificationListener.this.descriptorsByTable.get(new DatabaseTable(tableName));
                    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);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

The problem i am having is that whenever i receive a notification, i cannot get the cacheKey.

The method getIdentityMapManager().getCacheKeyByIndex(...) always returns null

Debugging through, it seems that the identityMaManager should return the cacheKey from an identityMap (a weakHashMap). The corresonding rowId is present but it is not returned.

Here is the line in the IdentityMapManager#getCacheKeyByIndex

CacheKey cacheKey = map.getCacheKey(indexValues, false);

It seems the CacheId's hashMap is based on the values hashcode. But when in look in the map, the value i should receive is indexed by a CacheId that contains a ROWID. Therefore, i cannot see how this should ever work...

I have tried to instantiate the cacheId using instead :

CacheId id = new CacheId(new Object[]{rowChange.getRowid(});

An it still does not work as the hashcode of a ROWID is the object's hashcode or so it seems.

Therefore i am stuck here. I don't understand how this can work elsewhere.

Any help is appreciated.

FYI, here is an extract of my persistence.xml

    <property name="eclipselink.target-database" value="Oracle11" />
    <property name="eclipselink.jdbc.native-sql" value="true" />
    <property name="eclipselink.jdbc.cache-statements" value="true" />
    <property name="eclipselink.query-results-cache" value="true" />
    <property name="eclipselink.cache.shared.default" value="true"/>
    <property name="eclipselink.refresh" value="true" />
    <property name="eclipselink.weaving" value="true"/>
    <property name="eclipselink.cache.database-event-listener" value="com.isel.persistence.database.DatabaseChangeNotificationListener" />
Laurent B
  • 2,200
  • 19
  • 29

1 Answers1

0

Found the answer :

The problem was about the same as this question.

I had indeed set up the eclipselink.target-database correctly in the persistence.xml, but it was overridden by the spring configuration :

<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
    <property name="showSql" value="false" />
    <property name="generateDdl" value="false" />
    <property name="databasePlatform" value="org.eclipse.persistence.platform.database.OraclePlatform" />
</bean>

which i then changed to <property name="databasePlatform" value="org.eclipse.persistence.platform.database.oracle.Oracle11Platform" />

Now, FYI, eclipselink stores rowIds as String in the CacheId keys which is done at : org.eclipse.persistence.platform.database.oracle.Oracle9Platform.getObjectFromResultSet(ResultSet, int, int, AbstractSession)

else if (type == OracleTypes.ROWID) {
  return resultSet.getString(columnNumber);
}
Community
  • 1
  • 1
Laurent B
  • 2,200
  • 19
  • 29