Given the following method with the Cacheable annotation:
#Cacheable(value = "cachekey", key = "#taskId")
public Task getTask(Long taskId) {
log.info("called");
Task task = ...;
return task;
}
And the following configuration:
#EnableCaching
#Configuration
public class CacheConfiguration {
#Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("cachekey");
}
#Scheduled(fixedDelay = 30000)
#CacheEvict(allEntries = true, cacheNames = {"cachekey"})
public void cacheEvict() {
}
}
And the following pom parts:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath/>
</parent>
...
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
By calling getTask, I always hit the method and no request is served from the cache itself.
What am I doing wrong?
I've recreated an application based off the code you provided and tried to keep it as similar as possible. The program yields the output:
Calling getTask
getTask called
Calling getTask again
true
Which indicates that getTask is only executed once and that the caching is working. So we'll have to assume it is something not listed in the code you provided. Two thoughts come to mind:
Are you sure that you are providing the same long to #getTask(Long) when calling it multiple times? As it's currently configured, different longs will result in different cache entries. You'll have to call it at least twice with the same long in order to see caching take effect.
Are you calling #getTask(Long) from a different bean than the one which contains #getTask(Long)? The functionality provided by #Cacheable is implemented using Spring AOP. Spring AOP will wrap the bean (CachedService in my example) with some generated code that handles the cache retrieval and put. However, this wrapping does not affect method calls internal to that bean.
For example, if I added a #getTask99() method to CachedService like in the code snippet below:
#Component
public static class CachedService {
public Task getTask99() {
return getTask(99L);
}
#Cacheable(value = "cachekey", key = "#taskId")
public Task getTask(Long taskId) {
System.out.println("getTask called");
Task task = new Task(System.out::println);
return task;
}
}
Calling #getTask99() produces:
Calling getTask99
getTask called
Calling getTask99 again
getTask called
false
Indicating that nothing was cached, as the bean wrapping has been circumvented. Instead, call #getTask(99L) from a different bean, like CacheTestRunner in my example.
Related
Very simple and straight forward exception. is there any way to cache exception using caffeine/springboot ?
some specific exceptions in my method can be very time consuming... [404 for example] i wish i could cache it and avoid long processing
A simple way to cache exceptions is to encapsulate the call, catch the exception and represent it as a value. I am just adding more details based on #ben-manes comment.
Approach 1: encapsulate the exception as a business object
Approach 2: return null value or Optional object on exception. For caching null values, you need to explicitly enable caching of null values (refer here - Spring Boot Cacheable - Cache null values)
Here is an example based on Spring Caching (can be extended to Caffeine). The following class loads a Book entity object which may result in exception. The exception is handled and cached, so next time the same ISBN code (argument) is passed the return value (exception) is returned from the cache.
#Component
public class SimpleBookRepository implements BookRepository {
#Override
#Cacheable("books")
public Book getByIsbn(String isbn) {
Book book = loadBook(isbn);
return book;
}
// Don't do this at home
private void loadBook(String isbn) {
Book book;
try {
//get book from DB here
book = loadBook();//DB call. can throw exception.
} catch (Exception e) {
book = new Book("None found"); Approach 1 - //encapsulate error as an entity.
book = null; // Approach 2 - set as null
}
return book;
}
}
1. Very first time add these dependency in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.7.0</version>
</dependency>
2. add #CacheConfig(cacheNames = {"customer"}) , #Slf4j under #Service
annotation
3. add #Cacheable on your method where you want caching. and in method add log
public Customer saveCustomer (Customer customerId) {
log.info("Inside save Customer method of Customer Service");
return customerRepository.save(customerId);
}
There are a few important point:
The #CacheConfig is a class level annotation and help to streamline caching configurations.
The #Cacheable annotation used to demarcate methods that are cacheable. In simple words, this annotation used to show caching API that we want to store results for this method into the cache so, on subsequent invocations, the value in the cache returned without execute the method.
Developing a spring boot batch application, wanted to know if there is a sample code to how to get the micro metrics discussed in the spring batch document?
I am looking way of getting these details per execution. Also, since I run the application using cron task schedule, can we get a separation of this data per execution?
Found the solution
You need not do any coding, all readily available.
Include actuator dependency in pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
In the property, file add this line (instead of * you can add comma separated and security) refrence
management.endpoints.web.exposure.include=*
If your application is running on port 8080
http://localhost:8080/actuator/metrics/spring.batch.job
Will give job stats. You can query on any other metrics parameters. Reference
You can calculate metrics for all execution in the corn task schedule by
First create a custom metric endpoint.
#Component
#Endpoint(id = "spring.batch.executions")
public class BatchMetricEndpoint {
#Autowired
BatchAllJobsMetricContext batchAllJobsMetricContext;
#ReadOperation
public Map<String, BatchPerJobMetricContext> features() {
return batchAllJobsMetricContext.getAll();
}
}
Second
Create a job listener
Tap into local metric endpoint after execution.
#Component
public class BatchJobListener implements JobExecutionListener {
#Autowired
private MetricsEndpoint metricsEndpoint;
#Autowired
BatchAllJobsMetricContext batchAllJobsMetricContext;
#Autowired
BatchPerJobMetricContext batchPerJobMetricContext;
#Override
public void beforeJob(JobExecution jobExecution) {
}
#Override
public void afterJob(JobExecution jobExecution) {
MetricsEndpoint.MetricResponse metricResponse = metricsEndpoint.metric("spring.batch.job",null);
String key = jobExecution.getJobParameters().getString("jobId");
String execution = "Execution "+jobExecution.getJobParameters().getString("executionCout");
if(batchAllJobsMetricContext.hasKey(key)){
batchAllJobsMetricContext.get(key).put(execution,metricResponse);
}else{
batchPerJobMetricContext.put(execution,metricResponse);
batchAllJobsMetricContext.put(key,batchPerJobMetricContext);
}
}
}
Third
Tap into the local metric to aggrigate data.
Please note this way of holding metic per iteration would be expensive on memory, you would like to keep some limit and push this data to time series data source.
Sample code
I am using spring jpa transactions in my project.One Case includes inserting a data in a synchronized method and when another thread accesses it the data is not updated.My code is given below :
public UpdatedDTO parentMethod(){
private UpdatedDTO updatedDTO = getSomeMethod();
childmethod1(inputVal);
return updatedDTO;
}
#Transactional
public synchronized childmethod1(inputVal){
//SomeCodes
//Place where update takes place
TableEntityObject obj = objectRepository.findByInputVal(inputVal);
if(obj == null){
childMethod2(inputVal);
}
}
#Transactional
public void childMethod2(inputVal){
//Code for inserting
TableEntityObject obj = new TableEntityObject();
obj.setName("SomeValue");
obj.setValueSet(inputVal);
objectRepository.save(obj);
}
Now if two threads access at the same time and if first thread completes childmethod2 and childmethod1 and without completing parentMethod() after that if second thread comes to the childMethod1() and checks if data exists,the data is null and is not updated by first thread.I have tried many ways like
#Transactional(propagation = Propagation.REQUIRES_NEW)
public synchronized childmethod1(inputVal){
//SomeCodes
//Place where update takes place
TableEntityObject obj = objectRepository.findByInputVal(inputVal);
if(obj == null){
childMethod2(inputVal);
}
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void childMethod2(inputVal){
//Code for inserting
TableEntityObject obj = new TableEntityObject();
obj.setName("SomeValue");
obj.setValueSet(inputVal);
objectRepository.save(obj);
}
also tried taking off #transactional in the childMethod1() but nothing works out.I know im doing something wrong here , but couldnt figure out where and what exactly i am doing wrong.Can anyone help me out with this
#Transactional is resolved using proxies on spring beans. It means it will have no effect if your method with #Transactional is called from the same class. Take a look at Spring #Transaction method call by the method within the same class, does not work?
The easiest would be moving those methods into separate service.
Typical checklist I follow in cases like these :
If Java based configuration then make sure
#EnableTransactionManagement annocation is present in the class
containing the #Configuration annotation
Make sure the transactionManager bean is created, again this should be mentioned in the configuration class.
Use of #Transactional annocatio over the method which is calling the repository, typically a class in the DAO layer
Adding the #Service annotation for the class which is invoking the methods in the repository
Nice blog which explains the Transaction configuration with JPA in depth --> http://www.baeldung.com/2011/12/26/transaction-configuration-with-jpa-and-spring-3-1/68954
I have developed a Spring Data repository, MemberRepository interface, that extends org.springframework.data.jpa.repository.JpaRepository. MemberRepository has a method:
#Cacheable(CacheConfiguration.DATABASE_CACHE_NAME)
Member findByEmail(String email);
The result is cached by Spring cache abstraction (backed by a ConcurrentMapCache).
The issue I have is that I want to write an integration test (against hsqldb) that asserts that the result is retrieved from db the first time and from cache the second time.
I initially thought of mocking the jpa infrastructure (entity manager, etc.) and somehow assert that the entity manager is not called the second time but it seems too hard/cumbersome (see https://stackoverflow.com/a/23442457/536299).
Can someone then please provide advice as to how to test the caching behavior of a Spring Data Repository method annotated with #Cacheable?
If you want to test a technical aspect like caching, don't use a database at all. It's important to understand what you'd like to test here. You want to make sure the method invocation is avoided for the invocation with the very same arguments. The repository fronting a database is a completely orthogonal aspect to this topic.
Here's what I'd recommend:
Set up an integration test that configures declarative caching (or imports the necessary bit's and pieces from your production configuration.
Configure a mock instance of your repository.
Write a test case to set up the expected behavior of the mock, invoke the methods and verify the output accordingly.
Sample
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class CachingIntegrationTest {
// Your repository interface
interface MyRepo extends Repository<Object, Long> {
#Cacheable("sample")
Object findByEmail(String email);
}
#Configuration
#EnableCaching
static class Config {
// Simulating your caching configuration
#Bean
CacheManager cacheManager() {
return new ConcurrentMapCacheManager("sample");
}
// A repository mock instead of the real proxy
#Bean
MyRepo myRepo() {
return Mockito.mock(MyRepo.class);
}
}
#Autowired CacheManager manager;
#Autowired MyRepo repo;
#Test
public void methodInvocationShouldBeCached() {
Object first = new Object();
Object second = new Object();
// Set up the mock to return *different* objects for the first and second call
Mockito.when(repo.findByEmail(Mockito.any(String.class))).thenReturn(first, second);
// First invocation returns object returned by the method
Object result = repo.findByEmail("foo");
assertThat(result, is(first));
// Second invocation should return cached value, *not* second (as set up above)
result = repo.findByEmail("foo");
assertThat(result, is(first));
// Verify repository method was invoked once
Mockito.verify(repo, Mockito.times(1)).findByEmail("foo");
assertThat(manager.getCache("sample").get("foo"), is(notNullValue()));
// Third invocation with different key is triggers the second invocation of the repo method
result = repo.findByEmail("bar");
assertThat(result, is(second));
}
}
As you can see, we do a bit of over-testing here:
The most relevant check, I think is that the second call returns the first object. That's what the caching is all about. The first two calls with the same key return the same object, whereas the third call with a different key results in the second actual invocation on the repository.
We strengthen the test case by checking that the cache actually has a value for the first key. One could even extend that to check for the actual value. On the other hand, I also think it's fine to avoid doing that as you tend to test more of the internals of the mechanism rather than the application level behavior.
Key take-aways
You don't need any infrastructure to be in place to test container behavior.
Setting a test case up is easy and straight forward.
Well-designed components let you write simple test cases and require less integration leg work for testing.
I tried testing the cache behavior in my app using Oliver's example. In my case my cache is set at the service layer and I want to verify that my repo is being called the right number of times. I'm using spock mocks instead of mockito. I spent some time trying to figure out why my tests are failing, until I realized that tests running first are populating the cache and effecting the other tests. After clearing the cache for every test they started behaving as expected.
Here's what I ended up with:
#ContextConfiguration
class FooBarServiceCacheTest extends Specification {
#TestConfiguration
#EnableCaching
static class Config {
def mockFactory = new DetachedMockFactory()
def fooBarRepository = mockFactory.Mock(FooBarRepository)
#Bean
CacheManager cacheManager() {
new ConcurrentMapCacheManager(FOOBARS)
}
#Bean
FooBarRepository fooBarRepository() {
fooBarRepository
}
#Bean
FooBarService getFooBarService() {
new FooBarService(fooBarRepository)
}
}
#Autowired
#Subject
FooBarService fooBarService
#Autowired
FooBarRepository fooBarRepository
#Autowired
CacheManager cacheManager
def "setup"(){
// we want to start each test with an new cache
cacheManager.getCache(FOOBARS).clear()
}
def "should return cached foobars "() {
given:
final foobars = [new FooBar(), new FooBar()]
when:
fooBarService.getFooBars()
fooBarService.getFooBars()
final fooBars = fooBarService.getFooBars()
then:
1 * fooBarRepository.findAll() >> foobars
}
def "should return new foobars after clearing cache"() {
given:
final foobars = [new FooBar(), new FooBar()]
when:
fooBarService.getFooBars()
fooBarService.clearCache()
final fooBars = fooBarService.getFooBars()
then:
2 * fooBarRepository.findAll() >> foobars
}
}
I am writing unit tests for service layer in my spring application.
Here is my service class
#Service
public class StubRequestService implements RequestService {
#Autowired
private RequestDao requestDao;
#Transactional(propagation = Propagation.REQUIRED, readOnly = true)
#Override
public Request getRequest(Long RequestId) {
Request dataRequest = requestDao.find(requestId);
return dataRequest;
}
}
Here is my test class
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(locations = { "/META-INF/spring/applicationContext.xml" })
public class StubRequestServiceTest {
#Mock
public RequestDao requestDao;
StubRequestService stubRequestService; // How can we Autowire this ?
#org.junit.Before
public void init() {
stubRequestService = new StubRequestService(); // to avoid this
stubRequestService.setRequestDao(dataRequestDao);
// Is it necessary to explicitly set all autowired elements ?
// If I comment/remove above setter then I get nullPointerException
}
#Test
public void testGetRequest() {
Request request = new Request();
request.setPatientCnt("3");
when(requestDao.find(anyLong())).thenReturn(request);
assertEquals(stubRequestService.getRequest(1234L).getPatientCnt(),3);
}
}
Its working fine but I have few questions
How can we Autowire service class in test ? I am using constructor in init() method to create service object.
Do we have to set all Autowire element for service class ? For ex StubRequestService have autowired RequestDao which I need to set explicitly before calling test method otherwise it giveds nullPointerException as requestDao is null in StubRequestService.getRequest method.
Which are the good practices to follow while unit testing Spring service layer ? (If I am doing anything wrong).
Your test is fine. It doesn't even have to have the #ContextConfiguration annotation.
The whole point of dependency injection frameworks like Spring is to be able to unit test services by simply instantiating them, setting mock dependencies, and then call their methods.
You're doing it correctly. You don't need to have a Spring context for such unit tests. That's why they're called unit tests: they test it in isolation of all their actual dependencies, Spring included.
Side note: assuming you're using JUnit, the arguments of the assertXxx method should be swapped. The expected value comes before the actual value. It becomes important when the assertion fails and you have a message like "expecting 6 but was 3" rather than "expecting 3 but was 6".
If you really feel that it will make your tests easier to understand - you can initialize a spring context and fetch all of the objects from there. However, usually it will require creating a separate spring configuration XML file specifically for tests therefore I would not recommend it.
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testApplicationContext.xml");
stubRequestService = (RequestService)applicationContext.getBean("myRequestServiceBean");
(and 3) Basically, I prefer testing each component of my application in total isolation from eachother and that's why I do not recommend what I described in [1].
What that means, is you take a separate logical slice of your application and test only it, while fully mocking up everything it tries to access.
Let's say you have three classes:
//Fetches stuff from some webservice and converts to your app domain POJOs
class DataAccessLayer {
public void setWebservice(Webservice ws) {...};
public MyObject getMyObject() {...};
}
//Formats the domain POJOs and sends them to some kind of outputstream or stuff.
class ViewLayer {
public void setOutputStream(OutputStream os) {...};
public void viewMyObject(MyObject mo) {...};
}
//Main entry point of our MyObject fetch-process-display workflow
class Controller {
public void setDataAccessLayer(DataAccessLayer dal) {...};
public void setViewLayer(ViewLayer vl) {...};
public void showMyObject() {
MyObject mo = dal.getMyObject();
...some processing here maybe...
vl.viewMyObject(mo);
}
}
Now, what tests can we write here?
Test if DataAccessLayer properly converts the object from mocked up WS to our domain object.
Test if ViewLayer properly formats the object given to him and writes it to mocked up output stream.
Test if Controller takes an object from mocked up DataAccessLayer processes it properly and sends it to mocked up ViewLayer.
Or You can use springockito
https://bitbucket.org/kubek2k/springockito/wiki/Home, it will make your tests cleaner