Actually, I am caching an Http Response using Spring Cache and now I want to put a condition that is to update the cache only when the Response is valid.
#Cacheable(value = CACHE, condition = "#result.body.responseData.toLowerCase().contains("A")")
public ResponseEntity<ProcessMqReqPostResponseBody> sendMqRequest(Integer pageNumber, Integer pageSize, String sortOrder, String merchantId) {
//Method Implementation
}
Without the condition, I can test my cache fine but when I added this condition, I get the error
org.springframework.expression.spel.SpelEvaluationException: EL1007E:
Property or field 'body' cannot be found on null
I don't understand that because I can output the responseEntity in my test and it is not null. Is that behavior correct?
Thanks,
Ashley
Spring's behavior is correct because you are writing a condition that will work according to the result.
The #result value is always null before the method is executed, the result value is only filled after the method is executed.
If you want to test this, you can change your condition as follows, method will not be cached at all and will not give an error.
#Cacheable(value = CACHE, condition = "#result != null and #result.body.responseData.toLowerCase().contains(\"A\")")
If you want to act on the result, you can only do so using the unless element.
Unlike condition(), this expression is evaluated after the method has been called and can therefore refer to the result.
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/cache/annotation/Cacheable.html#unless--
Related
I use Redis for caching and have the following service method:
#Cacheable(value = "productCache")
#Override
public List<ProductDTO> findAllByCategory(Category category) {
// code omitted
return productDTOList;
}
When I pass categoryA to this method, the result is cached and is kept during expiration period. If I pass categoryB to this method, it is retrieved from database and then kept in cache. Then if I pass categoryA again, it is retrieved from cache.
1. I am not sure if it is normal, because I just use value parameter ("productCache") of #Cacheable annotation and have no idea how it caches categoryA and categoryB results separately. Could you please explain how it works?
2. As mentioned on this page, there is also key parameter. But when using it as shown below, I think it does not make any sense and it works as above. Is that normal or am I missing something?
#Cacheable(value = "productCache", key="#category")
#Override
public List<ProductDTO> findAllByCategory(Category category) {
// code omitted
return productDTOList;
}
3. Should I get cache via Cache cache = cacheManager.getCache("productCache#" + category); ?
Caches are essentially key-value stores, where – in Spring –
the key is generated from the method parameter(s)
the value is the result of the method invocation
The default key generation algorithm works like this (taken right from Spring docs):
If no params are given, return SimpleKey.EMPTY.
If only one param is given, return that instance.
If more than one param is given, return a SimpleKey that contains all parameters.
This approach works well for most use-cases, as long as parameters have natural keys and implement valid hashCode() and equals() methods. If that is not the case, you need to change the strategy.
So in your example, the category object acts as key per default (for this to work, the Category class should have hashCode() and equals() implemented correctly). Writing key="#category" is hence redundant and has no effect. If your Category class would have an id property, you could write key="#category.id" however.
Should I get cache via Cache cache = cacheManager.getCache("productCache#" + category); ?
No, since there is no such cache. You only have one single cache named productCache.
I am using #Cacheable annotation to cache the results of my method. For performance reason I want to cache both null and non-null values returned from method.
But problem here is Spring is caching non-null values but not caching null for some reason.
Here is my code:
#Cacheable(
cacheManager = "promoCacheManager",
value = "promos:campaign",
key = "'promos:campaign:'.concat(#currencyId)"
)
public PromosDto getPromosByCurrency(Integer currencyId) {
...
I have tried every thing. Even I set
unless = "#result != null || #result == null"
But that didn't help as well.
Any pointer on this?
Check your cache manager settings.
For example: RedisCacheManager has an overloaded constructor where you can specify cacheNullValues; this is false by default - try setting it to true.
https://docs.spring.io/spring-data/redis/docs/current/api/org/springframework/data/redis/cache/RedisCacheManager.html#RedisCacheManager-org.springframework.data.redis.core.RedisOperations-java.util.Collection-boolean-
Also keep in mind:
NOTE When enabling cacheNullValues please make sure the RedisSerializer used by RedisOperations is capable of serializing NullValue.
Why not return Optional<PromosDto> to safely wrap a null. That should cache fine then, according to https://docs.spring.io/spring/docs/4.3.10.RELEASE/spring-framework-reference/htmlsingle/#cache-annotations-cacheable-condition
I have an #Entity Video having a one-to-many relation with a List<Tag> tags as one of its fields. I use the following #Repository using Spring Data to get the most popular tags:
#Repository
public interface TagRepository extends CrudRepository<Tag, Integer>{
#Query("SELECT t FROM Tag t WHERE (SELECT SUM(v.views) FROM Video v WHERE t MEMBER OF v.tags) > 0")
public List<Tag> findMostViewedTags(int maxTags);
}
The Query is processed and considered valid by Spring, I tested the generated SQL vs my database locally and it returned 2 Tags. In my Code however, I receive the value Null when I call the method findMostViewedTags(100).
The Query lookup strategy is the default "CREATE_IF_NOT_FOUND".
If there are no results found, should the method return an empty list or Null? My desired behavior is to receive an empty list.
Why does the method call return Null instead of a List<Tag> with size() 2?
The normal behavior is indeed returning an empty list if no results are found. If a List<Object> is the return value of the method in the defined interface, the method should never return Null.
The problem is that a parameter is given to the method and is not used anywhere in the Query. For some reason Spring decides to return a Null in that case. Solution: remove the unused parameter or use the parameter in the Query.
I have experienced similar problem. The cause was that I was using Mockito and have not correctly mocked the data with when().
I have an Interface method which can be called as below -
{{url}}/packageName/{{var}}/list
It can take in a param of type Collection<String> , so i can fetch specific results.
{{url}}/packageName/{{var}}/list?paramIds=param1¶mIds=param2
Now if i leave paramIds empty as below, Spring MVC creates LinkedHashMap , size 0 and i get no results back.
{{url}}/packageName/{{var}}/list?paramIds=
This is my annotation #RequestParam(value = "paramIds", required = "false") Collection<String> paramIds
I tried to get rid of required and use defaultValue but unable to set defaultValue to null.
For now i changed the annotation to #RequestParam(value = "paramIds", defaultValue = "none") and added code in the dao to handle "none" as null - wondering if there is a better way to solve this problem.
Thanks in advance.
It sounds to me like Spring is giving you the best option, an empty list is usually better than a null.
My first choice would be to modify your DAO code to handle an empty list instead of expecting null, but assuming that's not possible, why not have a check in your controller to pass null to your DAO if the collection is empty, like:
if (paramsIds.isEmpty()) {
dao.doSomething(null);
}
#RequestMapping(value = "/Fin_AddBankAccount", method = RequestMethod.POST)
public #ResponseBody JsonResponse addCoaCategory(
#RequestParam(value="code", required=true) long code,
#RequestParam(value="startFrom", required=true) long startFrom,
#RequestParam(value="name", required=true, defaultValue="N/A") String name)
{
}
defaultValue="N/A" not working , As I did not provide any text in name field , it store null in database instead of "N/A"?
What is the point of setting a default value if you really want that parameter.
if you mark it as required true(not needed as it is default) then no need of a default value.
If that parameter is not mandatory then mark it as false and give a default value.
Documentation of Spring RequestParam.required
Default is true, leading to an exception thrown in case of the parameter missing in the request. Switch this to false if you prefer a null in case of the parameter missing.
From your question I figured out that you are sending parameter name with empty value using POST request. According to the Spring documentation you should not send name parameter in the request in order to use default value. Simply remove name field from HTML form if it is empty.
It seems that default values makes more sense for GET requests.
make sure you don't pass empty string value
Valid Methods:
1. Fin_AddBankAccount?name=
O/P: name="N/A"
Fin_AddBankAccount?
O/P: name="N/A"
Invalid Methods:
Fin_AddBankAccount?name=""
this will set empty string to variable i.e. name="";
In my project
#RequestParam(value="name", required=true, defaultValue="N/A") String name
This code correctly sets name variable as defaultvalue N/A when requestparam "name" was not provided. My guess is you are not inserting this name variable into the table properly so database is storing null instead of "N/A". Please show us or double check the data access object code. Good luck
Thanks #Tiarê Balbi, in fact you do not need "required=true" because defaultValue="N/A" implicitly sets this variable as required=false anyways.