* Java Example of Hexagonal Architecture

* removing unnecessary maven files from pull request and spaces in code

* Removing unnecessary lines

* Example code for using Multiple Cache Manager in SpringBoot

* BAEL-3963:Using multiple cache managers in Spring

* removing new module created in last pull request

* Fixes as per editor Suggestions

Co-authored-by: Ankur Gupta <ankur.gupta@jda.com>
This commit is contained in:
Ankur Gupta 2020-04-18 20:41:31 +05:30 committed by GitHub
parent ca443a08a3
commit 95967877e9
16 changed files with 542 additions and 44 deletions

View File

@ -1,49 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-caching</artifactId>
<version>0.1-SNAPSHOT</version>
<name>spring-caching</name>
<packaging>war</packaging>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-caching</artifactId>
<version>0.1-SNAPSHOT</version>
<name>spring-caching</name>
<packaging>war</packaging>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../parent-boot-2</relativePath>
</parent>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../parent-boot-2</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
<properties>
<ehcache.version>3.5.2</ehcache.version>
</properties>
<properties>
<ehcache.version>3.5.2</ehcache.version>
</properties>
</project>

View File

@ -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) {

View File

@ -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<Order> getCustomerOrders(Integer customerId) {
return customerDetailRepository.getCustomerOrders(customerId);
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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<Cache> caches = new ArrayList<Cache>();
if ("getOrderDetail".equals(context.getMethod()
.getName())) {
caches.add(caffeineCacheManager.getCache(ORDER_CACHE));
} else {
caches.add(simpleCacheManager.getCache(ORDER_PRICE_CACHE));
}
return caches;
}
}

View File

@ -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<Order> 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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<Order> getCustomerOrders(Integer customerId) {
String customerOrderQuery = "select * from orderdetail where customerid = ? ";
List<Order> orders = new ArrayList<Order>();
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;
}
}

View File

@ -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();
}
}

View File

@ -0,0 +1,8 @@
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# Enabling H2 Console
spring.h2.console.enabled=true
spring.h2.console.path=/h2

View File

@ -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);

View File

@ -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)
);

View File

@ -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 givenCacheResolverIsConfigured_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 givenCacheResolverIsConfigured_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");
}
}