Friday, April 16, 2010

CheckCacheThenDatabase in TopLink Grid

EclipseLink JPA has a number of useful query hints that allow you to query the cache rather than, or before, the database. This is useful because if you know you've warmed up your cache you can execute queries for objects and yet not have to query the database.

As of TopLink 11gR1 (11.1.1.2.0) TopLink Grid doesn't support these hints but it's only a problem for the 'Grid Cache' configuration in which Coherence is used as a shared cache replacement. In Grid Cache, only primary key queries are sent to Coherence and all other queries go to the database. In the 'Grid Read' and 'Grid Entity' configurations all read queries are directed to Coherence.

I was recently asked how to get a query to CheckCacheThenDatabase with Grid Cache and came up with the GridCacheQueryHelper utility class below. It provides the static method checkCacheThenDatabase which will adjust an EclipseLink JPA query so that it queries Coherence (if the query can be translated to a Filter) and if no results are found in Coherence then it queries the database.

Configuring a query would look something like:

        Query query = em.createQuery("select e from Employee e");
        GridCacheQueryHelper.checkCacheThenDatabase(query);
        List<Employee> employees = query.getResultList();


NOTE: this code is based on how TopLink Grid 11.1.1.2.0 is implemented and may not work with future releases.

--Shaun

/**
 * Utility class that provides implementations of EclipseLink 
 * query hints for use with TopLink Grid 'Grid Cache' Configuration.
 * 
 * 
@author shsmith
 *
 */

class GridCacheQueryHelper {
    
    
public static void checkCacheThenDatabase(Query jpqlQuery) {
        JpaHelper
            .getDatabaseQuery(jpqlQuery)
            .setRedirector(
new CoherenceCheckCacheThenDatabase());
    }
    
    
private static class CoherenceCheckCacheThenDatabase implements QueryRedirector {
        
        
public Object invokeQuery(DatabaseQuery query, Record arguments,
                Session session) {
            
// Create appropriate Coherence Query Redirector
            QueryRedirector redirector = null;
            
if (query.isReadObjectQuery()) {
                redirector = 
new ReadObjectFromCoherence();
            } 
else if (query.isReadAllQuery()) {
                redirector = 
new ReadAllFromCoherence();                
            } 
else { 
                
throw new RuntimeException("CheckCacheThenDatabase only supported on ReadObject and ReadAll Queries.");
            }
            
// If nothing returned from Coherence then query database.
            // But only if the query was only run against Coherence 
            // and no results were found.
            List results = (List)redirector.invokeQuery(query, arguments, session);
            
if ((results.isEmpty()) && (query.getDoNotRedirect())) {
                
return query.execute((AbstractSession)session, (AbstractRecord) arguments);
            } 
else {
                
return results;
            }
        }
    }

}