I have a problem implementing a secong cache managers. At the moment I'm using EhCache, which is working fine. Additionally I would like to implement Java Simple Cache.
My CacheConfiguration looks like this:
CacheConfiguration.java
@Configuration
@EnableCaching
@AutoConfigureAfter(value = { MetricsConfiguration.class })
@AutoConfigureBefore(value = { WebConfigurer.class, DatabaseConfiguration.class })
public class CacheConfiguration {
private final javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration;
public CacheConfiguration(JHipsterProperties jHipsterProperties) {
JHipsterProperties.Cache.Ehcache ehcache =
jHipsterProperties.getCache().getEhcache();
jcacheConfiguration = Eh107Configuration.fromEhcacheCacheConfiguration(
CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class,
ResourcePoolsBuilder.heap(ehcache.getMaxEntries()))
.withExpiry(Expirations.timeToLiveExpiration(Duration.of(ehcache.getTimeToLiveSeconds(), TimeUnit.SECONDS)))
.build());
}
/**
* EhCache configuration
*
* @return
*/
@Bean
@Primary
public JCacheManagerCustomizer cacheManagerCustomizer() {
return cm -> {
cm.createCache(com.david.coinlender.domain.News.class.getName(), jcacheConfiguration);
// ...More caches
}
/**
* Java Simple Cache configuration
* @return
*/
@Bean
@Qualifier("simpleCacheManager")
public CacheManager simpleCacheManager() {
SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
simpleCacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("bitfinexAuthCache")));
return simpleCacheManager;
}
}
With Simple Cache I want to cache objects. I.e.:
@Cacheable(cacheManager = "simpleCacheManager", cacheNames = "bitfinexAuthCache", key = "#apiKey.apiKey")
private Exchange createBitfinexAuthenticatedExchange(ApiKeys apiKey) {
ExchangeSpecification exSpec = new BitfinexExchange().getDefaultExchangeSpecification();
exSpec.setApiKey(apiKey.getApiKey());
exSpec.setSecretKey(apiKey.getSecret());
Exchange bfx = ExchangeFactory.INSTANCE.createExchange(BitfinexExchange.class.getName());
bfx.applySpecification(exSpec);
return bfx;
}
However, on Server startup liquibase gives me an error stating:
Caused by: java.lang.IllegalStateException: All Hibernate caches should be created upfront. Please update CacheConfiguration.java to add com.david.coinlender.domain.News
at io.github.jhipster.config.jcache.NoDefaultJCacheRegionFactory.createCache(NoDefaultJCacheRegionFactory.java:37)
at org.hibernate.cache.jcache.JCacheRegionFactory.getOrCreateCache(JCacheRegionFactory.java:190)
at org.hibernate.cache.jcache.JCacheRegionFactory.buildEntityRegion(JCacheRegionFactory.java:113)
at org.hibernate.cache.spi.RegionFactory.buildEntityRegion(RegionFactory.java:132)
at org.hibernate.internal.CacheImpl.determineEntityRegionAccessStrategy(CacheImpl.java:439)
at org.hibernate.metamodel.internal.MetamodelImpl.initialize(MetamodelImpl.java:120)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:297)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:445)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:889)
... 25 common frames omitted
I'm using the Jhipster framework for my appication. I googled this issue for hours now and haven't found a solution yet.
Is this error due to wrong configuration? Can someone please point me in the right direction?
In JHipster (I did the code), there is in fact two layers of cache. You have Spring Cache and Hibernate Second Level Cache. Both are using the same actual Ehcache CacheManager
.
In your case, you've replaced Ehcache with a simple cache for Spring. But since the NoDefaultJCacheRegionFactory
is still configured on Hibernate, it is still Ehcache that is used for Hibernate. But the customizer isn't used anymore. So it fails.
You would like to have a simple cache for Spring and Ehcache for Hibernate. This means that in fact to don't need to have bean registered for Ehcache in Spring.
The easiest is then to do the following.
First, configure Ehcache in DatabaseConfiguration
. So when the hibernate JCache factory will retrieve it, it will be correctly configured.
public DatabaseConfiguration(Environment env, JHipsterProperties jHipsterProperties) {
this.env = env;
JHipsterProperties.Cache.Ehcache ehcache =
jHipsterProperties.getCache().getEhcache();
CachingProvider provider = Caching.getCachingProvider();
javax.cache.CacheManager cacheManager = provider.getCacheManager();
javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration = Eh107Configuration.fromEhcacheCacheConfiguration(
CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class,
ResourcePoolsBuilder.heap(ehcache.getMaxEntries()))
.withExpiry(Expirations.timeToLiveExpiration(Duration.of(ehcache.getTimeToLiveSeconds(), TimeUnit.SECONDS)))
.build());
cacheManager.createCache(com.mycompany.myapp.domain.User.class.getName(), jcacheConfiguration);
cacheManager.createCache(com.mycompany.myapp.domain.Authority.class.getName(), jcacheConfiguration);
cacheManager.createCache(com.mycompany.myapp.domain.User.class.getName() + ".authorities", jcacheConfiguration);
}
Then, configure Spring Cache.
public class CacheConfiguration {
public CacheConfiguration() {
}
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
Collection<Cache> caches = Arrays.asList(
new ConcurrentMapCache("mycache")
// ...
);
cacheManager.setCaches(caches);
return cacheManager;
}
}
@EnableCaching Doc
For those that wish to establish a more direct relationship between @EnableCaching and the exact cache manager bean to be used, the CachingConfigurer callback interface may be implemented. Notice the @Override-annotated methods below:
There is an additional alternative using CachingConfigurer
and taking the approach that is implemented on JCacheCacheConfiguration.
1- Implements the CachingConfigurer
interface in CacheConfiguration
class. One option is extends from JCacheConfigurerSupport
.
public class CustomCacheConfiguration extends JCacheConfigurerSupport {
2- Create the default cacheManager
bean, since the bean is configured through CachingConfigurer
it will be retrieved as a default cacheManager
and in this case it can be used by hibernate second level cache, for this approach the cacheManagerCustomizer
will be only a method. Like this :
@Bean
@Override
public CacheManager cacheManager() {
javax.cache.CacheManager jCacheCacheManager = createCacheManager();
cacheManagerCustomizer().customize(jCacheCacheManager);
return new JCacheCacheManager(jCacheCacheManager);
}
private javax.cache.CacheManager createCacheManager() {
CachingProvider cachingProvider = Caching.getCachingProvider();
return cachingProvider.getCacheManager();
}
private JCacheManagerCustomizer cacheManagerCustomizer() {
return cm -> {
cm.createCache(org.demo.app.domain.User.class.getName(), jcacheConfiguration);
......
};
}
3- Create the simpleCacheManager
bean
@Bean("simpleCacheManager")
public SimpleCacheManager simpleCacheManager() {
SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
simpleCacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("bitfinexAuthCache")));
return simpleCacheManager;
}
- In order to define which
cacheManager
will be used then define it through @Cacheable(cacheManager = "simpleCacheManager", cacheNames = "bitfinexAuthCache")
Here is the complete customization of CacheConfiguration
.
@Configuration
@EnableCaching
@AutoConfigureAfter(value = { MetricsConfiguration.class })
@AutoConfigureBefore(value = { WebConfigurer.class, DatabaseConfiguration.class})
public class CacheConfiguration extends JCacheConfigurerSupport {
private final javax.cache.configuration.Configuration<Object, Object> jcacheConfiguration;
public CustomCacheConfiguration(JHipsterProperties jHipsterProperties) {
JHipsterProperties.Cache.Ehcache ehcache =
jHipsterProperties.getCache().getEhcache();
jcacheConfiguration = Eh107Configuration.fromEhcacheCacheConfiguration(
CacheConfigurationBuilder.newCacheConfigurationBuilder(Object.class, Object.class,
ResourcePoolsBuilder.heap(ehcache.getMaxEntries()))
.withExpiry(Expirations.timeToLiveExpiration(Duration.of(ehcache.getTimeToLiveSeconds(), TimeUnit.SECONDS)))
.build());
}
@Bean
@Override
public CacheManager cacheManager() {
javax.cache.CacheManager jCacheCacheManager = createCacheManager();
cacheManagerCustomizer().customize(jCacheCacheManager);
return new JCacheCacheManager(jCacheCacheManager);
}
private javax.cache.CacheManager createCacheManager() {
CachingProvider cachingProvider = Caching.getCachingProvider();
return cachingProvider.getCacheManager();
}
private JCacheManagerCustomizer cacheManagerCustomizer() {
return cm -> {
cm.createCache(org.demo.app.domain.User.class.getName(), jcacheConfiguration);
cm.createCache(org.demo.app.domain.Authority.class.getName(), jcacheConfiguration);
cm.createCache(org.demo.app.domain.User.class.getName() + ".authorities", jcacheConfiguration);
cm.createCache(org.demo.app.domain.Company.class.getName(), jcacheConfiguration);
//cm.createCache(org.demo.app.domain.News.class.getName(), jcacheConfiguration);
};
}
@Bean("simpleCacheManager")
public SimpleCacheManager simpleCacheManager() {
SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
simpleCacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("bitfinexAuthCache")));
return simpleCacheManager;
}
}