GET method: How to convert snake_case query string to camelCase DTO - spring

I use snake_case DB columns and camelCase DTO.
And our team want to use snake_case when we code React component.
Because of it, I added #JsonNaming on DTO. But it works when I send Json data, as you know.
Is there any annotation or setting similar to #JsonNaming?
Here is my postman data and sample codes.
Debug data: sampleName=name, sampleDesc=null.
// Controller
#RestController
#RequestMapping("/sample")
public class SampleController {
#Autowired
private SampleService sampleService;
#GetMapping
public Result getSampleList(SampleDTO param) throws Exception {
return sampleService.getFolderList(param);
}
#PostMapping
public Result insertSample(#RequestBody SampleDTO param) throws Exception {
// this method works well with #JsonNaming
return sampleService.insertFolder(param);
}
}
// DTO
#Setter
#Getter
#NoArgsConstructor
#JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
#Alias("SampleDTO")
public class SampleDTO {
#NotNull
private Long sampleNo;
#NotBlank
private String sampleName;
private String sampleDesc;
#Builder
public SampleDTO(Long sampleNo, String sampleName, String sampleDesc) {
this.sampleNo = sampleNo;
this.sampleName = sampleName;
this.sampleDesc = sampleDesc;
}
}

I had the same problem and didn't find an annotation for this but maybe you can use #ConstructorProperties like this in your DTO's constructor:
#ConstructorProperties({"sample_no","sample_name","sample_desc"})
public SampleDTO(Long sampleNo, String sampleName, String sampleDesc) {
this.sampleNo = sampleNo;
this.sampleName = sampleName;
this.sampleDesc = sampleDesc;
}

Related

How to use #RequestParam with DTO

I have to make GET method with a DTO.
But when I code like this↓, an error occurs.
org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'param' for method parameter type SampleDTO is not present
After checking that error, I figured out I need add option #RequestParam(required=false).
Then, I restarted tomcat.
Although there was no more error, my param was null(I actually sent sample_name).
And I tried to use both of no annotation and #ModelAttribute.
Both of them occurs same error↓
Caused by: java.lang.NoSuchMethodError: org.springframework.beans.BeanUtils.getResolvableConstructor(Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
What should I do? plz give me advice.
I don't know best way handling DTO.
Because I usually coded using HashMap actually.
Here is my example code.
//Controller sample
point: insertSample method works well.
#RestController
#RequestMapping("/sample")
public class SampleController {
#Autowired
private SampleService sampleService;
#GetMapping
public Result getSampleList(#RequestParam SampleDTO param) throws Exception {
// (#RequestParam(required=false) SampleDTO param)
// (#ModelAttribute SampleDTO param)
// (SampleDTO param)
return sampleService.getFolderList(param);
}
#PostMapping
public Result insertSample(#RequestBody SampleDTO param) throws Exception {
return sampleService.insertFolder(param);
}
}
// DTO sample
#Getter // I didn't attach #Setter because of #Builder.
#NoArgsConstructor
#JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
#Alias("SampleDTO")
public class SampleDTO {
#NotNull
private Long sampleNo;
#NotBlank
private String sampleName;
private String sampleDesc;
#Builder
public SampleDTO(Long sampleNo, String sampleName, String sampleDesc) {
this.sampleNo = sampleNo;
this.sampleName = sampleName;
this.sampleDesc = sampleDesc;
}
}
In order to bind request parameters to object you need to have standard getters/setters in your DTO class. Add #Setter to your method, then you can bind without even any annotation.
#GetMapping
public Result getSampleList(SampleDTO param) throws Exception {
return sampleService.getFolderList(param);
}
#GetMapping
public Result getSampleList(#RequestParam("param") SampleDTO param) throws Exception {
// (#RequestParam(required=false) SampleDTO param)
// (#ModelAttribute SampleDTO param)
// (SampleDTO param)
return sampleService.getFolderList(param);
}
}
Try like this, You should have to designate variable

Why does MongoRepository save return an empty json and save empty value when a variable is not empty?

I have a simple document:
#Document
#NoArgsConstructor
#AllArgsConstructor
#Builder
#ToString
public class ProductUnit {
#Id
String id;
private String name;
private Integer price;
private LocalDateTime localDateTime;
}
Simple MongoRepository :
public interface productRepo extends MongoRepository<ProductUnit,String> {
ProductUnit deleteByName(String name);
List<ProductUnit> findByPrice(Integer price);
}
and Service :
#Service
public class productServiseImpl implements productServise {
#Autowired
productRepo repository;
#Override
public ProductUnit saveOrUpdate(ProductUnit productUnit) {
System.out.println("inside save or update");
return repository.save(productUnit);
}
#Override
public List<ProductUnit> findAll() {
return repository.findAll();
}
#Override
public ProductUnit deleteUnitByPrice(String name) {
return repository.deleteByName(name);
}
#Override
public List<ProductUnit> findByPrice(Integer price) {
return repository.findByPrice(price);
}
}
Now , inside RestController , I pass id through a post request and use a random class to generate a random value of the price and name .At this stage everything is fine, i.e. all values were initialized correctly, but when it comes to service.saveOrUpdate(forSave) It stores the value incorrectly, i.e. the request returns an empty json and the findAll method returns a list of empty json.Can you tell me what the error is? thanks
#RestController
public class productUnitRestController {
#Autowired
productServise service;
#Autowired
Supplier<MetaInfGenerator> generatorSupplier;
#GetMapping(path = "/all")
public List<ProductUnit> getAllProoduct(){
return service.findAll();
}
#PostMapping(path = "/products")
public ProductUnit createProoduct(#RequestParam("id") Optional<String> newId){
System.out.println("***** iside PostMapping ******");
MetaInfGenerator generator = generatorSupplier.get();
System.out.println("***** supplier PostMapping ******");
ProductUnit forSave = ProductUnit.builder()
.id(newId.get())
.name(generator.getRandomString())
.price(generator.getRandomInteger())
.localDateTime(LocalDateTime.now()).build();
System.out.println(forSave);
return service.saveOrUpdate(forSave);
}
}

