ModelMapper not mapping Spring Entity property - java

I am currently trying to perform an Entity to DTO mapping using ModelMapper's map method. On the deepest property mapping, the mapping result is null but it should be an object.
These are my mapping source Entities(Omitting Loombok Getters and Setters for brevety):
public class Client implements Serializable
{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public Long id;
#NotEmpty
public String name;
#NotEmpty
public String surname;
#NotEmpty
#Email
public String email;
#Column(name="creation_date", updatable=false)
#Temporal(TemporalType.DATE)
#DateTimeFormat(pattern="yyyy-MM-dd")
public Date creationDate;
public String photo;
#OneToMany(mappedBy="client", fetch=FetchType.LAZY, cascade=CascadeType.ALL, orphanRemoval=true)
public List<Invoice> invoices;
}
public class Invoice implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
#NotEmpty
public String description;
public String comment;
#Temporal(TemporalType.DATE)
#Column(name = "creation_date", updatable = false)
#DateTimeFormat(pattern = "yyyy-MM-dd")
public Date creationDate;
#ManyToOne(fetch = FetchType.LAZY)
public Client client;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "invoice_id")
public List<InvoiceItem> items;
}
public class InvoiceItem implements Serializable
{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public Long id;
public Integer amount;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="product_id")
public Product product;
}
public class Product implements Serializable
{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public Long id;
public String name;
public Double prize;
#Temporal(TemporalType.DATE)
#Column(name="creation_date", updatable=false)
#DateTimeFormat(pattern="yyyy-MM-dd")
public Date creationDate;
}
And these are my target DTOs(Omitting Loombok Getters and Setters for brevety):
public class ClientDTO implements Serializable
{
#JsonIgnore
public Long id;
public String name;
public String surname;
public String email;
#JsonIgnore
public Date creationDate;
public String photo;
}
public class ClientWithInvoicesDTO extends ClientDTO implements Serializable
{
#JsonManagedReference
public List<InvoiceDTO> invoices;
}
public class InvoiceDTO implements Serializable
{
#JsonIgnore
public Long id;
public String description;
public String comment;
public Date creationDate;
#JsonBackReference
public ClientDTO client;
public List<InvoiceItemDTO> items;
}
public class InvoiceItemDTO implements Serializable
{
#JsonIgnore
public Long id;
public Integer amount;
public ProductDTO productDTO;
}
public class ProductDTO implements Serializable
{
#JsonIgnore
public Long id;
public String name;
public Double prize;
#JsonIgnore
public Date creationDate;
}
And on my Controller class method I am doing mapping like this:
#RequestMapping(value = "/{id}/with-invoices", method = RequestMethod.GET)
public ResponseEntity findOneWithInvoices(#PathVariable Long id)
{
Client wantedClient = this.clientService.findOneWithInvoices(id);
ClientWithInvoicesDTO wantedClientWithInvoicesDto = this.modelMapper.map(wantedClient, ClientWithInvoicesDTO.class);
return new ResponseEntity<>(wantedClientWithInvoicesDto, HttpStatus.OK);
}
My clientService is correctly returning the data but "product" property is null on JSON response and it shouldn't be:
{
"name": "cupiditate",
"surname": "veritatis",
"email": "george.prohaska#example.com",
"photo": "ec0327a5-3de8-328c-a812-9000d6d5507d",
"invoices": [
{
"description": "Distinctio autem id vel necessitatibus unde omnis rerum. Minus maxime quos doloribus. Voluptatem amet praesentium sit magni quia molestiae. Officia aspernatur numquam ut perspiciatis a.",
"comment": "Odio enim libero tempore molestiae.",
"creationDate": "2013-02-16",
"items": [
{
"amount": 6,
"productDTO": null
},
...
Can anyone help me with this?

Shouldn't you put #Entity before your class? Like that:
#Entity
public class MyClass {
...
}

As M. Deinum says on comment below, on InvoiceItemDTO product property should be named as "product" instead of "productDTO". Now it works fine

Related

Problem to implement bidirectional relationship in hibernate with my spring boot api

I want to create a spring boot rest controller with this specification :
Customers of an electricity and gas supply company can choose to receive their monthly bills either by email or by regular mail, neither or both.
My goal is to create java hibernate entities to manage these customers and their choices of sending bills.
A utility customer is identified by their email and can have multiple choice change events that change the customer choice status.
Each choice made by a customer generates a choice change event.
A choice change event relates to a customer. A customer can have multiple choice events.
Here are my java entities.
#Entity
#Table(name = "customers")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Email(message="this field must respect the email format !")
private String email;
#ManyToOne
private Choices choices;
}
#Entity
#Table(name = "choices")
public class Choices {
#Id
private String id;
#Column(name = "email")
private boolean isThisChoice;
#OneToOne
private Customer customer;
}
The resulting customer with id 24587 (GET request):
{
"id": "24587",
"email": "tartampion",
"choices": [
{
"id": "regular mail",
"isThisChoice": false
},
{
"id": "email",
"isThisChoice": true
}
]
}
Must I have an entity of management of event of choice of the customer
Here you have to use Many to Many Mapping.
Because one customer can have many choices and one choice can be opted by many customers.
package com.umesh.entity;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
#Entity
#Table(name="customer")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="email")
private String email;
#ManyToMany(fetch=FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH })
#JoinTable(
name="customer_choice",
joinColumns=#JoinColumn(name="customer_id"),
inverseJoinColumns=#JoinColumn(name="choice_id")
)
#LazyCollection(LazyCollectionOption.FALSE)
private List<Choice> choices;
public void Customer(){}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public List<Choice> getChoices() {
return choices;
}
public void setChoices(List<Choice> choices) {
this.choices = choices;
}
public void addChoices(Choice choice){
if(choices == null){
choices = new ArrayList<>();
choices.add(choice);
}
choices.add(choice);
}
}
package com.umesh.entity;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
#Entity
#Table(name="choice")
public class Choice {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="choice")
private String choice;
#ManyToMany(fetch=FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH })
#JoinTable(
name="customer_choice",
joinColumns=#JoinColumn(name="choice_id"),
inverseJoinColumns=#JoinColumn(name="customer_id")
)
#LazyCollection(LazyCollectionOption.FALSE)
private List<Customer> customers;
public void Choics(){}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getChoice() {
return choice;
}
public void setChoice(String choice) {
this.choice = choice;
}
public List<Customer> getCustomers() {
return customers;
}
public void setCustomers(List<Customer> customers) {
this.customers = customers;
}
public void addCustomers(Customer customer){
if(customers == null){
customers = new ArrayList<>();
customers.add(customer);
}
customers.add(customer);
}
}
Did you mean a model more like:
#Entity
#Table(name = "customers")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Email(message="this field must respect the email format !")
private String email;
#ElementCollection
#CollectionTable(name="Choices")
#MapKeyColumn(name="CHOICE") //an "EMAIL" or "MAIL" string. You can use an enum instead if you want, but I wouldn't for upgrade reasons.
#Column(name="enabled")
private Map<String, Boolean> choices;
}
This will give you a Map of choices, resulting in JSON more like:
{
"id": "24587",
"email": "tartampion",
"choices": {
"MAIL": false,
"EMAIL": true
}
}
It should be much more expandable if you get other options and combinations in the future.
Similarly, you can use the same table structure with "Choices" as an entity:
#Entity
#Table(name = "customers")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Email(message="this field must respect the email format !")
private String email;
#OneToMany(mappedBy = "customer")
#MapKey(name="type") //don't need this if you want just a list
private Map<String, Choice> choices;
}
#Entity
#Table(name = "choices")
public class Choice {
#Id
#OneToOne
private Customer customer;
#Id
#Enumerated(EnumType.STRING)
private ChoiceType type;
#Column(name="enabled")
private boolean enabled;
}
#IdClass(EmployeeId.class)
public class ChoiceId implements Serializable {
private Integer customer;
private ChoiceType type;
}
public enum ChoiceType {
MAIL,
EMAIL;
}

