I try to create folders in the redis cache of my project.
I want something similar to what spring automatically do with spring session.
I have a lot of "DisplayItem" entries and I want to store them in a "displayitem" folder.
Here is my code :
#Cacheable(value = "displayItem", cacheManager = "longLifeCacheManager")
public DisplayItem getDisplayItem(String displayItemCode) {
// Do a lot of things
}
#Cacheable(cacheManager = "mediumLifeCacheManager", value = "preferences:userPreferences", key = "#zenithName")
public UserPreferences getUserPreferencesByZenithName(String zenithName) {
// Do something
}
If I replace the value by "foldername:displayItem", it create a folder, but only for the keys (like I did for the preferences in the screenshot).
I don't find how to store all the values in this same folder.
How can I do this ?
I finally found a solution.
In the configuration of the CacheManager, I need to set UserPrefix with True.
#Bean(name = "mediumLifeCacheManager")
public CacheManager mediumLifeCacheManager(RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
cacheManager.setDefaultExpiration(redisExpirationMedium);
cacheManager.setUsePrefix(true);
return cacheManager;
}
Now this works like I want.
Related
In spring boot, I want to take data from database and store it into a bean object. This needs to be done once (cache), and for further request bean object needs to be used, not to make database call again.
Example
/*
"DataFromDB" -> bean should have the values
*/
List<Users> uList = ApplicationContext.getBean("DataFromDB");
Is there any way to achieve this ?
Thank you
During your application boot, you can simply create a bean List<Users> uList and populate it with your required info.
Bean creation will happen once and whenever you want to reuse it, just get that bean. Spring will take care of the rest.
Somewhere in a config file, declare the bean:
#Component
public class InitialConfiguration {
#Bean
public List<Users> ulist() {
List<Users> uList = null;
// ulist = populate it from db
return uList;
}
}
Spring will create a ulist bean and store it. Now whenever you want to use it, you can simply autowire it into your variables:
#Service
public class SomeRandomClass {
#Autowire
List<Users> ulist;
public void performOperationOnUList() {
ulist.get(0); // use it
}
}
you can use a caching mechanism like ehcache
To add Ehcache to your application, here is the very basic approach you can follow.
Add Ehcache through your build tool. Here is an example for Gradle.
dependencies {
compile("org.hibernate:hibernate-ehcache:5.2.12.Final")
}
Add Ehcache configuration, Here I'm using annotation-based bean configuration.
#Configuration
#EnableCaching
public class CacheConfiguration {
#Bean
public EhCacheManagerFactoryBean ehCacheManagerFactory() {
EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();
cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
cacheManagerFactoryBean.setShared(true);
return cacheManagerFactoryBean;
}
#Bean
public EhCacheCacheManager ehCacheCacheManager() {
EhCacheCacheManager cacheManager = new EhCacheCacheManager();
cacheManager.setCacheManager(ehCacheManagerFactory().getObject());
cacheManager.setTransactionAware(true);
return cacheManager;
}
}
Define cache Regions. Here you can define individual caches for each repository you want to cache. create file named ehcache.xml and place in classpath.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ehcache>
<ehcache>
<diskStore path="java.io.tmpdir"/>
<cache name="usercache" maxElementsInMemory="100" eternal="false" timeToIdleSeconds="600" timeToLiveSeconds="3600" overflowToDisk="true"/>
</ehcache>
Add Cachable annotation to transactional methods where you want to cache the DB operation.
#Cacheable(value = "userCache", key = "#p0")
public Company find(Long id) {
//db operation in here
}
From your problem statement, what I can understand is you want to cache objects from database, caching should be done only once (preferably on application start-up) and should be accessible anywhere in the context.
For this, you can store the data from db in a static final collection. The caching operation can be done on application startup via EventListener annotation.
#Component
public class DbCache {
public static final List<Object> dbCache = new ArrayList<>();
#EventListener(value = ApplicationReadyEvent.class)
private void initCache() {
List<Object> dataFromDB = // data fetched from DB
dbCache.addAll(dataFromDB);
}
public static List<Object> getDbCache() {
return dbCache;
}
}
You can use the DbCache.getDbCache() anywhere in your code now to fetch the data.
I'm trying to configure Spring CacheManager with Hazelcast. Also, I want to configure Hazelcast's Near Cache so I can retrieve the (already deserialized) instance of my cached object.
Here is my configuration
#Bean
public HazelcastInstance hazelcastConfig() {
val config = new Config().setInstanceName("instance");
val serializationConfig = config.getSerializationConfig();
addCacheConfig(config, "USERS")
serializationConfig.addSerializerConfig(new SerializerConfig()
.setImplementation(getSerializer())
.setTypeClass(User.class)
return Hazelcast.newHazelcastInstance(config);
}
#Bean
public CacheManager cacheManager(HazelcastInstance hazelcastInstance) {
return new HazelcastCacheManager(hazelcastInstance);
}
#Bean
public PlatformTransactionManager chainedTransactionManager(PlatformTransactionManager jpaTransactionManager, HazelcastInstance hazelcastInstance) {
return new ChainedTransactionManager(
jpaTransactionManager,
new HazelcastTransactionManager(hazelcastInstance)
);
}
// Configure Near Cache
private void addCacheConfig(Config config, String cacheName) {
val nearCacheConfig = new NearCacheConfig()
.setInMemoryFormat(OBJECT)
.setCacheLocalEntries(true)
.setInvalidateOnChange(false)
.setTimeToLiveSeconds(hazelcastProperties.getTimeToLiveSeconds())
.setEvictionConfig(new EvictionConfig()
.setMaxSizePolicy(ENTRY_COUNT)
.setEvictionPolicy(EvictionPolicy.LRU)
.setSize(hazelcastProperties.getMaxEntriesSize()));
config.getMapConfig(cacheName)
.setInMemoryFormat(BINARY)
.setNearCacheConfig(nearCacheConfig);
}
Saving and retrieving from the Cache is working fine, but my object is deserialized every time I have a cache hit. I want to avoid this deserialization time using a NearCache, but it doesn´t work. I also tried BINARY memory format.
Is this possible with Hazelcast? Or is this deserialization always executed even if I have a NearCache?
Thanks
So after a few changes, it is working now. Here is my conclusion:
So in order to have NearCache working with Spring Cache, all your cached objects should be Immutable. This means final classes and final fields. Also, they all should extend the Serializable interface.
I am loading my properties file as following:
#Configuration
#PropertySource("classpath:app.properties")
class MyApp {
#Bean
public PropertySourcesPlaceholderConfigurer PropertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
In the properties file, I have several database-related properties:
database.dataSource.url=jdbc:postgresql://localhost:${db-port:5432}/mydb
database.dataSource.x=...
database.dataSource.y=...
database.dataSource.z=...
Note:
${db-port} should be replaced by either the value of property/environment variable db-port or 5432. In my case, I am defining the environment variable db-port when spawning the Tomcat container.
All database-related properties are grouped under database. root. This is intentional, see below.
I want to avoid that I have to enumerate/hardcode all possible database-related properties in my code. Luckily, the database layer in use (Hikari) has the nice feature that I can pass all properties via a java.util.Properties. So, I want retrieve all defined properties under database.* and simply forward it to Hikari.
For this, I wrote the following utility:
#Component
public class PropertyFetcher
{
#Autowired
private ConfigurableEnvironment environment;
public Properties get(final String key) {
final Properties p = new Properties();
for (final PropertySource<?> s : environment.getPropertySources()) {
if (s instanceof EnumerablePropertySource) {
for (final String k : ((EnumerablePropertySource) s).getPropertyNames()) {
if (k.startsWith(key) && k.length() > key.length()) {
p.put(k.substring(key.length()), s.getProperty(k));
}
}
}
}
return p;
}
}
Now, when calling get("database."), I have all database-related properties as defined in the properties file. Great! But, the value for property dataSource.url is now
jdbc:postgresql://localhost:${db-port:5432}/mydb
instead of
jdbc:postgresql://localhost:9876/mydb
So, for some reason, the ${db-port:5432} is not resolved (yet?) when going via this route (ConfigurableEnvironment).
How can this be fixed? Or is there a better way to get all properties under a certain root without having to enumerate/hardcode them into the code?
Please note that in the default scenario, the ${db-port:5432} in property database.dataSource.url=jdbc:postgresql://localhost:${db-port:5432}/mydb is correctly resolved. I tested this by defining the following member and logging it:
#Value("${database.dataSource.url}")
final String url; // holds jdbc:postgresql://localhost:9876/mydb
You should read the property values from real environment only. Then only you will get actual or effective value of a property.
This will require a little change in your code.
change this line:
p.put(k.substring(key.length()), s.getProperty(k));
to this:
p.put(k.substring(key.length()), environment.getProperty(k));
We are using Spring cache for Caching few elements. So whenever user requests same key of element, it goes to cache and check if it is available or not. If it is available it fetches from cache otherwise it executes the method. But before all this I want to implement one more functionality in my cache.
Requirement : On hourly basis my spring cache will check, if any element in the cache exists for more than an hour, it will remove it.
I searched on google but did not find any satisfactory link. Can someone help me or provide me a link for same ?
You need to set the time to live(TTL) for your cache. How you do this depends on your cash provider. A couple examples can be found here:
Can I set a TTL for #Cacheable
#EnableCaching
#Configuration
public class CacheConfiguration implements CachingConfigurer {
#Override
public CacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager() {
#Override
protected Cache createConcurrentMapCache(final String name) {
return new ConcurrentMapCache(name,
CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES).maximumSize(100).build().asMap(), false);
}
};
return cacheManager;
}
#Override
public KeyGenerator keyGenerator() {
return new DefaultKeyGenerator();
}
}
I have a problem when trying to get back a Guava cache from a cache manager, instead I get a Spring Cache.
This is the bean in my SpringConfig file :
#Bean
public CacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager() {
#Override
protected Cache createConcurrentMapCache(final String name) {
return new ConcurrentMapCache(name, CacheBuilder.newBuilder().expireAfterWrite(1440, TimeUnit.MINUTES)
.maximumSize(100).recordStats().build().asMap(), false); }
};
return cacheManager;
}
And then I'm able to use the #Cacheable :
#Cacheable(value = "myCache")
public void myCacheMethod(String key){
// call a web service
}
Everything works fine, but I can't get the cache Guava object created by the CacheBuilder in order to call the stats() method.
This is how I get the cache :
Cache myCache = cacheManager.getCache("myCache");
ValueWrapper wrapper = myCache.get("key");
WebServiceType myCachedObject= (WebServiceType) wrapper.get();
The last cache is a Spring cache, and I get an error if I cast it to Guava cache.
Is this possible ? Or did I do something wrong ?