QueryResultsCache
This service (API and implementation) provides a mechanism by which idempotent query results can be cached for the duration of an org.apache.causeway.applib.services.iactn.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.
For example, code that makes a repository call many times within a loop can be performance tuned using this service. The benefit is that the algorithm of the business logic can remain easy to understand.
API
class QueryResultsCache {
T execute(Callable<T> callable, Class<?> callingClass, String methodName, Object... keys) (1)
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)
void onTransactionEnded() (2)
void destroy()
}
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 to call at end of transaction, to clear out the cache. |
Members
Implementation
The framework provides a default implementation of o.a.c.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.
Related Services
The Scratchpad service is also intended for actions that are called many times, allowing arbitrary information to be shared between them.
Note that the scope of QueryResultsCache is for a single interaction, so is short-lived.
If you want longer-lived caching, then you may be able to use Spring’s cache support, such as @Cacheable
.
For more details, check out the relevant Spring Boot and Spring Framework documentation on the topic.