Spring MVC model binding not working for non public members - java

I have class Product with members like Name, Description... which are private, and I have public getter and setter methods. I tried to make Spring Boot MVC REST controller with POST method but it seams that what I am sending is not bind to model. This is JSON I'm sending to controller:
{
"Name": "proizvod7",
"Description": "neki opis2",
"CategoryId":1,
"Price":"15"
}
This is my code:
#RequestMapping(value = {"","/"},method = RequestMethod.POST,consumes = "application/json",produces="application/json")
public ResponseEntity PostProduct(#RequestBody #Valid Product p, BindingResult result){
if (!result.hasErrors()) {
return new ResponseEntity<>(service.insert(p), HttpStatus.CREATED);
} else
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
}
I figured that out when I created new class ProductDto which has public members Name, Description...
This is working version:
#RequestMapping(value = {"","/"},method = RequestMethod.POST,consumes = "application/json",produces="application/json")
public ResponseEntity PostProduct(#RequestBody #Valid ProductDto productDto, BindingResult result) {
if (!result.hasErrors()) {
Product p = new Product(productDto.Name, productDto.Price, productDto.Description, productDto.CategoryId);
return new ResponseEntity<>(service.insert(p), HttpStatus.CREATED);
} else {
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
}
However project is simple enough so I don't want to introduce DTO classes, but I would like to have private members inside my initial Product class with public getters and setters. Is it possible?
EDIT:
Here is Product class:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "products")
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
public Product(#NotNull String name, BigDecimal price, String description, Long category_id) {
Name = name;
Price = price;
Description = description;
Category_id = category_id;
}
public Long getId() {
return Id;
}
public void setId(Long id) {
Id = id;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public BigDecimal getPrice() {
return Price;
}
public void setPrice(BigDecimal price) {
Price = price;
}
public String getDescription() {
return Description;
}
public void setDescription(String description) {
Description = description;
}
#NotNull
private String Name;
private BigDecimal Price;
private String Description;
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
public Long getCategory_id() {
return Category_id;
}
public void setCategory_id(Long category_id) {
Category_id = category_id;
}
#Column(name = "category_id",nullable = true)
private Long Category_id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="category_id",updatable = false,insertable = false)
private Category category;
}

It seems your field names are breaking the deserializing logic. I do not know why are you trying to use those uppercase field names, but the problem is that when there is a setter for Name field, setName(), Jackson thinks this maps to a field name, not Name, hence the issue.
You should use #JsonProperty()
#JsonProperty("Name")
private String Name;
#JsonProperty("Price")
private BigDecimal Price;
#JsonProperty("Description")
private String Description;
#JsonProperty("categoryId")
#Column(name = "category_id", nullable = true)
private Long Category_id;
Also with #Data annotation from Lombok, you do not need to write all those getters/setters, they are already generated for you.

You can use #JsonSetter at the public setters... something like this
public class MyBean {
private String name;
#JsonSetter("name")
public void setTheName(String name) {
this.name = name;
}
}
You can check more examples here:
https://www.baeldung.com/jackson-annotations
#JsonSetter ref here https://fasterxml.github.io/jackson-annotations/javadoc/2.0.0/com/fasterxml/jackson/annotation/JsonSetter.html

Related

no setter methods in generated mapstruct implementation

I'm experimenting with mapstruct and follow this tutorial:
mapstruct tut
I have this entity:
#Entity
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_company")
#SequenceGenerator(name = "seq_company", allocationSize = 1)
#Column(nullable = false, updatable = false)
private Long id;
private String name;
private String shortName;
public Company() {
}
public Company(Long id, String name, String shortName) {
this.id = id;
this.name = name;
this.shortName = shortName;
}
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 String getShortName() {
return shortName;
}
public void setShortName(String shortName) {
this.shortName = shortName;
}
}
And this is the simple dto:
public class CompanyDto {
#JsonProperty("id")
private Long id;
#JsonProperty("name")
private String name;
#JsonProperty("shortName")
private String shortName;
}
And here is the mapper interface:
#Mapper(componentModel = "spring")
public interface CompanyMapper {
CompanyDto companyToCompanyDto(Company company);
Company companyDtoToCompany(CompanyDto companyDto);
List<CompanyDto> companiesToCompanyDtos(List<Company> companies);
}
I certanly oversee something, because there is no setters in the generated implementation, f. e.:
#Override
public Company companyDtoToCompany(CompanyDto companyDto) {
if ( companyDto == null ) {
return null;
}
Company company = new Company();
return company;
}
What goes here wrong?
I've noticed that your CompanyDto class has private fields but no getters or setters. There is no standard way to access the fields in that class. You might need to add those in order to map in or out of that class.

How to use Spring data JPA projections when a Collection of another DTO inside needs to be fetched

