问题
I'm developing a WEB APP under JBoss EAP 7.2 (EE8) using EhCache as JCache implementation (JSR 107)
I have this code:
@Inject
CacheManager cacheManager;
@CacheResult(cacheName = "roles")
public List<RoleDTO> get(@CacheKey String id) {
return service.getRoles(id);
}
public List<RoleDTO> getRoles(final String userId) {
final List<RoleDTO> output = get(userId);
return output;
}
According to this article I read, I'd expect the second time I access the get(userId) method should use cache and ignore entering the method, but is not so. I also queries injected cacheManager, and "roles" cache is always empty.
Where am I wrong ?
EDIT
I guess EhCache 3.x doesn't support jcache annotations in a standard EE environment, on the web I can only see other CDI implementations like ri o guice. However, by including one of these implementations, for example:
<dependency>
<groupId>org.jsr107.ri</groupId>
<artifactId>cache-annotations-ri-cdi</artifactId>
</dependency>
seems the new cache entry is put onto a new cachemanager (see the below code)
Caching.getCachingProvider().getCacheManager
instead of the injected and configured CacheManager I already have
EDIT EDIT
@CacheResult(cacheName = "roles", cacheResolverFactory = AppCacheResolverFactory.class, cacheKeyGenerator = CacheKeyGeneratorFactory.class)
public List<RoleDTO> getRoles(@CacheKey final String userId) {
This is my CacheResolverFactory class
public class AppCacheResolverFactory implements CacheResolverFactory {
@Inject
CacheManager cacheManager;
@Override
public CacheResolver getCacheResolver(CacheMethodDetails<? extends Annotation> cacheMethodDetails) {
return new DefaultCacheResolver(cacheManager.getCache(cacheMethodDetails.getCacheName()));
}
@Override
public CacheResolver getExceptionCacheResolver(CacheMethodDetails<CacheResult> cacheMethodDetails) {
return null;
}
}
Now I get this:
java.lang.ClassCastException: Invalid key type, expected : java.lang.String but was : org.jsr107.ri.annotations.DefaultGeneratedCacheKey
EDIT EDIT EDIT
I've created a CacheKeyGeneratorFactory and a StringCacheKey that wraps a String value:
CacheKeyGeneratorFactory
public class CacheKeyGeneratorFactory implements CacheKeyGenerator {
@Override
public GeneratedCacheKey generateCacheKey(CacheKeyInvocationContext<? extends Annotation> cacheKeyInvocationContext) {
final CacheInvocationParameter[] allParameters = cacheKeyInvocationContext.getAllParameters();
for (CacheInvocationParameter parameter : allParameters) {
if (StringCacheKey.class.equals(parameter.getRawType())) {
return (StringCacheKey)parameter.getValue();
}
}
return null;
}
}
StringCacheKey
public class StringCacheKey implements GeneratedCacheKey {
private String value;
public StringCacheKey(String param) {
this.value = param;
}
@Override
public int hashCode() {
return this.value.hashCode();
}
@Override
public boolean equals(Object obj) {
return obj != null && obj.getClass() == this.getClass() && this.value.equals(((StringCacheKey)obj).getValue());
}
public String getValue() {
return value;
}
}
Then I changed ehcache.xml configuration with this:
<cache alias="roles">
<key-type>mypackage.StringCacheKey</key-type>
<value-type>java.util.List</value-type>
And the method with @CacheResult too
public List<RoleDTO> get(@CacheKey String id)
Now it goes, however I wonder I can save all this bunch of code and class to make it work :/
回答1:
The RI wraps keys by default into DefaultGeneratedCacheKey
. You need to change your EHCache configuration to allow Object
types for the key. Alternatively you can register your own key generator and use your own key type. You can of course also specify org.jsr107.ri.annotations.DefaultGeneratedCacheKey
in your EHCache configuration, however, then you bind to a specific implementation.
Unfortunately, the JSR107/JCache standard for annotations requires that a cache key implements the interface GeneratedCacheKey
. That means, if you have a simple cache key like int or string, these always need to get wrapped, when stored into the cache.
Side note: There is no technical need for the interface GeneratedCacheKey
, since it does not define any new methods. This is a known problem in the standard. Unfortunately, there was no sufficient vetting of the annotations part before the standard got finalized.
来源:https://stackoverflow.com/questions/57823410/ee8-jcache-annotation-cacheresult-doesnt-work