How to send model attribute to spring controller using Postman

I want to test my controller using postman but don't know how to send a model attribute using postman.
I tried to send all attributes in row json fornamt and x-www-form-urlencoded in body but it is not working for me, I didn't understand where i'm getting wrong
My controller class looks like :
#RestController
public class DemoController {
#Autowired
private DemoService demoService;
#RequestMapping(value = "/userDetail", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON })
public String testme(
ModelMap model,
#ModelAttribute("inputParameter") InputParameter inputParameter,
BindingResult result) {
return demoService.getDetail(inputParameter);
}
}
Model Class :
public class InputParameter {
private String id;
private String name;
private String number;
private String address;
private String pass;
}

JSON field Desrializing to lowercase in Spring Boot

I have a Spring Boot Controller -
#RestController
public class UserController {
#PostMapping
#ResponseStatus(CREATED)
public UserResponse register( #Valid #RequestBody UserRequest userRequest) {
//return ....
}
}
Below is UserRequest.java
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
public class UserRequest {
private String email;
//other property
}
I am sending below json in request body -
{
"email" : "TEST#Example.com",
//some other fields.
}
Sometime client send email in uppercase or in camel case so in userRquest I want to change value of email field to lowercase like test#example.com while de serializing to UserRequest Object.
Is there any easy way to do this. Can I introduce my own annotation like #ToLowerCase how I can create my own annotation and use that at field level in UserRequest.
There is no easy way just by introducing a new annotation #ToLowerCase,
because then you would also need to implement some annotation processor
for doing the real conversion work.
But you can achieve your goal in a slightly different way.
In your UserRequest class annotate the email property
with #JsonDeserialize and specify a converter there.
#JsonDeserialize(converter = ToLowerCaseConverter.class)
private String email;
You need to implement the converter class by yourself,
but it is easy by extending it from StdConverter.
public class ToLowerCaseConverter extends StdConverter<String, String> {
#Override
public String convert(String value) {
return value.toLowerCase();
}
}
Jackson will use the setter methods in your class.
Perform the conversion to lower case in the setter.
For example
public void setEmail(String newValue)
{
email = StringUtils.lowerCase(newValue);
}
StringUtils is an apache commons class.
You can make a general StringDeserializer and register it in ObjectMapper as shown below:-
StringDeserializer class
public final class StringDeserializer extends StdDeserializer<String> {
public StringDeserializer() {
super((Class<String>) null);
}
#Override
public String deserialize(JsonParser parser, DeserializationContext context) throws IOException {
JsonToken token = parser.getCurrentToken();
if (token == JsonToken.VALUE_STRING) {
String text = parser.getText();
return text == null ? null : text.toLowerCase().trim();
}
return null;
}
}
JacksonConfiguration class
#Configuration
public class JacksonConfiguration {
#Autowired
void mapper(ObjectMapper mapper) {
mapper.registerModule(initModule());
}
private Module initModule() {
SimpleModule module = new SimpleModule();
module.addDeserializer(String.class, new StringDeserializer());
return module;
}
}
The above code makes jackson deserialize all strings to lowercase and trimmed.

JAXB Error while using in SpringREST to return a ArrayList of a domain object

I am trying to use JAXB in Spring RESTful webservice.
My code is as follows:
#RequestMapping(value = "/countries",
method = RequestMethod.GET,
headers="Accept=application/xml, application/json")
public #ResponseBody CountryList getCountry() {
logger.debug("Provider has received request to get all persons");
// Call service here
CountryList result = new CountryList();
result.setData(countryService.getAll());
return result;
}
The CountryList.java class looks like:
#XmlRootElement(name="countries")
public class CountryList {
#XmlElement(required = true)
public List<Country> data;
#XmlElement(required = false)
public List<Country> getData() {
return data;
}
public void setData(List<Country> data) {
this.data = data;
}
}
The Country.java looks like:
#XmlRootElement(name="country")
public class Country {
private Calendar createdDt;
private String updatedBy;
private String createdBy;
private Long id;
private String countryName;
private Calendar updatedDt;
// getters and setters for all attributes goes here
}
Now, when I access the method getCountry(), I am getting the following exception
Caused by: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "data"
this problem is related to the following location:
at public java.util.List com.cisco.bic.services.model.CountryList.getData()
at com.cisco.bic.services.model.CountryList
this problem is related to the following location:
at public java.util.List com.cisco.bic.services.model.CountryList.data
at com.cisco.bic.services.model.CountryList
Would anyone has any idea why is this error coming. Am I doing anything wrong in the annotaion part ??
Please help.
Regards
Saroj
You can't annotate both the getter/setter and the field, you need to decide on one of them.

Resources