JCache is the standard API for cache libraries. One common need is to
link the cache with another data source like a database (RDBMS or
NoSQL).
Of course JCache handles both access types: reader and writer.
This post is about readers.
To define the link between your datasource and the cache you need to
define a javax.cache.integration.CacheLoader. It is configured in the
cache configuration. If you use default cache configuration
implementation provided by the API you use:
CompleteConfiguration<MyKey, MyValue> configuration = new MutableConfiguration<MyKey, MyValue>() .setStoreByValue(false) // avoid local serialization .setReadThrough(true) // use CacheLoader when an entry is missing .setCacheLoaderFactory( new FactoryBuilder.SingletonFactory<CacheLoader<MyKey, MyValue>>( new CacheLoaderImpl() ) );
Configuration is quite simple:
- the cache is configured not stored by value to avoid to
serialize key/values locally each time - we set the cache to be read through which means the CacheLoader
will be asked when an entry is not found - we set a singleton CacheLoader to be used
Once you have the configuration just create the cache:
Cache<MyKey, MyValue> cache = manager.createCache("my-values", configuration);
Let say you have in the database an entry matching key “159875” and
the cache doesn’t have it yet, when you’ll do:
cache.get(new MyKey("159875"))
the cache will trigger the CacheLoader to find it and will put it in memory.
Note that the CacheLoader API defines two main methods:
V load(K key) throws CacheLoaderException; Map<K, V> loadAll(Iterable<? extends K> keys) throws CacheLoaderException;
First is the commonly used method and second one is the one used for
bulk loading (depending on the backing datasource it is or not
interesting).
For JPA a CacheLoader can look like:
import java.util.HashMap; import java.util.Map; import javax.cache.integration.CacheLoader; import javax.cache.integration.CacheLoaderException; import javax.persistence.EntityManager; public class JPACacheLoader<K, E> implements CacheLoader<K, E> { private final Class<E> entity; private final EntityManager em; public JPACacheLoader(final Class<E> entity, final EntityManager em) { this.entity = entity; this.em = em; } @Override public E load(final K key) throws CacheLoaderException { try { return em.find(entity, key); } catch (final Exception e) { return null; } } @Override public Map<K, E> loadAll(final Iterable<? extends K> keys) throws CacheLoaderException { // could use a bulk select but for the demo this is enough final Map<K, E> loaded = new HashMap<K, E>(); for (final K k : keys) { final E value = load(k); if (value != null) { loaded.put(k, value); } } return loaded; } }
Now that the JCache spec is finalzed, it is not clear to me if the CacheManager is supposed to be managed by the server or the application? (I would say server)
Do you know if the next version of the Java EE spec will include new elements in web.xml/application.xml like cache-manager-ref or even cache-ref?
Hi
well no idea how the spec will move on, I’d expect some integration with CDI. CacheManager is not managed by the container AFAIK since not needed now but component user can be integrated with CDI using a CDIFactory implementation (CacheLoader, CacheWriter….). Easiest would be using deltaspike BeanProvider I guess
In my mind, I would treat the CacheManager as some sort of external resource (like a DataSource/EntityManager which needs a DB, or a MailSession which needs some mail gateway) because it could need to store something outside of the application (serialization on a disk) and I don’t want my application to know too much about the folder structure of the machine it’s running on (I want the application server to abstract that part from the application).
My principle is the following: every external resource (DB, WS, queue, folder, URL, MailSession…) of my application should be managed (or at least configured) at the server-level (or in DDs) so that I don’t have to change my application if those resources change.
That’s the reason why I tend to configure logging at the server level (I don’t want the application to know too much about where to write its logs + I like to minimize toying with deployment descriptors as much as possible to avoid mistakes).
But when I see that nothing has been planned by experts to let caches be managed by the application server, I wonder if I my view on the subject isn’t too extremist. =)
Hi
Actually you are quite right but miss something: cache configuration you speak about is not in the spec. Caches are configured through an URI and properties. If I summarize a bit to make it clear the most portable solution will be to get the properties path (potentially external to the app) and create the cache from it. This way you still have the configuration outside the application but rely on a portable and not production details aware code.
Personally I would have expected a basic CDI integration like BVal did providing CacheManager and Cache directly as injectable beans but this is clearly not blocking since CDI beans are already integrated (@CacheResult etc…).
Note that in TomEE you can pretty quickly do what you describe too and use @Resource to get your cache or cache manager injected.
Think it is mainly a time frame issue and it will for sure be tackled for JavaEE 8.
Side note: this kind of request should be done on JCache jira 😉
Nice to see that TomEE allows to inject caches and cache managers as resources! I will have to look into it (one more reason to switch to TomEE I think ;-)).
But I suppose that, for the moment, caching is configured through proprietary TomEE means until declaration in web.xml/application.xml can be standardized like this:
or have the equivalent of persistence.xml for caching (cache.xml?)
and of course CDI integration.
Well here are few notes about it
1) TomEE allows almost all bean configuration as @Resource
2) Look back what you described and propose. If you put properties in web.xml then you can’t/shouldn’t change them when deploying which is clearly not what you want. So not having the config in the app is better and already supported since API uses properties you read as you want.
Well you are right, the following configuration should be better:
bind cache/CacheManager through server specific method (like ObjectFactory in Tomcat).
Reblogged this on Dinesh Ram Kali..