diff --git a/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/CacheConfig.java b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/CacheConfig.java new file mode 100644 index 0000000000..29419f4c12 --- /dev/null +++ b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/CacheConfig.java @@ -0,0 +1,90 @@ +package com.baeldung.caching.multicache; + +import com.github.benmanes.caffeine.cache.Caffeine; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.AnnotationCacheOperationSource; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.caffeine.CaffeineCache; +import org.springframework.cache.interceptor.CacheInterceptor; +import org.springframework.cache.interceptor.CacheOperationSource; +import org.springframework.cache.support.SimpleCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; + +import java.time.Duration; +import java.util.Arrays; + +@Configuration +@EnableCaching +public class CacheConfig { + + @Value("${spring.redis.host}") + private String redisHost; + + @Value("${spring.redis.port}") + private int redisPort; + + @Bean + public CaffeineCache caffeineCacheConfig() { + return new CaffeineCache("customerCache", Caffeine.newBuilder() + .expireAfterWrite(Duration.ofMinutes(1)) + .initialCapacity(1) + .maximumSize(2000) + .build()); + } + + @Bean + @Primary + public CacheManager caffeineCacheManager() { + SimpleCacheManager manager = new SimpleCacheManager(); + manager.setCaches(Arrays.asList( + caffeineCacheConfig())); + return manager; + } + + @Bean + public CacheManager redisCacheManager() { + return RedisCacheManager.RedisCacheManagerBuilder + .fromConnectionFactory(redisConnectionFactory()) + .withCacheConfiguration("customerCache", cacheConfiguration()) + .build(); + } + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); + redisStandaloneConfiguration.setHostName(redisHost); + redisStandaloneConfiguration.setPort(redisPort); + return new LettuceConnectionFactory(redisStandaloneConfiguration); + } + + @Bean + public RedisCacheConfiguration cacheConfiguration() { + return RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofMinutes(60)) + .disableCachingNullValues() + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); + } + + @Bean + public CacheInterceptor cacheInterceptor() { + CacheInterceptor interceptor = new CustomerCacheInterceptor(caffeineCacheManager()); + interceptor.setCacheOperationSources(cacheOperationSource()); + return interceptor; + } + + @Bean + public CacheOperationSource cacheOperationSource() { + return new AnnotationCacheOperationSource(); + } + +} \ No newline at end of file diff --git a/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/Customer.java b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/Customer.java new file mode 100644 index 0000000000..4632799a1c --- /dev/null +++ b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/Customer.java @@ -0,0 +1,21 @@ +package com.baeldung.caching.multicache; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Setter +public class Customer implements Serializable { + + private String id; + + private String name; + + private String email; +} \ No newline at end of file diff --git a/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/CustomerCacheInterceptor.java b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/CustomerCacheInterceptor.java new file mode 100644 index 0000000000..2ca1d2b2de --- /dev/null +++ b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/CustomerCacheInterceptor.java @@ -0,0 +1,30 @@ +package com.baeldung.caching.multicache; + +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.interceptor.CacheInterceptor; +import org.springframework.data.redis.cache.RedisCache; + +public class CustomerCacheInterceptor extends CacheInterceptor { + + private final CacheManager caffeineCacheManager; + + public CustomerCacheInterceptor(CacheManager caffeineCacheManager) { + this.caffeineCacheManager = caffeineCacheManager; + } + + @Override + protected Cache.ValueWrapper doGet(Cache cache, Object key) { + Cache.ValueWrapper existingCacheValue = super.doGet(cache, key); + + if (cache.getClass() == RedisCache.class) { + Cache caffeineCache = caffeineCacheManager.getCache(cache.getName()); + if (existingCacheValue != null && caffeineCache != null && caffeineCache.get(key) == null) { + caffeineCache.putIfAbsent(key, existingCacheValue.get()); + } + } + + return existingCacheValue; + } +} + diff --git a/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/CustomerController.java b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/CustomerController.java new file mode 100644 index 0000000000..d053754be8 --- /dev/null +++ b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/CustomerController.java @@ -0,0 +1,22 @@ +package com.baeldung.caching.multicache; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class CustomerController { + + private final CustomerService customerService; + + @Autowired + public CustomerController(CustomerService customerService) { + this.customerService = customerService; + } + + @GetMapping("/customer/{id}") + public Customer getCustomer(@PathVariable String id) { + return customerService.getCustomer(id); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/CustomerRepository.java b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/CustomerRepository.java new file mode 100644 index 0000000000..da08e299e5 --- /dev/null +++ b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/CustomerRepository.java @@ -0,0 +1,44 @@ +package com.baeldung.caching.multicache; + +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.util.HashMap; +import java.util.Map; + +@Service +public class CustomerRepository { + + private final Map customerMap = new HashMap<>(); + + public Customer getCustomerById(String id) { + return customerMap.get(id); + } + + @PostConstruct + private void setupCustomerRepo() { + Customer product1 = getCustomer("100001", "name1", "name1@mail.com"); + customerMap.put("100001", product1); + + Customer product2 = getCustomer("100002", "name2", "name2@mail.com"); + customerMap.put("100002", product2); + + Customer product3 = getCustomer("100003", "name3", "name3@mail.com"); + customerMap.put("100003", product3); + + Customer product4 = getCustomer("100004", "name4", "name4@mail.com"); + customerMap.put("100004", product4); + + Customer product5 = getCustomer("100005", "name5", "name5@mail.com"); + customerMap.put("100005", product5); + } + + private static Customer getCustomer(String id, String name, String email) { + Customer customer = new Customer(); + customer.setId(id); + customer.setName(name); + customer.setEmail(email); + + return customer; + } +} diff --git a/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/CustomerService.java b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/CustomerService.java new file mode 100644 index 0000000000..db24254d83 --- /dev/null +++ b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/CustomerService.java @@ -0,0 +1,25 @@ +package com.baeldung.caching.multicache; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.stereotype.Service; + +@Service +public class CustomerService { + + private final CustomerRepository customerRepository; + + @Autowired + public CustomerService(CustomerRepository customerRepository) { + this.customerRepository = customerRepository; + } + + @Caching(cacheable = { + @Cacheable(cacheNames = "customerCache", cacheManager = "caffeineCacheManager"), + @Cacheable(cacheNames = "customerCache", cacheManager = "redisCacheManager") + }) + public Customer getCustomer(String id) { + return customerRepository.getCustomerById(id); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/MultipleCachingApplication.java b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/MultipleCachingApplication.java new file mode 100644 index 0000000000..862a2993a0 --- /dev/null +++ b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/multicache/MultipleCachingApplication.java @@ -0,0 +1,12 @@ +package com.baeldung.caching.multicache; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class MultipleCachingApplication { + + public static void main(String[] args) { + SpringApplication.run(MultipleCachingApplication.class, args); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-caching-2/src/main/resources/application.properties b/spring-boot-modules/spring-caching-2/src/main/resources/application.properties index 38f3537d01..97e4f97fcb 100644 --- a/spring-boot-modules/spring-caching-2/src/main/resources/application.properties +++ b/spring-boot-modules/spring-caching-2/src/main/resources/application.properties @@ -9,5 +9,6 @@ spring.jpa.hibernate.ddl-auto=update #setting cache TTL caching.spring.hotelListTTL=43200 # Connection details -#spring.redis.host=localhost -#spring.redis.port=6379 +spring.redis.host=localhost +spring.redis.port=6379 +spring.main.allow-bean-definition-overriding=true