Home > database >  How to make Model Mapper map correctly from entity to DTO
How to make Model Mapper map correctly from entity to DTO

Time:12-28

As context, I create a Spring Boot REST API and I encountered the next problem, when I map a Entity class to a DTO, it won't take one of the fields, more precisely the department field. Here foundItem = mapper.convertToType(itemDTO, Item.class); after this mapping from a itemDTO, foundItem won't contain a department, it will be just null instead

 public ItemDTO updateItem(Long departmentId, Long itemId, ItemDTO itemDTO) throws ApiException {
        Department department = departmentRepository.findById(departmentId).orElseThrow(
                () -> new ApiException("Department with this id does not exist ",
                        HttpStatus.NOT_FOUND));

        Item foundItem = validateItem(itemId);
        Item convertedItem = mapper.convertToType(itemDTO, Item.class);
        if (convertedItem.getStock() < 0) {
            throw new ApiException("Stock must be a positive value", HttpStatus.BAD_REQUEST);
        }

        if (convertedItem.getStockThreshold() < 0) {
            throw new ApiException("Stock threshold must be a positive value", HttpStatus.BAD_REQUEST);
        }

        itemDTO.setId(itemId);
        itemDTO.setDepartment(mapper.convertToType(department, DepartmentDTO.class));
        foundItem = mapper.convertToType(itemDTO, Item.class);
        itemRepository.save(foundItem);


//        ItemDTO returnItem = mapper.convertToType(convertedItem, ItemDTO.class);
        logger.info("Updated item");
        return itemDTO;
    }

Here it is before conversion enter image description here

After conversion enter image description here

EDIT!

This is my Mapper class

@Component
public class Mapper {
    private final ModelMapper modelMapper;
    private final Logger logger = LoggerFactory.getLogger(ModelMapper.class);

    public Mapper(ModelMapper modelMapper) {
        this.modelMapper = modelMapper;
    }

    public <T> T convertToType(Object source, Class<T> resultClass) {
        logger.debug("converted object from "   source.getClass().getSimpleName()   " to "   resultClass.getSimpleName());
        return modelMapper.map(source, resultClass);
    }
}

ItemDTO class

@AllArgsConstructor

@NoArgsConstructor
@Setter
public class ItemDTO {
    private Long id;
    private String description;
    private String price;
    private int stock;

    private int stockThreshold;

    private DepartmentDTO department;

    public Long getId() {
        return id;
    }

    public String getDescription() {
        return description;
    }

    public String getPrice() {
        return price;
    }

    public int getStock() {
        return stock;
    }

    public int getStockThreshold() {
        return stockThreshold;
    }

    public Long getDepartment() {
        return department.getId();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ItemDTO itemDTO = (ItemDTO) o;
        return stock == itemDTO.stock && stockThreshold == itemDTO.stockThreshold &&
                id.equals(itemDTO.id) && description.equals(itemDTO.description) &&
                price.equals(itemDTO.price) && department.equals(itemDTO.department);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, description, price, stock);
    }

And Item class

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "ITEMS")
public class Item {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    private Long id;
    private String description;
    private String price;
    private int stock;

    private int stockThreshold = 5;
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "department_id", nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    @JsonIgnore
    private Department department;

    @Override
    public String toString() {
        return "Item{"  
                "id="   id  
                ", description='"   description   '\''  
                ", price='"   price   '\''  
                ", stock="   stock  
                ", department="   department  
                '}';
    }
}

DepartmentDTO Class:

@NoArgsConstructor

@Getter
@Setter
public class DepartmentDTO {
    private Long id;
    private String description;
    private List<Item> items;


    public DepartmentDTO(long id, String description) {
        this.id = id;
        this.description = description;
    }

    @Override
    public String toString() {
        return "DepartmentDTO{"  
                "id="   id  
                ", description='"   description   '\''  
                ", items="   items  
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        DepartmentDTO that = (DepartmentDTO) o;
        return Objects.equals(id, that.id) && Objects.equals(description, that.description) && Objects.equals(items, that.items);
    }

//    @Override
//    public int hashCode() {
//        return Objects.hash(id, description, items);
//    }
}

Department Class

@NoArgsConstructor
@Entity
@Getter
@Setter
@Table(name = "DEPARTMENTS")
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String description;
    @OneToMany(mappedBy = "department", fetch = FetchType.EAGER,
            cascade = CascadeType.ALL)
    private List<Item> items;
    public Department(Long id, String description) {
        this.id = id;
        this.description = description;
    }

    @Override
    public String toString() {
        return "Department{"  
                "id="   id  
                ", description='"   description   '\''  
                ", items="   items  
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Department)) return false;
        Department that = (Department) o;
        return id.equals(that.id) && Objects.equals(description, that.description) && Objects.equals(items, that.items);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, description, items);
    }

}

A minimal reproducible example:

Department department = new Department(1L, "first");
ItemDTO  itemDTO = new ItemDTO(1L, "modified description for update",
                "200$", 200, 6, mapper.convertToType(department, 
DepartmentDTO.class));
Item item = mapper.convertToType(itemDTO,Item.class);

item will have it's department field null, because my mapper has a problem in mapping the deparment.

CodePudding user response:

Preface: Thank you for quickly responding to my comments. I see that you are fairly new to Stack Overflow and looking back at your questions, I have noticed a trend. I suggest you read How do I ask a good question? and Minimal, Reproducible Example.

Answer:

TLDR: You have to make ItemDTO#getDepartment return the department object.

Here's how I approached the problem:

  1. Created a new project to hold and run your code.
  2. Cleaned up your code.
  • Removed JPA annotations.
  • Removed Jackson annotations.
  • Removed Lombok annotations.
  1. The only dependency I could not resolve was ModelMapper. I assumed that this is from ModelMapper. I have never used it before, but I made the assumption that it uses getters/setters like popular serialization libraries such as Jackson do.
  2. This led me to check your getters/setters, wherein I found that ItemDTO#getDepartment returned the ID of the department, not the DepartmentDTO object itself.

I thought I would share how I tackled your problem with the hope that it will help you better solve problems yourself!

  • Related