Complex Json to Nested POJO spring MVC - java

I am trying to get the following Json into POJOS using #RequestBody Instance instance
{
"service_id": "service-id-here",
"plan_id": "plan-id-here",
"context": {
"platform": "cloudfoundry",
"some_field": "some-contextual-data"
},
"organization_guid": "org-guid-here",
"space_guid": "space-guid-here",
"parameters": {
"agent_name": 1,
"url": "foo",
"credential": "asdasd",
"ia_url": "asdasd"
}
}
Below are my POJOs
Instance
public class Instance {
#JsonProperty(value = "service_id")
String serviceId;
#JsonProperty(value = "plan_id")
String planId;
//TODO : Replace with Context class when the spec defines things clearly
#JsonProperty(value = "context")
Object context;
#JsonProperty(value = "organization_guid")
String organizationGuid;
#JsonProperty(value = "space_guid")
String spaceGuid;
#JsonProperty(value = "parameters")
Parameters parameters;
}
Parameters
public class Parameters {
#JsonProperty(value = "agent_name")
String agentName;
#JsonProperty(value = "url")
String url;
#JsonProperty(value = "credential")
String credential;
#JsonProperty(value = "ia_url")
String iaUrl;
}
I use #JsonProperty everywhere. Is there any way to get underscore separated json keys into java's naming convention for variables (Camelcase)??
I tried using #JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) to my POJO classes instead of the #JsonProperty for each parameter. I just get an empty json {} in instance. What am I missing here?

