QueryResultsCache (interface)

Provides a mechanism by which idempotent query results can be cached for the duration of an interaction.

Caching such values is useful to improve the response time (for the end user) of code that loops "naively" through a set of items, performing an expensive operation each time. If the data is such that the same expensive operation is made many times, then the query cache is a perfect fit.

API

QueryResultsCache.java
interface QueryResultsCache {
  T execute(Callable<T> callable, Class<?> callingClass, String methodName, Object... keys)     (1)
  void onTransactionEnded()     (2)
  R execute(MethodReferences.Call0<? extends R> action, Class<?> callingClass, String methodName)
  R execute(MethodReferences.Call1<? extends R, A0> action, Class<?> callingClass, String methodName, A0 arg0)
  R execute(MethodReferences.Call2<? extends R, A0, A1> action, Class<?> callingClass, String methodName, A0 arg0, A1 arg1)
  R execute(MethodReferences.Call3<? extends R, A0, A1, A2> action, Class<?> callingClass, String methodName, A0 arg0, A1 arg1, A2 arg2)
  R execute(MethodReferences.Call4<? extends R, A0, A1, A2, A3> action, Class<?> callingClass, String methodName, A0 arg0, A1 arg1, A2 arg2, A3 arg3)
  R execute(MethodReferences.Call5<? extends R, A0, A1, A2, A3, A4> action, Class<?> callingClass, String methodName, A0 arg0, A1 arg1, A2 arg2, A3 arg3, A4 arg4)
}
1 execute(Callable, Class, String, Object)

Executes the callable if not already cached for the supplied calling class, method and keys.

2 onTransactionEnded()

Not API, for framework use only.

Members

execute(Callable, Class, String, Object)

Executes the callable if not already cached for the supplied calling class, method and keys.

onTransactionEnded()

Not API, for framework use only.

Implementation

The framework provides a default implementation of o.a.i.core.runtimeservices.queryresultscache.QueryResultsCacheDefault.

Usage

Suppose that there’s a TaxService that calculates tax on Taxable items, with respect to some TaxType, and for a given LocalDate. To calculate tax it must run a database query and then perform some additional calculations.

Our original implementation is:

@DomainService
public class TaxService {
    public BigDecimal calculateTax(
            final Taxable t, final TaxType tt, final LocalDate d) {
        // query against DB using t, tt, d
        // further expensive calculations
    }
}

Suppose now that this service is called in a loop, for example iterating over a bunch of orders, where several of those orders are for the same taxable products, say. In this case the result of the calculation would always be the same for any given product.

We can therefore refactor the method to use the query cache as follows:

public class TaxService {
    public BigDecimal calculateTax(
            final Taxable t, final TaxType tt, final LocalDate d) {
        return queryResultsCache.execute(
            new Callable<BigDecimal>(){                         (1)
                public BigDecimal call() throws Exception {
                     // query against DB using t, tt, d
                     // further expensive calculations
                }
            },
            TaxService.class,                                   (2)
            "calculateTax",
            t, tt, d);
        }
}
1 the Callable is the original code
2 the remaining parameters in essence uniquely identify the method call.

This refactoring will be worthwhile provided that enough of the orders being processed reference the same taxable products. If however every order is for a different product, then no benefit will be gained from the refactoring.

The Scratchpad service is also intended for actions that are called many times, allowing arbitrary information to be shared between them.