Converting request parameters to the Class object - java

I have two POJO classes.
Class 1 is Engine.java:
private String engineId;
public String getEngineId(){
return this.engineId;
}
public void setEngineId(String engineId){
this.engineId = engineId;
}
The Second POJO class is Vehicle.java:
private String type;
private String manufacturer;
private Engine engine;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
I have a REST Controller for providing information of Vechicles (RequestMethod.GET):
#RequestMapping(method = RequestMethod.GET)
public Vehicle getVechileDetails(Vehicle inputVehicle){
Vehicle vehicle = new Vehicle();
// some processing
return vehicle;
}
When I hit this service and provide the Request parameter as type or manufacturer, then the Spring creates the Vehicle object and populates the value of type and manufacturer. But if I provide the value of engineId, then the Spring is not able to create the Engine object such that vehicle.getEngine().getEngineId() != null
Is there any way in which if I invoke my Rest Service like:
http://localhost:8080/Controller/getVehicleDetails?engineId=12345
then the Vehicle is created with Engine having the value of engineId ?

You can get the vehicleId like this (ResponseEntity structure is included):
#RequestMapping(value = "/Controller/getVehicleDetails", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public
#ResponseBody
ResponseEntity<AjaxResponse> controller(#RequestParam(value = "engineId") Long engineId) {
//Do whatever you want with the engineId
return new ResponseEntity<AjaxResponse>(new AjaxResponse("Success"), HttpStatus.OK);
}
But, for POJOs, I have to warn two things:
Make sure your classes implement Serializable
For the fields which are not certain while converting from Java to Javascript or vice versa, like Date variables, You have to set your JsonSerializer class properly
Update: You wanted to get the object from request, so:
#RequestMapping(value = "/Controller/getVehicleDetails", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, , consumes = MediaType.APPLICATION_JSON_VALUE)
public
#ResponseBody
ResponseEntity<AjaxResponse> controller(#RequestBody Vehicle vehicle) {
//vehicle.getEngine().getEngineId()
return new ResponseEntity<AjaxResponse>(new AjaxResponse("Success"), HttpStatus.OK);
}
POJOS:
public class Engine implements Serializable {
//...
}
public class Vehicle implements Serializable {
private Engine engine;
}
JS side:
var vehicle = {
engine: {id: 123}//,
//...
}
//via angularjs:
$http.post("http://localhost:8080/Controller/getVehicleDetails", vehicle).success(function(response) {
console.log("success");
});

Here you can refer , where the request body has JSON which converted to Object
See : For references
#RequestMapping(value = EmpRestURIConstants.CREATE_EMP, method = RequestMethod.POST)
public #ResponseBody Employee createEmployee(#RequestBody Employee emp) {
logger.info("Start createEmployee.");
emp.setCreatedDate(new Date());
empData.put(emp.getId(), emp);
return emp;
}

Related

How to change object properties name when serialize?

I have an object like this.
public class Profile {
private String email;
private String phone;
#SerializedName("userCode")
private String user_code;
public String getEmail() {
return email;
}
public String getPhone() {
return phone;
}
public String getUser_code() {
return user_code;
}
}
This is what I got when I return that object in Rest API
{
"email": "abc#gmail.com",
"phone": 12345678,
"user_code": "742aeaefac"
}
Apparently annotation #SerializedName here did not work, I can understand that it get the object properties name base on its getter name, not in the annotation. If I change the getter name into getUserCode(), it will work as I expected.
I also try to use #JsonProperty but didn't help too.
Can someone explain what is the work here to solve this?
Update the code for serialization process in the controller.
#PostMapping(path = "/login", produces = "application/json")
#ResponseBody
public ClientRepresentation login(#RequestBody LoginRequest login) {
Map<String, Object> resObj = new HashMap<String, Object>();
ProfileResponse profileResponse = userService.findUserProfileByUserCode(login.getUserCode());
//Code logic to process object data...
resObj.put("profile", profileResponse);
return ClientRepresentationBuilder.buildClientRep(HttpStatus.OK.value(), "Success", resObj);
}
ClientRepresentation class
public class ClientRepresentation implements Serializable {
private Integer code;
private String message;
private Object data;
}

Hibernate #Formula returns an old value in PUT response

