this seems to be redundant and obvious question,but can anyone explain me the concept of ParameterizedType Reference in rest template
RestTemplate is deprecated, use WebClient instead. Anyways, ParameterizedTypeReference is used where the target type is a generic type. Consider this, the response body contains a Set<SomeObject>, you can not pass the generic type directly to exchange method because Set<SomeObject>.class is an invalid reference. In this case, you can use ParameterizedTypeReference. Below is an example:
ParameterizedTypeReference<Set<SomeObject>> someObject =
new ParameterizedTypeReference<Set<SomeObject>>() {};
ResponseEntity<Set<SomeObject>> response =
restTemplate.exchange("uri", HttpMethod.GET, null, someObject);
Other way of doing this is encapsulating Set<SomeObject> into a wrapper and create getters and setters to that set. But I don't recommend using it.
Related
I'm creating an order service, new to RestServices world.
I need to read the order model into a OrderDTO and persist in the DB.
For that I have a below method:
#PostMapping(produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<OrderDTO> createOrder(#Valid #RequestBody OrderDTO orderDTO) {
Order order = new Order(orderDTO);
Order createdOrder = orderService.createOrder(order);
OrderDTO createdOrderDTO = new OrderDTO(order);
ResponseEntity<OrderDTO> responseEntity = new ResponseEntity<OrderDTO>(createdOrderDTO, null, HttpStatus.CREATED);
return responseEntity;
}
Everything working fine, but I have concerns about the current design:
I'm reading an input into DTO
To Store the object I'm converting into Order object which will be persisted by Hibernate
Again to send the response back I'm converting the actual order object into DTO.
finally I will create 4-5 Objects per a request, if my app got 100 request it may run into memory issue.
How i can read the model data and persist efficiently?
In general, prefer DTO because of single responsibility principle, every object have its own responsibility and It's also clearer to separate View/Controller from Model objects
You can sometimes reduce OrderDTO, use an object that is both DTD and real Object,
It'll include DTD properties and also other properties that you can add using builder for example, I'm using #JsonIgnoreProperties(ignoreUnknown = true) to set only the DTD properties when object is created from request, e.g.:
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(Include.NON_NULL)
public class Order
You can also use JsonGetter/JsonProperty/JsonSetter to control what expected/returned
#JsonGetter and #JsonSetter are old alternatives to #JsonProperty.
I prefer a Mapper like Mapstruct:
OrderDtoMapper mapper = new OrderDTOMapper();
Order order = OrderDtoMapper.map(orderDto, Order.class);
and back:
OrderDTO createdOrderDTO = OrderDtoMapper.map(order, OrderDTO.class);
For me the code looks more readable ... and you do not have much to write for, as Mapstruct maps it automatically. Because it looks like you will map quite a lot ;)
Perhaps a mapper is worth a try: http://mapstruct.org/
I don't see any issue with the design.
As Nizet pointed out. Objects created are short lived.
Normally DTO and Entity design is followed to keep the UI and Service Layer separate.
In this way, you have the option to filter out sensitive info from being passed to the world like password, pin.
But if you want you can use Order entity directly in Controller class.
I won't suggest that but it's possible.
I'm working in a Spring Boot environnement using Kotlin. I made a controller with a method annotated with #GetMapping. This method have some parameters of type #RequestParam declared as Double type. If I try to call my method without providing these parameters, my code raises the following exception:
java.lang.IllegalStateException: Optional double parameter 'latitude' is present but cannot be translated into a null value due to being declared as a primitive type.
I assume that the parameters have default value (probably 0.0), but Kotlin need an object which can be null, so the exception is raised.
All works fine if I provide the parameters, but I want my code working if no parameters are provided.
How can I avoid this exception?
Here's how my controller looks like:
#RestController
#RequestMapping("/api/stations")
class StationController {
#GetMapping
fun findAll(#RequestParam(value = "latitude", required = false) currentLatitude: Double,
#RequestParam(value = "longitude", required = false) currentLongitude: Double): ResponseEntity<List<Entity>> {
//Method body
}
Maybe the following part of the documentation regarding basic types will help you:
On the Java platform, numbers are physically stored as JVM primitive types, unless we need a nullable number reference (e.g. Int?) or generics are involved. In the latter cases numbers are boxed.
Your guess might be correct then. Try using Double? and it should be ok.
I am using JSR107 caching with Springboot. I have following method.
#CacheResult(cacheName = "books.byidAndCat")
public List<Book> getAllBooks(#CacheKey final String bookId, #CacheKey final BookCategory bookCat) {
return <<Make API calls and get actual books>>
}
First time it makes actual API calls, and second time it loads cache without issue. I can see the following part of log.
Computed cache key SimpleKey [cb5bf774-24b4-41e5-b45c-2dd377493445,LT] for operation CacheResultOperation[CacheMethodDetails ...
But the problem is I want to load cache without making even first API call, Simply needs to fill the cache like below.
String cacheKey = SimpleKeyGenerator.generateKey(bookId, bookCategory).toString();
cacheManager.getCache("books.byidAndCat").put(cacheKey, deviceList);
When I am checking, hashcode of cachekeys are same in both cases, But it is making API calls. If the hashcode is same in both cases, why it is making API calls without considering the cache ?
When debugging spring classes identified that, org.springframework.cache.interceptor.SimpleKeyGenerator is used with the cache key generation even #CacheResult is there.
EDIT and enhance the question :
Apart from that if getAllBooks has overloaded methods, and then call this cached method via separate overloaded method, in that case also method caching is not working.
I'm not an expert of JSR107 annotations in the context of Spring. I use the Spring Cache annotations instead.
When using JSR107, the key used is a GeneratedCacheKey. So that's what you should put in your cache. Not the toString() of it. Note that SimpleKeyGenerator isn't returning a GeneratedCacheKey. It returns a SimpleKey which is the key used by Spring when using its own cache annotations instead of JSR-107. For JSR-107, you need a SimpleGeneratedCacheKey.
Then, if you want to preload the cache, just call getAllBooks before needing it.
If you want to preload the cache in some other way, a #javax.cache.annotation.CachePut should do the trick. See its javadoc for an example.
As #Henri suggested, we can use the cacheput. But for that we need methods. With the below we can update the cache very similar to the cacheput,
//overloaded method both id and cat is available.
List<Object> bookIdCatCache = new ArrayList<>();
bookIdCatCache.add(bookId);
bookIdCatCache.add(deviceCat);
Object bookIdCatCacheKey = SimpleKeyGenerator.generateKey(bookIdCatCache.toArray(new Object[bookIdCatCache.size()]));
cacheManager.getCache("books.byidAndCat").put(bookIdCatCacheKey , bookListWithIdAndCat);
//overloaded method only id is there
List<Object> bookIdCache = new ArrayList<>();
String nullKey = null
bookIdCache.add(bookId);
bookIdCache.add(nullKey);
Object bookIdCacheKey = SimpleKeyGenerator.generateKey(bookIdCache.toArray(new Object[bookIdCache.size()]));
cacheManager.getCache("books.byidAndCat").put(bookIdCacheKey , bookListWithId);
//Not correct(My previous implementation)
String cacheKey = SimpleKeyGenerator.generateKey(bookId, bookCategory).toString();
//Correct(This is getting from spring)
Object cacheKey = SimpleKeyGenerator.generateKey(bookIdCatCache.toArray(new Object[bookIdCatCache.size()]));
I am using Spring REST template to consume a rest api. I am invoking a rest endpoint in order to create something and I have an Object like the following
Object{
String a;
int b;
Map<String,List<String> data;
}
restTemplate.exchange(endpoint, HttpMethod.POST,
new HttpEntity<>(object, headers), Object.class);
I do see that this object contain the correct values however in the end the Object is saved but data is null.
From what I've seen JsonMapper.convertToJson(object) doesn't convert the data property with type Map. Can I replace it with another converter?
Is there a problem with the mapping converters? should I create one for doing this?
Thanks
I am developing a Spring Rest application. One of my methods is that:
#RequestMapping(method = RequestMethod.GET)
public #ResponseBody
Collection<Configuration> getConfigurationInJSON() {
Collection<Configuration> confList = new ArrayList<Configuration>();
...
I fill my confList and send it for GET request, it works. However when I want to keep that confList in a HashMap and send it after got it's entrySet as like that:
#RequestMapping(method = RequestMethod.GET)
public
#ResponseBody
Collection<Configuration> getAllConfigurationsInJSON() {
return configurationMap.values();
}
It gives me 406 error, so it means there is a wrong. What are the differences between that collections and why the second one is not same with first example?
For the sake of simplicity, can you just copy the values() collection?
new ArrayList<Configuration>(configurationMap.values());
Only thing that comes to my mind is that Spring expects mutable collection, but don't really understand why. Hard to say without debugging, try enabling org.springframework.web full logging.
The obvious difference is that configurationMap.values() is a Set.
You need to check if the JSON marshaller expects a List to be returned and is not able to marshal Set instances, as the marshaller will check the actual type of the returned value instead of the declared return type of the method, which is Collection.
By the way, isn't there any clue in the logs about this ?