I tried to use EhCache 3.5 caching features in our web application based on Spring Boot 2/Spring Framework 5.
I added EHCache dependency:
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
<version>1.0.0</version>
</dependency>
Then created ehcache.xml file in src/main/resources folder:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
monitoring="autodetect" dynamicConfig="true">
<cache name="orders" maxElementsInMemory="100"
eternal="false" overflowToDisk="false"
memoryStoreEvictionPolicy="LFU" copyOnRead="true"
copyOnWrite="true" />
</ehcache>
Spring 5 reference guide does't mention EHCache usage, Spring 4 reference guide states: "Ehcache 3.x is fully JSR-107 compliant and no dedicated support is required for it."
So I created controller OrderController and REST endpoint:
@Cacheable("orders")
@GetMapping(path = "/{id}")
public Order findById(@PathVariable int id) {
return orderRepository.findById(id);
}
Spring Boot configuration:
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
But when I call this endpoint I get an exception:
Cannot find cache named 'orders' for Builder[public org.Order org.OrderController.findById(int)] caches=[orders] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'
Then I tried to use example from Spring Framework 4:
@Bean
public CacheManager cacheManager() {
return new EhCacheCacheManager(ehCacheCacheManager().getObject());
}
@Bean
public EhCacheManagerFactoryBean ehCacheCacheManager() {
EhCacheManagerFactoryBean cmfb = new EhCacheManagerFactoryBean();
cmfb.setConfigLocation(new ClassPathResource("ehcache.xml"));
cmfb.setShared(true);
return cmfb;
}
But it doesn't compile because of exception:
The type net.sf.ehcache.CacheManager cannot be resolved. It is indirectly referenced from required .class file
Please, advise.
There is a mix of things here. Ehcache 3, which you are using, is used with Spring through JCache.
Which is why you need to use spring.cache.jcache.config=classpath:ehcache.xml
.
Then, your Ehcache configuration is indeed an Ehcache 2 configuration. So is the EhCacheCacheManager
. For JCache, you should use JCacheCacheManager
. But in fact, you don't even need it with an ehcache.xml
.
Here are the steps to make it work
Step 1: Set the correct dependencies. Note that you don't need to specify any version as they are provided by the parent pom dependency management. javax.cache is now version 1.1.
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
Step 2: Add an ehcache.xml
file in src/main/resources
. An example below.
<?xml version="1.0" encoding="UTF-8"?>
<config
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns:jsr107='http://www.ehcache.org/v3/jsr107'
xmlns='http://www.ehcache.org/v3'
xsi:schemaLocation="
http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.5.xsd
http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.5.xsd">
<service>
<jsr107:defaults enable-management="false" enable-statistics="true"/>
</service>
<cache alias="value">
<resources>
<heap unit="entries">2000</heap>
</resources>
</cache>
</config>
Step 3: An application.properties
with this line is needed to find the ehcache.xml
spring.cache.jcache.config=classpath:ehcache.xml
Note that since JCache is found in the classpath, Spring Cache will pick it as the cache provider. So there's no need to specify spring.cache.type=jcache
.
Step 4: Enable caching like you did
@SpringBootApplication
@EnableCaching
public class Cache5Application {
private int value = 0;
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Cache5Application.class, args);
Cache5Application app = context.getBean(Cache5Application.class);
System.out.println(app.value());
System.out.println(app.value());
}
@Cacheable("value")
public int value() {
return value++;
}
}
You need to force it to use ehcache version 2
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.3</version>
</dependency>
To use ehcache 3:
Here is the application:
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Here is the application.yml
spring:
cache:
ehcache:
config: ehcache.xml
Here a service with a counter for testing
@Service
public class OrderService {
public static int counter=0;
@Cacheable("orders")
public Order findById(Long id) {
counter++;
return new Order(id, "desc_" + id);
}
}
Here is a test to prove it is using the cache:
@RunWith(SpringRunner.class)
@SpringBootTest
public class OrderServiceTest {
@Autowired
private OrderService orderService;
@Test
public void getHello() throws Exception {
orderService.findById(1l);
assertEquals(1, OrderService.counter);
orderService.findById(1l);
assertEquals(1, OrderService.counter);
orderService.findById(2l);
assertEquals(2, OrderService.counter);
}
}
See here for working example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With