I have a DTO that has a Collection of another DTO. I want to use Spring data JPA projections to fetch only the data needed for this particular DTO. But I don't know how.
Upd: added Entity classes. The question is about Spring Data Projections, not Spring data jpa in general.
#Getter
#Setter
#NoArgsConstructor
public class RestaurantDto {
private int id;
private String name;
private String address;
private int votes;
private List<DishDtoShort> dishes;
public RestaurantDto(String name, String address) {
this.name = name;
this.address = address;
}
public RestaurantDto(int id, String name, String address,int votes, List<DishDtoShort> dishes) {
this.votes = votes;
this.id = id;
this.name = name;
this.address = address;
this.dishes = dishes;
}
public void addDish(DishDtoShort dishDtoShort) {
dishes.add(dishDtoShort);
}
public List getDishes() {
return Collections.unmodifiableList(dishes);
}
}
DTO inside a Collection
#Getter
#Setter
#NoArgsConstructor
public class DishDtoShort {
private int id;
private String name;
private double price;
public DishDtoShort(int id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
}
Restaurant Entity
#Entity
#Getter
#Setter
#NoArgsConstructor
#ToString(exclude = {"votes", "dishes"})
public class Restaurant extends AbstractNamedEntity {
String address;
#OneToMany(mappedBy = "restaurant", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonManagedReference
private List<Dish> dishes;
#OneToMany(mappedBy = "restaurant", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonManagedReference
private List<Vote> votes;
public void addDish(Dish dish) {
this.dishes.add(dish);
dish.setRestaurant(this);
}
public void removeDish(Dish dish) {
this.dishes.remove(dish);
dish.setRestaurant(null);
}
public void addVote(Vote vote) {
this.votes.add(vote);
vote.setRestaurant(this);
}
public void removeVote(Vote vote) {
this.votes.remove(vote);
vote.setRestaurant(null);
}
public Restaurant(String name, String address) {
super(name);
this.address = address;
}
public List<Dish> getDishes() {
return Collections.unmodifiableList(dishes);
}
public List<Vote> getVotes() {
return Collections.unmodifiableList(votes);
}
}
Dish Entity
#Entity
#Getter
#Setter
#ToString
#NoArgsConstructor
public class Dish extends AbstractNamedEntity {
private double price;
private LocalDate dateAdded;
#ManyToOne(fetch = FetchType.LAZY)
#JsonBackReference
private Restaurant restaurant;
public void setRestaurant (Restaurant restaurant) {
this.restaurant = restaurant;
}
public Dish(String name, double price, Restaurant restaurant) {
super(name);
this.price = price;
this.dateAdded = LocalDate.now();
this.restaurant = restaurant;
}
}
P.S. I know about wrong class naming.
I think, you just have to define your DTO in your repository and don't forget to have same name on your entity and DTO for the mapping :
#Repository
public interface RestaurantRepository extends JpaRepository<Restaurant, Integer> {
List<RestaurantDto> findByName(String name);
}
you can have more informations here : https://www.baeldung.com/spring-data-jpa-projections

is convertion to DTO like this ok or it is a bad practice?

I have Entity that has list of another entities inside. Same for DTO, dto object with a list of another dto.
I need to convert Entity to DTO with the list inside.
Here how it looks with streams ( a bit messy, don't know if it is ok to use ):
public List<RestaurantDto> getAll() {
List<Restaurant> restaurantList = restaurantRepository.findAll();
return restaurantList.stream()
.map(restaurant -> new RestaurantDto(restaurant.getName(), restaurant.getAddress(),
restaurant.getDishes().stream()
.map(dish -> new DishDto(dish.getId(), dish.getName(), dish.getPrice(), dish.getRestaurant()))
.collect(Collectors.toList())))
.collect(Collectors.toList());
}
Here is my DTO
#Getter
#Setter
#NoArgsConstructor
public class RestaurantDto {
private String name;
private String address;
private List<DishDto> dishes;
private int votes;
public RestaurantDto(String name, String address) {
this.name = name;
this.address = address;
}
public RestaurantDto(String name, String address, List<DishDto> dishes) {
this.name = name;
this.address = address;
this.dishes = dishes;
}
public void addDish(DishDto dishDto) {
dishes.add(dishDto);
}
public List getDeishes() {
return Collections.unmodifiableList(dishes);
}
}
And DTO that is in List
#Getter
#Setter
#NoArgsConstructor
#ToString
public class DishDto {
private int id;
#NotBlank
private String name;
#Digits(integer = 12, fraction = 2)
private double price;
private String restaurantName;
private String restaurantAddress;
#Digits(integer = 12, fraction = 0)
private int restaurantId;
public DishDto(int id, String name, double price, int restaurantId) {
this.id = id;
this.name = name;
this.price = price;
this.restaurantId = restaurantId;
}
public DishDto(int id, String name, double price, Restaurant restaurant) {
this.id = id;
this.name = name;
this.price = price;
this.restaurantName = restaurant.getName();
this.restaurantAddress = restaurant.getAddress();
this.restaurantId = restaurant.getId();
}
}
I do appreciate your help a lot!
In my opinion, retrieving the whole entity and then converting it to DTOs in order to have fewer fields can be considered as an anti-pattern.
You should use projections if you want to retrieve less fields from the entity.
Bringing all the fields to the memory and then using just couple of them is not a good idea.
Take a look at projections. The following resources may help:
https://vladmihalcea.com/the-best-way-to-map-a-projection-query-to-a-dto-with-jpa-and-hibernate/
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections

In Spring mvc How to add add set values to mysql

My goal :
In Spring MVC I have to save mobile phone contact list into database.
example:
phone1 sonia 2554654 work
2554654 home
multiple phone_number with multiple phone_Number type
contacts table
id,
contact_name
phone_number
phone_type
in my java class I have
public class ContactMobile {
private String type;
private String number;
public ContactMobile() {
}
public ContactMobile(String type, String number) {
super();
this.type = type;
this.number = number;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
}
and here I use SET for phone number and type
#Entity
#Table(name = "_contact")
public class MobileContact {
private String id;
private String fullname;
private Set<ContactMobile> mobileNumbers;
public MobileContact(String fullname, Set<ContactMobile> mobileNumbers) {
super();
this.fullname = fullname;
this.mobileNumbers = mobileNumbers;
}
#Id
#Column(name = "Id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Column(name = "fullname")
public String getFullname() {
return fullname;
}
public void setFullname(String fullname) {
this.fullname = fullname;
}
public Set<ContactMobile> getMobileNumbers() {
return mobileNumbers;
}
public void setMobileNumbers(Set<ContactMobile> mobileNumbers) {
this.mobileNumbers = mobileNumbers;
}
public MobileContact() {
super();
}
}
I am using hibernate to store data..
my question is in my MobileContact class in
public Set<ContactMobile> getMobileNumbers() {
return mobileNumbers;
}
what annotation I have to use here to save multiple phonenumbers?
The MobileContact entity has many ContactMobile, it is a OneToMany relation. In your ContactMobile table, you should has a field for the id of MobileContact, like mobile_contact_id, and set the join column on that field as below in your ContactMobile:
#OneToMany(fetch = FetchType.LEZY)
#JoinColumn(name = "mobile_contact_id")
private Set<ContactMobile> mobileNumbers;
You can get the detail about the relation in this.
You can use the Embeddables (instead of Entities) for very simple value objects like MobileContact (then they do not need an ID, and the are no just simple value objects without own identity)
#Embeddable
public class ContactMobile {...
//implement an equals and hashcode method!
}
public class MobileContact {
...
#ElementCollection
private Set<ContactMobile> mobileNumbers;
...
}
#See Java Persistence/ElementCollection

Retrieve data using hsql - relation many to one

I define the following entities :BaseEntity , magasin and article :
#Entity(name = "magasin")
#Table(name = "magasin")
public class Magasin extends BaseEntity {
private static final long serialVersionUID = 1L;
#Basic
#Size(min=5, max=100, message="The name must be between {min} and {max} characters")
private String name;
#OneToMany(cascade=CascadeType.ALL, mappedBy="magasin")
#Valid
private Set<Article> article;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Article> getArticle() {
return article;
}
public void setArticle(Set<Article> article) {
this.article = article;
}
}
#Entity(name="article")
#Table(name="article")
public class Article extends BaseEntity {
private static final long serialVersionUID = 1L;
#ManyToOne
private Magasin magasin;
#Basic
#Size(min=5, max=100, message="The name must be between {min} and {max} characters")
private String name;
#Basic
private float price;
public Magasin getMagasin() {
return magasin;
}
public void setMagasin(Magasin magasin) {
this.magasin = magasin;
}
public String getName() {
return name;
}
public void setName(String nom) {
this.name = nom;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
}
#MappedSuperclass
public class BaseEntity {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
public void setId(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public boolean isNew() {
return (this.id == null);
}
}
How can create a hql query in order to retrieve all Article for a magasin selected ?
I try this
#Override
public List<Article> findArticle(Magasin magasin) {
String query = "From Article m where m.magasin.id = "+magasin.getId();
System.out.print(query);
Session session = getSessionFactory().getCurrentSession();
if((session.createQuery(query).list()!=null) && (session.createQuery(query).list().size()!=0))
return (List<Article>) session.createQuery(query).list();
else
return null;
}
But it returns nothing , always null .How can I resolve it ?
I don't know the type of your magasin id so adapt the code below.
First get the Magasin instance for the id:
Magasin mag = (Magasin)session.get(Magasin.class, id);
Next you can access the articles for the magasin mag via accessor
Set<Article> articles = mag.getArticle();
Try this:
"Select * From Article,Mgasin where Article.mgasin.id = "+magasin.getId();

Categories