Deserialize inner json string property with GSON

I'm using Springboot and Gson for my backend.
I have these two classes in a many to many relation:
Order class
#Getter
#Setter
#Builder
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table( name = "orders")
public class Order {
#Id
#Expose
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Expose
#NotBlank
private String poNumber;
#Expose
#NotBlank
private String dimension;
#Expose
#NotBlank
private int initialQuantity;
#Expose
#NotBlank
private int leftQuantity;
#Expose
#NotBlank
private Date startDate;
#Expose
#NotBlank
private Date endDate;
#Expose
#SerializedName("status")
#Enumerated(EnumType.STRING)
#Column(length = 20)
private PoStatus status;
#OneToMany(mappedBy="order")
private Set<Log> logs;
#Expose
#SerializedName("products")
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable( name = "orders_products",
joinColumns = #JoinColumn(name = "order_id"),
inverseJoinColumns = #JoinColumn(name = "sap_code"))
private Set<Product> products = new HashSet<>();
Product Class
#Getter
#Setter
#Builder
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table( name = "products")
public class Product {
#Expose
#Id
#NotBlank
private String sapCode;
#Expose
#NotBlank
private String sapCodeDescription;
#Expose
private String productData;
}
And this is the service that I use to serve the data to my rest endpoint
public String getAllOrders() {
List<Order> allOrders = orderRepository.findAll();
String allOrdersResult = gson.toJson(allOrders);
return allOrdersResult;
}
And this is the response:
[
{
"id": 1,
"poNumber": "003100059361",
"dimension": "INTBKGR",
"initialQuantity": 200000,
"leftQuantity": 200000,
"startDate": "17/08/2022 00:00",
"endDate": "17/08/2022 00:00",
"status": "READY",
"products": [
{
"sapCode": "000000000000416234",
"sapCodeDescription": "1.STUFE 15X",
"productData": "{\"pieces\": 85, \"mark\": true, \"description\": \"elementum pellentesque quisque porta volutpat erat quisque erat eros viverra eget congue eget\"}"
}
]
}
]
My aim is to deserialize/escape the productData String property.
I've tried by creating a ProductData class and using the #JsonAdapter annotation, but as far as I understood this annotation Is used when you need to give a custom behaviour to your deserialization, the JSON string in my example is very simple and I don't need any particular logic behind it.
I think you have to declare a class for that and change productData type from string to that class.
I resolved in this way, I think this is NOT a good approach and I hope that there is a more automatic approach to solve this.
Product Class
public class Product {
#Expose
#Id
#NotBlank
private String sapCode;
#Expose
#NotBlank
private String sapCodeDescription;
//THIS PROPERTY IS TO SAVE THE JSON IN THE DB
#NotBlank
#Column(columnDefinition="TEXT")
#JsonProperty
private String productDataColumn;
//THIS PROPERTY IS TO EXPOSE THE DATA TO THE API
#Transient
#Expose
private ProductData productData;
public void createProductData() {
this.productData = new Gson().fromJson(productDataColumn, ProductData.class);
}
}
ProductData Class
public class ProductData {
#Expose
public int pieces;
#Expose
public boolean marcatura;
#Expose
public String description;
}
OrderService
public String getAllOrders() {
List<Order> allOrders = orderRepository.findAll();
for(Order o : allOrders){
Product orderProduct = o.getProducts().stream().findFirst().get();
orderProduct.createProductData();
}
String allOrdersResult = gson.toJson(allOrders);
return allOrdersResult;
}

extended class doesn't load in frontend

my Entity is:
#Data
#Entity
#Table(name = "CORE_BOOK")
public class Book extends BaseEntity<Integer>{
#Column(name = "TITLE")
private String title;
#Column(name = "AUTHOR")
private String author;
#Column(name = "ISBN_NUMBER")
private String isbnNumber;
#Column(name = "price")
private Long price;
#Column(name = "LANGUAGE")
private String language;
#Column(name = "COVER_PHOTO_URL")
private String coverPhotoUrl;
}
and BaseEntity
#MappedSuperclass
public class BaseEntity<T extends Serializable> implements Serializable {
private static final long serialVersionUID = 95231478632145632L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private T id;
}
then when i breakpoint on findAll in controller i can see id in array List
#GetMapping("/list")
#ResponseBody
public List<Book> findAll() {
return bookRepository.findAll();
}
but in generated-request.http or in frontend (react table) , extended property (id) doesn't load
GET http://localhost:8080/rest/books/list
and the result as follows:
{
"title": "test",
"author": "SMH",
"isbnNumber": "513548",
"price": 150000,
"language": "English",
"coverPhotoUrl": "../images.jpg"
}
please help me how i can see extended property in frontend

how to display DTO correctly in Spring boot

I have two Entities in my spring boot application. I am working with dtos and displaying my dto in the end. But I am getting the wrong output from my getRequest. My first entity is MeetingSetting which can have multiple MeetingTimes and inside MeetingTime I have meetingName as a foreign key. I want to display meetingTime like this:
{
"id": 1,
"date": "2021-06-31",
"startTime": "15:30",
"endTime": "16:30",
"meetingName": "Test"
}
But I am getting instead this one:
{
"id": 1,
"date": "2021-06-31",
"startTime": "15:30",
"endTime": "16:30",
"meetingName": {
"id": 1,
"meetingName": "Tewasddweewrst2",
"meetingUrl": null,
"meetingPw": ""
}
}
Could someone take a look at my code and tell me what I am doing wrong?
MeetingSetting Entity::
#Entity
#Table(name = "meeting_settings")
#Setter
#Getter
public class MeetingsSetting implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "meeting_name", unique = true)
private String meetingName;
#Column(name = "meeting_url")
private String meetingUrl;
#Column(name = "meeting_pw")
private String meetingPw;
#OneToMany(mappedBy = "meetingName", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<MeetingTime> meetingTime = new HashSet<>();
}
MeetingSettingDTO:
#Getter
#Setter
public class MeetingSettingDTO {
private Long id;
#NotNull
private String meetingName;
#NotNull
private String meetingUrl;
#NotNull
private String meetingPw;
#JsonIgnore
private Set<MeetingTime> meetingTime;
}
MeetingTimeEntity:
#Entity
#Table(name = "meeting_times")
#Getter
#Setter
public class MeetingTime implements Serializable {
#JsonIgnore
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "meeting_date")
private String date;
#Column(name = "start_time")
private String startTime;
#Column(name = "end_time")
private String endTime;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "meeting_name" , referencedColumnName = "meeting_name")
private MeetingsSetting meetingName;
}
MeetingTimeDTO:
#Getter
#Setter
public class MeetingTimeDTO {
private Long id;
#NotNull
private String date;
#NotNull
private String startTime;
#NotNull
private String endTime;
private MeetingSettingDTO meetingName;
}
In my service I am first getting MeetingTime as an entity from my repository then converting it to DTO and returning it for my controller:
#Service
public class MeetingTimeService {
ModelMapper modelMapper = new ModelMapper();
#Autowired
MeetingTimeRepository meetingTimeRepository;
public List<MeetingTimeDTO> findAllMeetingTimes(){
List<MeetingTime> meetingTimeList = meetingTimeRepository.findAll();
return meetingTimeList.stream()
.map(this::convertToDto)
.collect(Collectors.toList());
}
private MeetingTimeDTO convertToDto(MeetingTime meetingTime) {
MeetingTimeDTO meetingTimeDTO = modelMapper.map(meetingTime, MeetingTimeDTO.class);
return meetingTimeDTO;
}
}
Controller:
#GetMapping(value = "/" )
public List<MeetingTimeDTO> getAllTimes() {
return meetingTimeService.findAllMeetingTimes();
}
In MeetingTimeDTO:
private MeetingSettingDTO meetingName;
The type needs to be changed to:
private string meetingName;
I have not worked with ModelMapper so cannot help you with how to map a specific field from the related object but this answer here seems to provide the info needed.

