diff --git a/spring-caching/pom.xml b/spring-caching/pom.xml
index d33f24de1f..11dd9a8873 100644
--- a/spring-caching/pom.xml
+++ b/spring-caching/pom.xml
@@ -15,6 +15,10 @@
+
+ org.springframework.boot
+ spring-boot-starter-web
+
org.springframework
spring-context
@@ -40,6 +44,19 @@
spring-test
test
+
+ com.github.ben-manes.caffeine
+ caffeine
+
+
+ com.h2database
+ h2
+ runtime
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
diff --git a/spring-caching/src/main/java/com/baeldung/caching/boot/CacheApplication.java b/spring-caching/src/main/java/com/baeldung/caching/boot/CacheApplication.java
index 714dc443e0..afa1d0ec99 100644
--- a/spring-caching/src/main/java/com/baeldung/caching/boot/CacheApplication.java
+++ b/spring-caching/src/main/java/com/baeldung/caching/boot/CacheApplication.java
@@ -3,9 +3,13 @@ package com.baeldung.caching.boot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
+//import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@EnableCaching
+//to run any module like ehcache,caching or multiplecachemanager in local machine
+//add it's package for ComponentScan like below
+//@ComponentScan("com.baeldung.multiplecachemanager")
public class CacheApplication {
public static void main(String[] args) {
diff --git a/spring-caching/src/main/java/com/baeldung/multiplecachemanager/bo/CustomerDetailBO.java b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/bo/CustomerDetailBO.java
new file mode 100644
index 0000000000..3da4c23e28
--- /dev/null
+++ b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/bo/CustomerDetailBO.java
@@ -0,0 +1,28 @@
+package com.baeldung.multiplecachemanager.bo;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Component;
+
+import com.baeldung.multiplecachemanager.entity.Customer;
+import com.baeldung.multiplecachemanager.entity.Order;
+import com.baeldung.multiplecachemanager.repository.CustomerDetailRepository;
+
+@Component
+public class CustomerDetailBO {
+
+ @Autowired
+ private CustomerDetailRepository customerDetailRepository;
+
+ @Cacheable(cacheNames = "customers")
+ public Customer getCustomerDetail(Integer customerId) {
+ return customerDetailRepository.getCustomerDetail(customerId);
+ }
+
+ @Cacheable(cacheNames = "customerOrders", cacheManager = "alternateCacheManager")
+ public List getCustomerOrders(Integer customerId) {
+ return customerDetailRepository.getCustomerOrders(customerId);
+ }
+}
diff --git a/spring-caching/src/main/java/com/baeldung/multiplecachemanager/bo/OrderDetailBO.java b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/bo/OrderDetailBO.java
new file mode 100644
index 0000000000..390881167f
--- /dev/null
+++ b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/bo/OrderDetailBO.java
@@ -0,0 +1,25 @@
+package com.baeldung.multiplecachemanager.bo;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Component;
+
+import com.baeldung.multiplecachemanager.entity.Order;
+import com.baeldung.multiplecachemanager.repository.OrderDetailRepository;
+
+@Component
+public class OrderDetailBO {
+
+ @Autowired
+ private OrderDetailRepository orderDetailRepository;
+
+ @Cacheable(cacheNames = "orders", cacheResolver = "cacheResolver")
+ public Order getOrderDetail(Integer orderId) {
+ return orderDetailRepository.getOrderDetail(orderId);
+ }
+
+ @Cacheable(cacheNames = "orderprice", cacheResolver = "cacheResolver")
+ public double getOrderPrice(Integer orderId) {
+ return orderDetailRepository.getOrderPrice(orderId);
+ }
+}
diff --git a/spring-caching/src/main/java/com/baeldung/multiplecachemanager/config/MultipleCacheManagerConfig.java b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/config/MultipleCacheManagerConfig.java
new file mode 100644
index 0000000000..247a9b596b
--- /dev/null
+++ b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/config/MultipleCacheManagerConfig.java
@@ -0,0 +1,40 @@
+package com.baeldung.multiplecachemanager.config;
+
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.CachingConfigurerSupport;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cache.caffeine.CaffeineCacheManager;
+import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
+import org.springframework.cache.interceptor.CacheResolver;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+//import org.springframework.context.annotation.Primary;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+
+@Configuration
+@EnableCaching
+public class MultipleCacheManagerConfig extends CachingConfigurerSupport {
+
+ @Bean
+ //@Primary
+ public CacheManager cacheManager() {
+ CaffeineCacheManager cacheManager = new CaffeineCacheManager("customers", "orders");
+ cacheManager.setCaffeine(Caffeine.newBuilder()
+ .initialCapacity(200)
+ .maximumSize(500)
+ .weakKeys()
+ .recordStats());
+ return cacheManager;
+ }
+
+ @Bean
+ public CacheManager alternateCacheManager() {
+ return new ConcurrentMapCacheManager("customerOrders", "orderprice");
+ }
+
+ @Bean
+ public CacheResolver cacheResolver() {
+ return new MultipleCacheResolver(alternateCacheManager(), cacheManager());
+ }
+}
diff --git a/spring-caching/src/main/java/com/baeldung/multiplecachemanager/config/MultipleCacheResolver.java b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/config/MultipleCacheResolver.java
new file mode 100644
index 0000000000..1bd869d98e
--- /dev/null
+++ b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/config/MultipleCacheResolver.java
@@ -0,0 +1,38 @@
+package com.baeldung.multiplecachemanager.config;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.interceptor.CacheOperationInvocationContext;
+import org.springframework.cache.interceptor.CacheResolver;
+
+public class MultipleCacheResolver implements CacheResolver {
+
+ private final CacheManager simpleCacheManager;
+
+ private final CacheManager caffeineCacheManager;
+
+ private static final String ORDER_CACHE = "orders";
+
+ private static final String ORDER_PRICE_CACHE = "orderprice";
+
+ public MultipleCacheResolver(CacheManager simpleCacheManager, CacheManager caffeineCacheManager) {
+ this.simpleCacheManager = simpleCacheManager;
+ this.caffeineCacheManager = caffeineCacheManager;
+
+ }
+
+ @Override
+ public Collection extends Cache> resolveCaches(CacheOperationInvocationContext> context) {
+ Collection caches = new ArrayList();
+ if ("getOrderDetail".equals(context.getMethod()
+ .getName())) {
+ caches.add(caffeineCacheManager.getCache(ORDER_CACHE));
+ } else {
+ caches.add(simpleCacheManager.getCache(ORDER_PRICE_CACHE));
+ }
+ return caches;
+ }
+}
diff --git a/spring-caching/src/main/java/com/baeldung/multiplecachemanager/controller/MultipleCacheManagerController.java b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/controller/MultipleCacheManagerController.java
new file mode 100644
index 0000000000..17a73bb27a
--- /dev/null
+++ b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/controller/MultipleCacheManagerController.java
@@ -0,0 +1,43 @@
+package com.baeldung.multiplecachemanager.controller;
+
+import java.util.List;
+
+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;
+
+import com.baeldung.multiplecachemanager.bo.CustomerDetailBO;
+import com.baeldung.multiplecachemanager.bo.OrderDetailBO;
+import com.baeldung.multiplecachemanager.entity.Customer;
+import com.baeldung.multiplecachemanager.entity.Order;
+
+@RestController
+public class MultipleCacheManagerController {
+
+ @Autowired
+ private CustomerDetailBO customerDetailBO;
+
+ @Autowired
+ private OrderDetailBO orderDetailBO;
+
+ @GetMapping(value = "/getCustomer/{customerid}")
+ public Customer getCustomer(@PathVariable Integer customerid) {
+ return customerDetailBO.getCustomerDetail(customerid);
+ }
+
+ @GetMapping(value = "/getCustomerOrders/{customerid}")
+ public List getCustomerOrders(@PathVariable Integer customerid) {
+ return customerDetailBO.getCustomerOrders(customerid);
+ }
+
+ @GetMapping(value = "/getOrder/{orderid}")
+ public Order getOrder(@PathVariable Integer orderid) {
+ return orderDetailBO.getOrderDetail(orderid);
+ }
+
+ @GetMapping(value = "/getOrderPrice/{orderid}")
+ public double getOrderPrice(@PathVariable Integer orderid) {
+ return orderDetailBO.getOrderPrice(orderid);
+ }
+}
diff --git a/spring-caching/src/main/java/com/baeldung/multiplecachemanager/entity/Customer.java b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/entity/Customer.java
new file mode 100644
index 0000000000..cfae15f4e9
--- /dev/null
+++ b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/entity/Customer.java
@@ -0,0 +1,24 @@
+package com.baeldung.multiplecachemanager.entity;
+
+public class Customer {
+
+ private int customerId;
+
+ private String customerName;
+
+ public int getCustomerId() {
+ return customerId;
+ }
+
+ public void setCustomerId(int customerId) {
+ this.customerId = customerId;
+ }
+
+ public String getCustomerName() {
+ return customerName;
+ }
+
+ public void setCustomerName(String customerName) {
+ this.customerName = customerName;
+ }
+}
diff --git a/spring-caching/src/main/java/com/baeldung/multiplecachemanager/entity/Item.java b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/entity/Item.java
new file mode 100644
index 0000000000..4131464981
--- /dev/null
+++ b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/entity/Item.java
@@ -0,0 +1,34 @@
+package com.baeldung.multiplecachemanager.entity;
+
+public class Item {
+
+ private int itemId;
+
+ private String itemDesc;
+
+ private double itemPrice;
+
+ public int getItemId() {
+ return itemId;
+ }
+
+ public void setItemId(int itemId) {
+ this.itemId = itemId;
+ }
+
+ public String getItemDesc() {
+ return itemDesc;
+ }
+
+ public void setItemDesc(String itemDesc) {
+ this.itemDesc = itemDesc;
+ }
+
+ public double getItemPrice() {
+ return itemPrice;
+ }
+
+ public void setItemPrice(double itemPrice) {
+ this.itemPrice = itemPrice;
+ }
+}
diff --git a/spring-caching/src/main/java/com/baeldung/multiplecachemanager/entity/Order.java b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/entity/Order.java
new file mode 100644
index 0000000000..15da60d6ea
--- /dev/null
+++ b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/entity/Order.java
@@ -0,0 +1,44 @@
+package com.baeldung.multiplecachemanager.entity;
+
+public class Order {
+
+ private int orderId;
+
+ private int itemId;
+
+ private int quantity;
+
+ private int customerId;
+
+ public int getOrderId() {
+ return orderId;
+ }
+
+ public void setOrderId(int orderId) {
+ this.orderId = orderId;
+ }
+
+ public int getItemId() {
+ return itemId;
+ }
+
+ public void setItemId(int itemId) {
+ this.itemId = itemId;
+ }
+
+ public int getQuantity() {
+ return quantity;
+ }
+
+ public void setQuantity(int quantity) {
+ this.quantity = quantity;
+ }
+
+ public int getCustomerId() {
+ return customerId;
+ }
+
+ public void setCustomerId(int customerId) {
+ this.customerId = customerId;
+ }
+}
diff --git a/spring-caching/src/main/java/com/baeldung/multiplecachemanager/repository/CustomerDetailRepository.java b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/repository/CustomerDetailRepository.java
new file mode 100644
index 0000000000..aab011427d
--- /dev/null
+++ b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/repository/CustomerDetailRepository.java
@@ -0,0 +1,49 @@
+package com.baeldung.multiplecachemanager.repository;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowCallbackHandler;
+import org.springframework.stereotype.Repository;
+
+import com.baeldung.multiplecachemanager.entity.Customer;
+import com.baeldung.multiplecachemanager.entity.Order;
+
+@Repository
+public class CustomerDetailRepository {
+
+ @Autowired
+ private JdbcTemplate jdbcTemplate;
+
+ public Customer getCustomerDetail(Integer customerId) {
+ String customerQuery = "select * from customer where customerid = ? ";
+ Customer customer = new Customer();
+ jdbcTemplate.query(customerQuery, new Object[] { customerId }, new RowCallbackHandler() {
+ public void processRow(ResultSet rs) throws SQLException {
+ customer.setCustomerId(rs.getInt("customerid"));
+ customer.setCustomerName(rs.getString("customername"));
+ }
+ });
+ return customer;
+ }
+
+ public List getCustomerOrders(Integer customerId) {
+ String customerOrderQuery = "select * from orderdetail where customerid = ? ";
+ List orders = new ArrayList();
+ jdbcTemplate.query(customerOrderQuery, new Object[] { customerId }, new RowCallbackHandler() {
+ public void processRow(ResultSet rs) throws SQLException {
+ Order order = new Order();
+ order.setCustomerId(rs.getInt("customerid"));
+ order.setItemId(rs.getInt("orderid"));
+ order.setOrderId(rs.getInt("orderid"));
+ order.setQuantity(rs.getInt("quantity"));
+ orders.add(order);
+ }
+ });
+ return orders;
+ }
+}
diff --git a/spring-caching/src/main/java/com/baeldung/multiplecachemanager/repository/OrderDetailRepository.java b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/repository/OrderDetailRepository.java
new file mode 100644
index 0000000000..58c0968e48
--- /dev/null
+++ b/spring-caching/src/main/java/com/baeldung/multiplecachemanager/repository/OrderDetailRepository.java
@@ -0,0 +1,53 @@
+package com.baeldung.multiplecachemanager.repository;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowCallbackHandler;
+import org.springframework.stereotype.Repository;
+
+import com.baeldung.multiplecachemanager.entity.Item;
+import com.baeldung.multiplecachemanager.entity.Order;
+
+@Repository
+public class OrderDetailRepository {
+
+ @Autowired
+ private JdbcTemplate jdbcTemplate;
+
+ public Order getOrderDetail(Integer orderId) {
+ String orderDetailQuery = "select * from orderdetail where orderid = ? ";
+ Order order = new Order();
+ jdbcTemplate.query(orderDetailQuery, new Object[] { orderId }, new RowCallbackHandler() {
+ public void processRow(ResultSet rs) throws SQLException {
+ order.setCustomerId(rs.getInt("customerid"));
+ order.setOrderId(rs.getInt("orderid"));
+ order.setItemId(rs.getInt("itemid"));
+ order.setQuantity(rs.getInt("quantity"));
+ }
+ });
+ return order;
+ }
+
+ public double getOrderPrice(Integer orderId) {
+
+ String orderItemJoinQuery = "select * from ( select * from orderdetail where orderid = ? ) o left join item on o.itemid = ITEM.itemid";
+ Order order = new Order();
+ Item item = new Item();
+
+ jdbcTemplate.query(orderItemJoinQuery, new Object[] { orderId }, new RowCallbackHandler() {
+ public void processRow(ResultSet rs) throws SQLException {
+ order.setCustomerId(rs.getInt("customerid"));
+ order.setOrderId(rs.getInt("orderid"));
+ order.setItemId(rs.getInt("itemid"));
+ order.setQuantity(rs.getInt("quantity"));
+ item.setItemDesc("itemdesc");
+ item.setItemId(rs.getInt("itemid"));
+ item.setItemPrice(rs.getDouble("price"));
+ }
+ });
+ return order.getQuantity() * item.getItemPrice();
+ }
+}
diff --git a/spring-caching/src/main/resources/application.properties b/spring-caching/src/main/resources/application.properties
new file mode 100644
index 0000000000..53a3ac93b4
--- /dev/null
+++ b/spring-caching/src/main/resources/application.properties
@@ -0,0 +1,10 @@
+spring.datasource.url=jdbc:h2:mem:testdb
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=
+#spring.h2.console.enabled=false
+
+
+# Enabling H2 Console
+spring.h2.console.enabled=true
+spring.h2.console.path=/h2
diff --git a/spring-caching/src/main/resources/data.sql b/spring-caching/src/main/resources/data.sql
new file mode 100644
index 0000000000..e4165ae71f
--- /dev/null
+++ b/spring-caching/src/main/resources/data.sql
@@ -0,0 +1,7 @@
+INSERT INTO CUSTOMER VALUES(1001,'BAELDUNG');
+
+INSERT INTO ITEM VALUES(10001,'ITEM1',50.0);
+INSERT INTO ITEM VALUES(10002,'ITEM2',100.0);
+
+INSERT INTO ORDERDETAIL VALUES(300001,1001,10001,2);
+INSERT INTO ORDERDETAIL VALUES(300002,1001,10002,5);
\ No newline at end of file
diff --git a/spring-caching/src/main/resources/schema.sql b/spring-caching/src/main/resources/schema.sql
new file mode 100644
index 0000000000..5862499bc0
--- /dev/null
+++ b/spring-caching/src/main/resources/schema.sql
@@ -0,0 +1,19 @@
+CREATE TABLE CUSTOMER(
+ CUSTOMERID INT PRIMARY KEY,
+ CUSTOMERNAME VARCHAR(250) NOT NULL
+);
+
+CREATE TABLE ITEM(
+ITEMID INT PRIMARY KEY,
+ITEMDESC VARCHAR(250),
+PRICE DOUBLE
+);
+
+CREATE TABLE ORDERDETAIL(
+ORDERID INT PRIMARY KEY,
+CUSTOMERID INT NOT NULL,
+ITEMID INT NOT NULL,
+QUANTITY INT,
+FOREIGN KEY (customerid) references CUSTOMER(customerid),
+FOREIGN KEY (itemid) references ITEM(itemid)
+);
\ No newline at end of file
diff --git a/spring-caching/src/test/java/com/baeldung/multiplecachemanager/MultipleCacheManagerIntegrationUnitTest.java b/spring-caching/src/test/java/com/baeldung/multiplecachemanager/MultipleCacheManagerIntegrationUnitTest.java
new file mode 100644
index 0000000000..4bcbdb4d68
--- /dev/null
+++ b/spring-caching/src/test/java/com/baeldung/multiplecachemanager/MultipleCacheManagerIntegrationUnitTest.java
@@ -0,0 +1,64 @@
+package com.baeldung.multiplecachemanager;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.caffeine.CaffeineCache;
+import org.springframework.util.Assert;
+
+import com.baeldung.multiplecachemanager.bo.OrderDetailBO;
+import com.baeldung.multiplecachemanager.entity.Order;
+import com.baeldung.multiplecachemanager.repository.OrderDetailRepository;
+
+@SpringBootApplication
+@SpringBootTest
+public class MultipleCacheManagerIntegrationUnitTest {
+
+ @MockBean
+ private OrderDetailRepository orderDetailRepository;
+
+ @Autowired
+ private OrderDetailBO orderDetailBO;
+
+ @Autowired
+ private CacheManager cacheManager;
+
+ @Autowired
+ private CacheManager alternateCacheManager;
+
+ @Test
+ public void whenCallGetOrderDetail_thenDataShouldBeInCaffieneCacheManager() {
+ Integer key = 30001;
+ cacheManager.getCache("orders")
+ .evict(key);
+ Order order = new Order();
+ order.setCustomerId(1001);
+ order.setItemId(10001);
+ order.setOrderId(30001);
+ order.setQuantity(2);
+ Mockito.when(orderDetailRepository.getOrderDetail(key))
+ .thenReturn(order);
+ orderDetailBO.getOrderDetail(key);
+ org.springframework.cache.caffeine.CaffeineCache cache = (CaffeineCache) cacheManager.getCache("orders");
+ Assert.notNull(cache.get(key)
+ .get(), "caffieneCache should have had the data");
+ }
+
+ @Test
+ public void whenCallGetOrderPrice_thenDataShouldBeInAlternateCacheManager() {
+ Integer key = 30001;
+ alternateCacheManager.getCache("orderprice")
+ .evict(key);
+ Mockito.when(orderDetailRepository.getOrderPrice(key))
+ .thenReturn(500.0);
+ orderDetailBO.getOrderPrice(key);
+ Cache cache = alternateCacheManager.getCache("orderprice");
+ Assert.notNull(cache.get(key)
+ .get(), "alternateCache should have had the data");
+ }
+}