My Spring boot app has 2 Entities - Document and Card. Card has column dtFrom. Clients have to work with column daysOnDtConfirm (Document.dtConfirm - dtFrom). Annotation #Formula for GET requests works great, but in PUT response returns an old value of daysOnDtConfirm. How return a new value?
#Entity
#Table(name="document")
public class Document extends BaseEntity{
private String name;
#Column(name = "dt_confirm")
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime")
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
#JsonFormat(shape = JsonFormat.Shape.STRING)
private LocalDateTime dtConfirm ;
#Column(name = "contragent_name")
private String contragentName;
....
//CARD
#OneToMany(mappedBy="document" , fetch = FetchType.EAGER)
private List<Card> cards = new ArrayList<Card>();
public List<Card> getCards() {
if (this.cards == null) {
this.cards = new ArrayList<Card>();
}
return this.cards;
}
public void setCard(Card card) {
getCards().add(card);
card.setDocument(this);
}
public int getNrOfCards() {
return getCards().size();
}
....
}
And
#Entity
#Table(name="card")
public class Card extends BaseEntity {
#ManyToOne
#JsonIgnore
#JoinColumn(name = "document_id")
private Document document;
private String name;
private double quantity;
#Column(name = "dt_from")
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDate")
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
#JsonIgnore
private LocalDate dtFrom ;
#Formula("(select IFNULL(DATEDIFF(Document.dt_confirm , dt_from), 0) from
Document where Document.id = document_id )")
private int daysOnDtConfirm;
...
public void setDtFrom(LocalDate dtFrom) {
this.dtFrom = dtFrom;
}
public void setDtFrom(int daysOnDtConfirm) {
if (this.document.getDtConfirm() != null){
LocalDate dateTo = this.document.getDtConfirm().toLocalDate();
this.dtFrom = dateTo.minusDays(daysOnDtConfirm);
}
}
...
}
Service :
#Service
public class DocumentServiceImpl implements DocumentService {
#Autowired
DocumentRepository documentRepository;
#Autowired
CardRepository cardRepository;
...
#Override
#Transactional
public void changeCard(Document document, Card card) {
//IF ID is NULL then isNew==true!!!!
if (card.isNew()){
card.setDocument(document);
card.setDtFrom(card.getDaysOnDtConfirm());
document.setCard(card);
cardRepository.saveAndFlush(card);
}
else{
Card cardEdit = cardRepository.findOne(card.getId());
if (cardEdit != null) {
cardEdit.setDocument(document);
cardEdit.setName(card.getName());
cardEdit.setUnit(card.getUnit());
cardEdit.setQuantity(card.getQuantity());
//cardEdit.setDtFrom(card.getDtFrom());
cardEdit.setDtFrom(card.getDaysOnDtConfirm());
cardEdit.setDescription(card.getDescription());
cardRepository.saveAndFlush(cardEdit);
}
}
#Override
#Transactional
public Document changeDocumentAndCards(Document document) {
Document documentEdit = changeDocument(document);
List<Card> cards = document.getCards();
//check if the same rows in DB and Client, DELETE difference
deleteCardsFromDocument(document);
//if not empty received from client rows then change
if (!cards.isEmpty()) {
for (Card card : cards) {
changeCard(documentEdit, card);
}
}
return documentEdit;
}
...
}
RestController:
#RestController
#RequestMapping("/api/docs")
public class DocController {
#Autowired
DocumentService documentService;
#RequestMapping(value = "",
method = RequestMethod.GET,
produces = {"application/json", "application/xml"})
#ResponseStatus(HttpStatus.OK)
public #ResponseBody
List<Document> getAllDocument(HttpServletRequest request, HttpServletResponse response) {
List<Document> list = new ArrayList<>();
Iterable<Document> documents = this.documentService.getDocumentAll();
documents.forEach(list::add);
return list;
}
....
#RequestMapping(value = "/{id}",
method = RequestMethod.PUT,
consumes = {"application/json", "application/xml"},
produces = {"application/json", "application/xml"})
#ResponseStatus(HttpStatus.OK)
public Document updateDocument(//#ApiParam(value = "The ID of the existing Document resource.", required = true)
#PathVariable("id") Long id,
#RequestBody Document document,
HttpServletRequest request, HttpServletResponse response) {
Document documentEdit = documentService.changeDocumentAndCards(document);
return documentEdit;
}
...
}
The issue seems to come from the changeDocument(Document document) method. The return value of saveAndFlush() call should be assigned back to documentEdit
UPDATE
The issue is that hibernate will not re-calculate the #Formula field after it is updated. It just fetches it from cache.
The only way I managed to get this working on my machine was to refresh the card entity after updating it. For that to work I needed to add an entity manager in the service class.
In your DocumentServiceImpl (actually could be any service class) class add the following:
public class DocumentServiceImpl implements DocumentService {
//...
#PersistenceContext
private EntityManager em;
#Transactional
public void refreshEntity(Object entity) {
em.refresh(entity);
}
Then, you should call this refreshEntity() method after an update, so that hibernate doesn't fetch it from cache.
This way it worked for me. Hope it helps you.

Dynamic Request body REST API Method using swagger

I have use case were I need to get requestBody based on selection of field.below is same code which I was able get the dynamic responseBody Based on selection ProtocolType.Is there is any way that swagger can read the RequestBody Dynamically.
Controller.Java
#ApiOperation(value = "Protocol Account", tags = {"ProtocolAccount"})
#RequestMapping(value = "/protocolAccount/{protocolName}",
method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody public ProtocolAccount getProtocol(#PathVariable String protocolName)
{
return service.getProtocol(protocolName);
}
Service.Java
public ProtocolAccount getProtocol(String protocolName){
ProtocolAccount protocolAccount=new ProtocolAccount();
Object object=ProtocolType.fromMap(protocolName);
protocolAccount.setProtocol(object);
return protocolAccount;
}
POJOs
public class ProtocolAccount
{
String Id;
private Object protocolType
}
public class Protocol{
private String port;
}
public class FTPProtocol extends Protocol{
/*Some Fields*/
}
public class SFTPProtocol extends Protocol{
/*Some Fields*/
}
Enumeration
public enum ProtocolType
{
SFTP("SFTP"), FTPS("FTPS"), AS2("AS2"), FTP("FTP");
private final String value;
private static final EnumMap<ProtocolType,
Object>map = new EnumMap<ProtocolType, Object>(ProtocolType.class);
static{
map.put(ProtocolType.SFTP, new SFTPProtocol());
map.put(ProtocolType.FTP, new FTPProtocol());
map.put(ProtocolType.FTPS,new FTPSProtocol());
}
ProtocolType(String v){
value=v;
}
public static ProtocolType fromValue(String val){
return EnumSet.allOf(ProtocolType.class)
.stream().filter(e->e.value.equals(val))
.findFirst().orElseThrow(()->new IllegalArgumentException(val));
}
public String value(){
return value;
}
public static Object fromMap(String value)
{
return map.get(ProtocolType.fromValue(value));
}
}

Deserialize json ids to list of objects

I am sending ajax json request to my controller using jackson. This is my entity:
#Entity
public class Template implements Serializable
{
private String templateName;
#ManyToMany(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
private List<Action> actions;
//getters setters
}
My JSON looks like:
"{"templateName":"aaa",
"actions":["2", "3"]
}"
Controller:
#RequestMapping(value = "/testCreate", consumes = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody List<ObjectError> testCreate(#Valid #RequestBody final TemplateForm templateForm,
final BindingResult bindingResult)
{
if (bindingResult.hasErrors())
{
return bindingResult.getAllErrors();
}
else
{
//some actions
return EMPTY_LIST;
}
}
How to map action ids from JSON on list of Action object? Thank you.
You can use #InitBinder in case you are using Spring.
Like this:
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(ArrayList.class, "actions",
new ActionEditor(actionService));
}
and ActionEditor will look like:
public class ActionEditor extends PropertyEditorSupport {
private final ActionService actionService;
public ActionEditor(ActionService actionService) {
this.ActionService = actionService;
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
List<Action> facilities = new ArrayList<Action>();
String[] ids = text.split(",");
Set<Long> actionIds = new HashSet<Long>();
for (String id : ids) {
actionIds.add(Long.parseLong(id));
}
facilities.addAll(actionService.list(actionIds));
setValue(facilities);
}}

