Spring MVC Reflection - invoke Pojo setters - spring

I have a controller that is supposed to update any object in the database, how can I make it so that that objects setters are called based on the property keys of that object?
I have heard that reflection might be the answer to my prays, is that so, and if so, what is the most elegant way to achieve what I'm looking for?
Object entity = repository.findOne(id);
for (Object key : request.getParameterMap().keySet()) {
//invoke setter-method of entity
//for example, if key == "name" , i want entity.setName() to be called
}
repository.save(entity)
I'm running Spring MVC. If more info is needed please let me know!
UPDATE, working code:
Object entity = repository.findOne(id);
BeanWrapper beanWrapper = new BeanWrapperImpl(entity);
for (Object key : request.getParameterMap().keySet()) {
beanWrapper.setPropertyValue(key.toString(), request.getParameterMap().get(key));
}
repository.save(entity);

Perhaps it would be better to the same data binding facilities that Spring MVC uses to implement #ModelAttribute, etc.
new WebDataBinder(entity).bind(new ServletRequestParameterPropertyValues(request));
Note that this approach imposes specific format of parameter names, as described in 5.4 Bean manipulation and the BeanWrapper.

Related

How to decide a type of GetMapping method in Spring boot?

I'm spring boot learner and trying to clone-code a website. Below is a code to get a data of the specific content.
#GetMapping("/api/articles/{id}")
public List<Article> takeArticle() { return articleRepository.findAllByOrderByModifiedAtDesc();}
Then the ARC shows whole data of contents which I've already posted, but I want a specific data according to the id value. I think the problem is the type of takeArticle() method. So which type should be used for the method above to fulfill my purpose?
#GetMapping("/api/articles/{id}")
public Article takeArticle(#PathVariable Integer id) {
return articleRepository.findById(id).orElseThrow(() -> {
// throw Not found exception if article doesn't exists with given id
});
}
By the way you shouldn't use repository interfaces directly in your controller layer. Use service layer between repository and controllers.

What is the best way to populate Entity from DTO

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.

Something similar to Grails Domain properties field in Spring?

I have started a new project in Spring Boot after using Grails for 4 years.
In Grails I have used properties field of an instance of a domain class to update the associate row in a db table. The assignment of domain.properties was usually done inside a service.
The properties field was set with data coming from a web form.
This approach allows to update a domain instance with a single line, instead of writing n assignemnt, where n is the number of the attributes defined in the domain class.
Now the question.. there is something similar in Spring?
I would like to do something similar in Spring:
update(Long radioId,Map properties) {
// get the radio to be update from the db
Radio radio = getRadio(radioId)
radio.properties = properties
save(radio)
}
I add some detail,
My controller
public ModelAndView updateRadio(Radio radio) {
radioService.update(radio);
return new ModelAndView("redirect:/superadmin/radio/"+radio.getIdentifier()+"/zoom");
}
My Service
#Service
public class RadioService {
...
public void update(Radio radio) {
assert radio.getId() != null;
radioRepository.save(radio);
}
...
}
Now if the web form does not explicity send all the fields defined in Radio I have problem since I will loose the value of the field already stored.
If I could write somthing like that
public void update(Map radioProperties,Long radioId) {
Radio radio = radioRepository.findById(radioId);
radio.properties = radioProperties // only properties present in this map will be update (in grails)
radioRepository.save(radio);
}
it would be great.
In the latter method only the properties in the map (ence in the web form) will be updated, and the other store field of the radio instance will be untouched.
Take a look at Spring Data JPA, it can load domain objects by id, bind incoming request parameters to domain objects and also automagic you some CRUD repositories.

Spring JPA Update operation

I am working on Spring JPA. As part of it, I have to update an entity ignoring few attributes. The following code is in effort to implement the update operation.
#Transactional
public void updateDMove(DTCRto jsonRto){
//copyProperties(Object source, Object target, String[] ignoreProperties)
DMove dMoveDB = dMoveRepo.findDMove(jsonRto.getLn(), jsonRto.getDriver(), jsonRto.getType());
DMove dMoveRto = jsonRto.convertToDMove(jsonRto);
BeanUtils.copyProperties(dMoveRto,drayMoveDB, new String[] {"moveId", "created","lastchange","locations","status"});
dMoveRepo.save(dMoveDB);
}
DMove : Model class which needs to be updated.
dMoveRepo : respective repository class.
dMoveRto : incoming object.
dMoveDb : object existing in the database.
moveId : is the PK in the DMove class.
Can anyone suggest me what is the way to implement the update operation in Spring JPA ?
Thanks.
detached entity passed to persist means that hibernate doesn't recognize the entity you passed to update, because dMoveDB isn't a persistent object, you lost that when you used this line BeanUtils.copyProperties(dMoveRto,drayMoveDB, new String[] {"moveId", "created","lastchange","locations","status"});
I suggest you remove the moveId so the entity you try to update keeps its orginal primary key and remains as a persistent object.
One last thing, you have to make sure that the object you get from dMoveRepo.findDMove(...) isn't null

#Cacheable : how to pass a new attribute that can be used in my own KeyGenerator?

I need your expertise :)
I'm working on a application where method calls on a service need to be authenticated.
That means I want each method call to be cached with a key containing the username (to avoid for an unauthorized user to retrieve information cached by an authorized one).
With a personnalized KeyGenerator, all works fine.
Example of my key : username:USERNAME.appVersion:VERSION.METHOD.PARAM1.etc
But at some location, I got methods that retrieve a national content : this one will be the same for each user. And I want to avoid a cache key for each user asking for this content.
Example : appVersion:VERSION.METHOD.PARAM1.etc
So when I'm positioning my #Cacheable annotations, is there any way to set a new parameter in it ? The Key Generator will be able to catch it and know if he had to prefix the cache key name with user information or not.
Thanks for your help :)
Take care
I don't really understand what you're saying by "set a new parameter in it". That parameter should come from somewhere right?
KeyGenerator gives you access to the Method, the actual instance and the method arguments. You may want to have a specific KeyGenerator for this particular cache operation which is something that will be available as from Spring 4.1 but in the mean time you can implement a composite that invokes the right KeyGenerator instance based on the method or, for instance, an annotation you have created to flag it.
Thank you snicoll, that was crystal clear and you really helped me a lot :)
Waiting for Spring 4.1, my team and I decided to use a custom #SharedCache annotation.
Here is some code samples to help if someone is in the same situation.
Given an existing custom GenericKeyGenerator (he's building a custom cache key for each cached method invocation)
We have a new custom AuthenticatedGenericKeyGenerator : he's inherited from GenericKeyGenerator and simply prefixing the cache key with user information
The application is now using AuthenticatedGenericKeyGenerator by default :
<cache:annotation-driven key-generator="keyGenerator"/>
<bean id="keyGenerator" class="your.package.AuthenticatedGenericKeyGenerator" />
AuthenticatedGenericKeyGenerator.java in details :
public class AuthenticatedGenericKeyGenerator extends GenericKeyGenerator {
public AuthenticatedGenericKeyGenerator() {
super(...);
}
#Override
public Object generate(final Object target, final Method method, final Object... params) {
String cacheKey = super.generate(target, method, params).toString();
if(!method.isAnnotationPresent(SharedCache.class)) {
cacheKey = "user:" + some user information + "." + cacheKey;
}
return cacheKey;
}
}
Our custom #SharedCache annotation :
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#Documented
public #interface SharedCache {
}
Now we just have to annotate #Cacheable methods with an extra #SharedCache if we want the cache key to be shared and not be unique (with an user id for example).

Resources