Hibernate #JoinColumn and fetch single column instead of model object

Iam using a hibernate entity class and have a ManyToOne relation with another model as follows
#ManyToOne
#JoinColumn(name ="`brand-id`", referencedColumnName="`id`", nullable=false, insertable = false, updatable = false)
private HotelBrand brand;
and my modal is as follows
#Entity
#Table(name = "`hotel`" })
public class Hotel implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="`id`", unique = true, nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotEmpty
#Column(name="`brand-id`", nullable=false)
private Integer brandid;
#NotEmpty
#Column(name="`hotel-code`", nullable=false)
private String hotelid;
#NotEmpty
#Column(name="`hotel-name`", nullable=false)
private String hotelname;
#NotEmpty
#Column(name="`have-reports`", nullable=false)
private String havereports;
#ManyToOne
#JoinColumn(name ="`brand-id`", referencedColumnName="`id`", nullable=false, insertable = false, updatable = false)
private HotelBrand brand;
public Hotel() {}
public Hotel(Integer id, Integer brandid, String hotelid, String hotelname, HotelBrand brand ) {
super();
this.id = id;
this.brandid = brandid;
this.hotelid = hotelid;
this.hotelname = hotelname;
this.brand = brand;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getBrandid() {
return brandid;
}
public void setBrandid(Integer brandid) {
this.brandid = brandid;
}
public String getHotelid() {
return hotelid;
}
public void setHotelid(String hotelid) {
this.hotelid = hotelid;
}
public HotelBrand getBrand() {
return brand;
}
public void setBrand(HotelBrand brand) {
this.brand = brand;
}
public String getHotelname() {
return hotelname;
}
public void setHotelname(String hotelname) {
this.hotelname = hotelname;
}
}
so when fetching data iam getting joined column as seperate json object but i need only single column from that joined model.
Iam getting result as follows
{
"id": 115,
"brandid": 7,
"hotelid": "ABC",
"hotelname": "sample1",
"brand": {
"id": 7,
"brandname": "brand1"
}
}
But iam expecting as
{
"id": 115,
"brandid": 7,
"hotelid": "ABC",
"hotelname": "sample1",
"brandname": "brand1"
}
any help would be much appreciated.
You have to annotate the field brand with the annotation #JsonIgnore.
Than you will not received it in the json response

Categories

Resources