Yes, is this possible using PropertyNamingStrategy class through JsonNaming annotation
Ex:
#JsonNaming(PropertyNamingStartergy.LowerCaseWithUnderscoresStrategy.class)
class Class_name{
...
}
//----
The below code has updated. In that code am using
PropertyNamingStrategy.SnakeCaseStrategy
Working code (TESTED).
Getters and setters are important for this to work. But #JsonProperty does not require them
User.java
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class User {
private int id;
private String beanName;
private Role role;
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
Role.java
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class Role {
private int id;
private String roleName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
Here is the Controller
#RestController
#RequestMapping("/test")
public class NamingController {
#RequestMapping(value="/jsontopojo", method = RequestMethod.POST)
public ResponseEntity<User> jsontopojo(#RequestBody User nam) {
return new ResponseEntity<User>( nam, HttpStatus.OK);
}
}

Related

Getting data by a particular key

I am a beginner in java spring boot and I want to find a country by id but it's giving me this error when I test it in Postman meanwhile a country with the said id 4 exist in MySQL daabase. I don't know what went wrong "timestamp": "2022-05-11T16:54:40.909+00:00",
"status": 404,
"error": "Not Found",
"path": "/country/findById/4"
Here is my Entity class
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class)
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String code;
private String capital;
private String description;
private String nationality;
private String continent;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getCapital() {
return capital;
}
public void setCapital(String capital) {
this.capital = capital;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getNationality() {
return nationality;
}
public void setNationality(String nationality) {
this.nationality = nationality;
}
public String getContinent() {
return continent;
}
public void setContinent(String continent) {
this.continent = continent;
}
public List<State> getStates() {
return states;
}
public void setStates(List<State> states) {
this.states = states;
}
#OneToMany(mappedBy = "country")
private List<State> states;
}
Here is my Repository
#Repository
public interface CountryRepository extends JpaRepository<Country,Integer> {
}
Here is my Service
#Service
public class CountryService {
#Autowired
private CountryRepository countryRepository;
public List<Country> getAllCountry() {
return countryRepository.findAll();
}
public void saveCountryInfo(Country country){
countryRepository.save(country);
}
public Optional<Country> getCountryById(Integer id){
return countryRepository.findById(id);
}
}
Here is my Controller
#Controller
public class CountryController {
#Autowired
private CountryService countryService;
#GetMapping("/countries")
public String getCountry(Model model){
List<Country> countryList = countryService.getAllCountry();
model.addAttribute("countries",countryList);
return "country";
}
#PostMapping("/countries/addNew")
public String saveInfo(Country country){
countryService.saveCountryInfo(country);
return "redirect:/countries";
}
#GetMapping("/country/findById")
#ResponseBody
public Optional<Country> getCountryById(Integer id){
return countryService.getCountryById(id);
}
}
But this is the error from postman
"timestamp": "2022-05-11T16:54:40.909+00:00",
"status": 404,
"error": "Not Found",
"path": "/country/findById/4"
In your controller, you are not accepting the id as path variable
#GetMapping("/country/findById/{id}")
#ResponseBody
public Optional<Country> getCountryById(#PathVariable("id") Integer id){ //Bind PathVariable id to id
return countryService.getCountryById(id);
}
{xyz} this works as placeholder/variable
#PathVariable("xyz") Integer id This is how Spring will bind that variable to your parameter

Sending JSON data over post man giving the Failed to evaluate Jackson deserialization for type in Spring Boot Jpa project

Course.java
package com.example.jpa_training.JPAD.model;
#Entity
#Table(name = "COURSE")
public class Course implements Serializable{
public Course() {}
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
private String description;
#ManyToOne(targetEntity=Department.class)
#JsonIgnore
private Department department;
#ManyToMany(mappedBy="courses", targetEntity=Student.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JsonIgnore
private Set<Student> students = new HashSet<Student>();
#ManyToOne(cascade = CascadeType.MERGE)
#JoinColumn(name="professor_id")
#JsonManagedReference
private Professor professor;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Professor getProfessor() {
return professor;
}
public void setProfessor(Professor professor) {
this.professor = professor;
}
public Set<Student> getStudents() {
return students;
}
public void addStudent(Student student) {
this.students.add(student);
}
public void removeStudent(Student student) {
this.students.remove(student);
}
#OneToMany(mappedBy = "course", fetch = FetchType.LAZY)
private Set<Review> reviews;
}
Review.java
#Entity
public class Review implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long reviewId;
#ManyToOne
private Course course;
private String reviewDescription;
private double courseRating;
public Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
public String getReviewDescription() {
return reviewDescription;
}
public void setReviewDescription(String reviewDescription) {
this.reviewDescription = reviewDescription;
}
public double getCourseRating() {
return courseRating;
}
public void setCourseRating(double courseRating) {
this.courseRating = courseRating;
}
}
Postman Input
{
"course": {
"id": 4,
"name": "Data Analysis",
"description": "Just take it",
"professor": {
"name": "Kapil Dev",
"qualification": "M.Tech",
"department": {
"deptId": 1,
"deptName": "Big Data",
"buildingName": "DS-04"
}
}
},
"reviewDescription": "Good course, nice teaching",
"courseRating": 0.0
}
Error Log
Failed to evaluate Jackson deserialization for type [[simple type, class com.example.jpa_training.JPAD.model.Review]]: com.fasterxml.jackson.databind.JsonMappingException:
2020-12-30 11:45:00.869 WARN 11152 --- [nio-8080-exec-2] .c.j.MappingJackson2HttpMessageConverter : Failed to evaluate Jackson deserialization for type [[simple type, class com.example.jpa_training.JPAD.model.Review]]: com.fasterxml.jackson.databind.JsonMappingException:
2020-12-30 11:45:00.869 WARN 11152 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type
'application/json;charset=UTF-8' not supported]
Tried solutions
Using #JsonBackReference and #JsonManagedReference
Using #JsonIdentityInfo and #JsonIgnore
but the error is the same
I can save and retrieve the data from Java but when I send data over postman or using curl command I get the above error, I tried many ways but couldn't fix it
I wouldn't suggest exposing entities directly to your controller. Entities should only contain JPA annotations in your case. You can expose a DTO (Data Transfer Object) to your controller and then map the DTO to the corresponding entity.
ReviewDto
public class ReviewDto {
private String reviewDescription;
private double courseRating;
private CourseDto course;
// getters, setters, etc
}
CourseDto
public class CourseDto {
private Long id;
private String name;
private String description;
// professorDto, getters, setters, etc
}
An example demonstrating how your controller class will be
#RestController
public class DemoController {
private final ReviewDtoMapper reviewDtoMapper;
private final ReviewService reviewService;
public DemoController(ReviewDtoMapper reviewDtoMapper,
ReviewService reviewService) {
this.reviewDtoMapper = reviewDtoMapper;
this.reviewService = reviewService;
}
#PostMapping(value = "demo")
public ResponseEntity<String> postReview(#RequestBody ReviewDto reviewDto) {
final Review review = reviewDtoMapper.mapFrom(reviewDto);
reviewService.save(review);
return ResponseEntity.ok("");
}
}
The class to map from reviewDto to review entity and the opposite.
#Component
public class ReviewDtoMapper {
public ReviewDto mapTo(final Review entity) {
ReviewDto reviewDto = new ReviewDto();
reviewDto.setReviewDescription(entity.getReviewDescription());
// set all the properties you want
return reviewDto;
}
public Review mapFrom(ReviewDto dto) {
Review review = new Review();
review.setReviewDescription(dto.getReviewDescription());
// set all the properties you want
return review;
}
}
Of course, you have to make adjustments according to your needs.
If you like this way of doing things I would suggest you check
MapStruct, it will automatically make the mappers for you.

