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.
Related
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 developing REST api using Spring Boot. I've a controller which accepts POST requests.
http://localhost:8085/carride/end-ride
In the above request i want to access the parameter ride_transection_id for finding particular transection object and also some other value as well.
So basically i have 3 way to do that.
1. i can use #PathVariable
#RequestMapping(value = "/end-ride", method = RequestMethod.POST)
public ResponseEntity<?> endRide(#PathVariable("ride_transection_id") long ride_transection_id,#RequestBody
SomeDTORequest someDTORequest ) {
//find transaction using path varibale
}
2.i can use #RequestParam
#RequestMapping(value = "/end-ride", method = RequestMethod.POST
public #ResponseBody item getitem(#RequestParam("ride_transection_id")
long ride_transection_id,#RequestBody SomeDTORequest someDTORequest ){
//find transaction using RequestParam varibale
}
i can use DTO Object SomeDTORequest and accept ride_transection_id into that with other value as well.
#RequestMapping(value = "/end-ride", method = RequestMethod.POST)
public ResponseEntity<?> endRide(#RequestBody SomeDTORequest someDTORequest ) {
//find transaction using path someDTORequest .getID()
}
i am little bit confuses.just want ask which is safest and right way to access the ride_transection_id ?
thanks
You can use any of them but every way is designed for a certain use.
Path variable:
is used when you need to access an entity using a certain field for example i want to access an order and this order is defined by id so to access this order i need the following request Get /order/{id}
Request Parameter:
when you want to send a specific variable or flag for a certain method
for example Get /orders?is_shipped=true, so this will get all shipped orders or you may need orders at certain page Get /orders?page=1
Request body:
when you need to update the entity by the put or patch request as you will update the entity using the entity's json representation which can be send through the request body
for example PUT /orders/{id}
body: {"title": "order_1"}
then the order with id {id} will be updated with the new title
Spring data rest
See also
Basically, all these 3 methods are fine. But if you want to develop or design RESTful services with best practices, I strongly recommend you should provide the querying service with #PathVariable and GET method such as GET /tickets/12. Otherwise, to digest request body with #RequestBody annotation to retrieve querying criteria for POST method is the second suggestion.
Because POST method is usually to be used for creating something. And for querying something, both #PathVariable and #RequestParam annotations are suitable for GET method. More specifically, #RequestParam is often to be used in Filtering, Sorting and Searching results. For example:
Filtering: GET /tickets?state=open - Here, state is a query parameter that implements a filter.
Sorting: GET /tickets?sort=-priority,created_at - Retrieves a list of tickets in descending order of priority. Within a specific priority, older tickets are ordered first.
Searching: GET /tickets?state=closed&sort=-updated_at - Retrieve recently closed tickets.
Please also refer to this article Best Practices for Designing a Pragmatic RESTful API.
Hope this helps you! :)
I need to implement PATCH functionality at my Spring #RestController.
I saw a lot of questions and the most common approach is to use a plain Java Map to do this. Map that allows null helps to solve the issue with null or absent values because it looks like impossible to implement on POJO.
Is there at Spring any out of the box functionality that helps to reflect values from Map to the existing model object or I have to implement it by myself.. for example using Jackson and so on ?
I can share My implementation of PATCH, hope this helps some one in some way. I have a client which has six fields like ( name, type, address fields , ID, Number, postcode), I can edit the client and change anything.
this is also an elaboration on the question with a partial answer ( or a complete one if there is no other way than the two below) Or perhaps PATCH is supposed to be done differently
clientService is just a service which holds the ClientRepository
#RequestMapping(value = "/{id}", method = RequestMethod.PATCH ,produces = {"application/vnd.api+json"} )
ResponseEntity<Resource<Client>> editClient(#PathVariable("id") Integer id,#RequestBody Client editedClientFromBrowser) {
// the id is the ID of the client that I was editing..
//i can use this to retrieve the one from back end
// editedClientFromBrowser is the changed Client Model Object
//from the browser The only field changed is the one
// that was changed in the browser but the rest of
//the object is the same with the ID as well
logger.info("Edit Client reached");
//retreive the same Client from backend and update and save it
// Client client = clientService.getClient(id);
// if (client == null) {
// throw new EntityNotFoundException("Client not found - id: " + id);
// }else{
// change the field that is different from the
//editedClientFromBrowser and then saveAndFlush client
//}
//OR saveAndFlush the editedClientFromBrowser itself
clientService.saveAndFlush(editedClientFromBrowser);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
now another method I read on (http://www.baeldung.com/http-put-patch-difference-spring) and tried:
#RequestMapping(value = "/{id}", method = RequestMethod.PATCH ,
produces = {"application/vnd.api+json"} )
ResponseEntity<Resource<Client>> editClient(#PathVariable("id") Integer id,
#RequestBody Map<String, Object> updates)
this one does give me a hashMap. but it gives me each and every field. even the ones I did not change. So, is that really beneficial? No idea seriously, may be it is lighter than getting the whole client object back.
I would have liked if I get only the hashmap of one or two fields which I did change. that would have been more in line with PATCH i think. Can I improve my two implementations in some way?
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 ?
A 3rd party is sending me part of the data to fill in my domain object via a query string. I need to partially fill in my domain object, and then have the user fill in the rest via a form. I don't have any control over the query string parameters coming in, so I can't change those, but I'd really like to be able to use Spring MVC's data binding abilities, rather than doing it by hand.
How can I do this?
To add some complication to this, some of the parameters will require extensive processing because they map to other objects (such as mapping to a user from just a name) that may not even exist yet and will need to be created. This aspect, I assume, can be handled using property editors. If I run into trouble with this, I will ask another question.
Once I have a partially filled domain object, passing it on to the edit view, etc. is no problem, but I don't know how to properly deal with the initial domain object population.
The only thing I have been able to come up with so far is to have an extra class that has it's properties named to match the inbound query parameters and a function to convert from this intermediary class to my domain class.
This seems like a lot of overhead though just to map between variable names.
Can you not just have the getter named differently from the setter, or have 2 getters and 2 setters if necessary?
private int spn;
// Standard getter/setter
public int getSpn() {
return spn;
}
public void setSpn(int spn) {
this.spn = spn;
}
// More descriptively named getter/setter
public int getShortParameterName() {
return spn;
}
public void setShortParameterName(int spn) {
this.spn = spn;
}
Maybe that is not standard bean convention, but surely would work?