How to nest #PathVariable in spring-rest?

I have a simple #RestController service that takes query parameters, and spring automatically parses them to an bean:
#ResponseStatus(HttpStatus.OK)
#RequestMapping(value = "/rest", method = RequestMethod.GET)
public MyDTO getGiataHotel(#Valid MyParams p) {
Sysout(p.getId()); //prints "123"
}
public class MyParams {
private int id;
//private SubParams subs;
}
Query: .../rest?id=123
Now I'd like to structure the parameter object with nested classes. How can I achieve this?
public class SubParams {
private String name;
//some more
}
Ideally my query should be: Query: .../rest?id=123&name=test, and the "test" string should go into the SubParams bean.
Is that possible?
You have to register a Custom Covertor if you need to set to a inner class. The change would be following:
#ResponseStatus(HttpStatus.OK)
#RequestMapping(value = "/rest", method = RequestMethod.GET)
public MyDTO getGiataHotel(#ModelAttribute("subParam") MyParams params, #Valid MyParams p) {
//Do stuff
}
The subParam denotes that there is a converter registered for conversion.
public class MyParamsConverter implements Converter<String, MyParams> {
#Override
public MyParams convert(String name) {
MyParams myParams = new MyParams();
SubParams subParams = new SubParams();
subParams.setName(name);
myParams.setSubParams(subParams);
return myParams;
}
}
You can achieve this by using the #ModelAttribute annotation : http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/mvc.html#mvc-ann-modelattrib-method-args (this is not in the Path parameters, but in the requestParams either get/post)
#RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(#ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
maybe u should use RequestMethod.POST, like this
#RequestMapping(value = "/rest", method = RequestMethod.POST)
public ModelAndView getGiataHotel(#ModelAttribute("subparams") SubParams subparams){
SubParams sub=subparams;
//do something...
}

Categories