How to select fields in spring data?

I have entity as follow.
#Entity
#Table(name = "BankProduct")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne
private ProductUseType type;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#ManyToOne
private ProductSerial serial;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ProductUseType getType() {
return type;
}
public void setType(ProductUseType type) {
this.type = type;
}
public ProductSerial getSerial() {
return serial;
}
public void setSerial(ProductSerial serial) {
this.serial = serial;
}
}
My controller is :
#RestController
public class DEmoController {
#Autowired
private ProductRepository productRepository;
#GetMapping("/products")
public Returns products() {
return new Returns(ReturnStatus.SUCCESS.getStatus(), productRepository.findAll(), null);
}
}
It will load both of type and serial of product.
Can I only load type but not to load serial?
I don't want to add fetch=FetchType.LAZY to serial, because if next time I want to load serial but not to load type, it will be terrible.
Check the Projection interface
Create a interface ProductProjection
interface ProductProjection {
String getName();
String getType();
}
and add a method in you Repository
List<ProductProjection> findAllProjection()
That's the whole point of fetch=FetchType.LAZY. It'll not load any of your types/fields until you ask for them explicitly.
Take a look at this question: Link

Annotation #Column package javax.persistence with error in the use spring data

I'm using Spring Data and I'm getting this exception and I'm not understanding why.
The dataCadastro field in the domain is the only field with a different name in the database. In the base is as datacad
Repository
public interface IRepositorioUsuario extends CrudRepository<Usuario, Long> {
}
Service
#Stateless
public class UsuarioService {
#Inject
IRepositorioUsuario usuarioRepositorio;
public void buscar() {
usuarioRepositorio.findAll().forEach(u -> System.out.println(u.getNome()));
}
}
Domain
#Entity
#Table(name="TUSUARIO")
public class Usuario implements Serializable {
private static final long serialVersionUID = -5427866189669150032L;
private Long codigo;
private String nome;
private String login;
private String senha;
#Column(name="datacad") // query error.....
private Date datacadastro;
private Boolean situacao;
#Id
public Long getCodigo() {
return codigo;
}
public void setCodigo(Long codigo) {
this.codigo = codigo;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getSenha() {
return senha;
}
public void setSenha(String senha) {
this.senha = senha;
}
public Date getDatacadastro() {
return datacadastro;
}
public void setDatacadastro(Date datacadastro) {
this.datacadastro = datacadastro;
}
public Boolean getSituacao() {
return situacao;
}
public void setSituacao(Boolean situacao) {
this.situacao = situacao;
}
// Omitting hasCode
Error Query
SQL Error: 1054, SQLState: 42S22
Unknown column 'usuario0_.datacadastro' in 'field list'
select
usuario0_.codigo as codigo1_0_,
usuario0_.datacadastro as datacada2_0_,
usuario0_.login as login3_0_,
usuario0_.nome as nome4_0_,
usuario0_.senha as senha5_0_,
usuario0_.situacao as situacao6_0_
from TUSUARIO usuario0_
With the annotation #Column(name = datacad) in getDataCadastro
Seccess
select
usuario0_.codigo as codigo1_0_,
usuario0_.datacad as datacad2_0_,
usuario0_.login as login3_0_,
usuario0_.nome as nome4_0_,
usuario0_.senha as senha5_0_,
usuario0_.situacao as situacao6_0_
from TUSUARIO usuario0_
It is because you are using #Id annotation on the getter method. And so the jpa looks only at the getters to derive the column names and ignores the #column annotation on the field but starts working when you place it on the getter.
As an exercise, you can move Id annotation to field level and should see it working again.
It is recommended to place jpa annotations either at field level or getters. but not to mix

serializing object using gson and getting LazyInitializationException

This is my Controller method, i am trying to read my database by providing zip, cityname and province name.
#RequestMapping(value = "/retrieve", method = RequestMethod.GET)
public #ResponseBody String retrieveObjectThroughAjax(ModelMap model){
//Calling Service Method to read data according to zip,cityName and province provide
PropertyItems propertyItems=getPropertyTypeandAddressService.readAddressFromZip("H2H-
2N3","Montreal","Quebec");
Gson gson = new Gson();
String json = null;
try{
json = gson.toJson(propertyItems); // serializing object
}catch(Exception e){
logger.error(Constants.METHOD_INSIDE_MESSAGE +"getAuthors",e);
}
logger.debug(json);
return json;
}
}
Service Method
#Service
public class GetPropertyTypeandAddressServiceImpl implements GetPropertyTypeandAddressService{
#Autowired
private GetPropertyTypeandAddressDAO getPropertyTypeandAddressDAO;
#Transactional
public PropertyItems readAddressFromZip(String zipCode,String cityName,String provinceName){
PropertyItems propertyItems=getPropertyTypeandAddressDAO.getAddressFromZip(zipCode, cityName, provinceName);
Hibernate.initialize(propertyItems);
return propertyItems;
}
}
DAO Method
#Repository
public class GetPropertyTypeandAddressDAOimp implements GetPropertyTypeandAddressDAO{
#Autowired
private SessionFactory sessionFactory;
#Override
public PropertyItems getAddressFromZip(String zipCode,String cityName,String provinceName) {
PropertyItems propertyitems = new PropertyItems();
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(PropertyItems.class,"propertyItemsClass");
if(zipCode != null){
criteria.createAlias("propertyItemsClass.address","address");
criteria.add(Restrictions.eq("address.zip",zipCode));
List<PropertyItems> propertyitem = criteria.list();
if(propertyitem.size()>0){
propertyitems = propertyitem.get(0);
}
}
else if(cityName != null){
criteria.createAlias("propertyItemsClass.address","address");
criteria.add(Restrictions.eq("address.city","city"));
criteria.add(Restrictions.eq("city.cityname",cityName));
List<PropertyItems> propertyitem = criteria.list();
if(propertyitem.size()>0){
propertyitems = propertyitem.get(0);
}
}
else if(provinceName != null){
criteria.createAlias("propertyItemsClass.address","address");
criteria.add(Restrictions.eq("address.city","city"));
criteria.add(Restrictions.eq("city.provinces","provinces"));
criteria.add(Restrictions.eq("provinces.provinceName",provinceName));
List<PropertyItems> propertyitem = criteria.list();
if(propertyitem.size()>0){
propertyitems = propertyitem.get(0);
}
}
return propertyitems;
}
}
Console Error
09:53:56,988 ERROR HelloController:567 - Inside Method: getAuthors org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.agilemaple.common.entity.Property.propertyType, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
As requested my Property Items Look like this
Entity:
Propert Items
#Entity
#Table(name="web_property_item")
public class PropertyItems {
#Id
#GeneratedValue
private Integer id;
private String name;
#ManyToOne
#JoinColumn(name="property_type_id")
private PropertyType propertyType;
#OneToOne(mappedBy="propertyItems",cascade=CascadeType.ALL)
private Address address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public PropertyType getPropertyType() {
return propertyType;
}
public void setPropertyType(PropertyType propertyType) {
this.propertyType = propertyType;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
Entity : Property Type
#Entity
#Table(name="web_property_type")
public class PropertyType {
#Id
#GeneratedValue
private Integer id;
private String name;
#ManyToOne
#JoinColumn(name="property_id")
private Property property;
#OneToMany(fetch = FetchType.LAZY,mappedBy="propertyType", cascade=CascadeType.ALL)
private Set<PropertyItems> propertyItems;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Property getProperty() {
return property;
}
public void setProperty(Property property) {
this.property = property;
}
public Set<PropertyItems> getPropertyItems() {
return propertyItems;
}
public void setPropertyItems(Set<PropertyItems> propertyItems) {
this.propertyItems = propertyItems;
}
}
The problem in hibernate. Your field Set of properties has Lazy fetch method, it means that it will try to get when you call method get of this set. When u calling tojson methods, gson calls all get methods of object but in this moment hibernate session is close and hibernate can't open it in controller. I've faced with the same problem but directly on JSP. In a three weeks i resolved it by one more property for hibernate ( in your case) and I write code to opening session in view interceptor. I'm underground just right now, so I can't show property, but in a hour I will edit this answer and add property.
Added:
I remembered ! property is: hibernate.enable_lazy_load_no_trans = true
If it won't help, I will add code of opensessioninviewinterceptor.
#Override
public void addInterceptors(InterceptorRegistry registry) {
OpenSessionInViewInterceptor sessionInViewInterceptor = new OpenSessionInViewInterceptor();
sessionInViewInterceptor.setSessionFactory(sessionFactory());
}

Categories

Resources