entityListToResponseModelList unable to find mapped target properties, resulting in null results

22 Views Asked by At

I'm having problems with my mapping configuration. Whenever I get an order by its ID, I'm having no problems whatsoever. However, once I attempt to get every single order using entityListToResponseModelList, every single ID, aggregate or not, returns null in postman. My mapping seems correct, yet I get a warning telling me that I have "umapped target properties". Here's the full warning:

#13 16.36 /usr/src/app/src/main/java/com/example/clothingstore/ordersubdomain/mapperlayer/OrderResponseMapper.java:63: warning: Unmapped target properties: "orderId, productId, customerId, employeeId". Mapping from Collection element "Order order" to "OrderResponseModel orderResponseModel".

#13 16.36     List<OrderResponseModel> entityListToResponseModelList(List<Order> orders);
                                       ^

My ResponseMapper:

package com.example.clothingstore.ordersubdomain.mapperlayer;

import com.example.clothingstore.customersubdomain.datalayer.Customer;
import com.example.clothingstore.employeesubdomain.datalayer.Employee;
import com.example.clothingstore.ordersubdomain.datalayer.Order;
import com.example.clothingstore.ordersubdomain.presentationlayer.OrderController;
import com.example.clothingstore.ordersubdomain.presentationlayer.OrderResponseModel;
import com.example.clothingstore.productsubdomain.datalayer.Product;
import org.mapstruct.AfterMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.springframework.hateoas.Link;

import java.util.List;
import java.util.stream.Collectors;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;

@Mapper(componentModel = "spring")
public interface OrderResponseMapper {

    @Mapping(expression = "java(order.getOrderIdentifier().getOrderId())", target = "orderId")
    @Mapping(expression = "java(order.getProductIdentifier().getProductId())", target = "productId")
    @Mapping(expression = "java(product.getName())", target = "name")
    @Mapping(expression = "java(product.getPrice())", target = "price")
    @Mapping(expression = "java(order.getCustomerIdentifier().getCustomerId())", target = "customerId")
    @Mapping(expression = "java(customer.getBillingAddress().getStreetAddress())", target = "streetAddress")
    @Mapping(expression = "java(customer.getBillingAddress().getPostalCode())", target = "postalCode")
    @Mapping(expression = "java(customer.getBillingAddress().getCity())", target = "city")
    @Mapping(expression = "java(customer.getBillingAddress().getProvince())", target = "province")
    @Mapping(expression = "java(order.getEmployeeIdentifier().getEmployeeId())", target = "employeeId")
    @Mapping(expression = "java(employee.getLastName())", target = "lastName")
    @Mapping(expression = "java(order.getDeliveryStatus().name())", target = "deliveryStatus")
    @Mapping(expression = "java(order.getShippingPrice())", target = "shippingPrice")
    @Mapping(expression = "java(order.getTotalPrice())", target = "totalPrice")
    OrderResponseModel entityToResponseModel(Order order, Product product, Customer customer, Employee employee);
    
    @AfterMapping
    default void addLinks(@MappingTarget OrderResponseModel model){

        Link selfLink = linkTo(methodOn(OrderController.class)
                .getOrderByOrderId(model.getOrderId()))
                .withSelfRel();
        model.add(selfLink);

        Link ordersLink =
                linkTo(methodOn(OrderController.class)
                        .getOrders())
                        .withRel("All orders");
        model.add(ordersLink);
    }

    @Mapping(expression = "java(order.getOrderIdentifier().getOrderId())", target = "orderId")
    @Mapping(expression = "java(order.getProductIdentifier().getProductId())", target = "productId")
    @Mapping(expression = "java(order.getCustomerIdentifier().getCustomerId())", target = "customerId")
    @Mapping(expression = "java(order.getEmployeeIdentifier().getEmployeeId())", target = "employeeId")
    List<OrderResponseModel> entityListToResponseModelList(List<Order> orders);
}

And a few other classes that I feel like probably have nothing to do with my problem, but I'm including them for good measure.

OrderResponseModel

package com.example.clothingstore.ordersubdomain.presentationlayer;

import com.example.clothingstore.common.enums.DeliveryStatus;
import com.example.clothingstore.common.identifiers.CustomerIdentifier;
import com.example.clothingstore.common.identifiers.EmployeeIdentifier;
import com.example.clothingstore.common.identifiers.OrderIdentifier;
import com.example.clothingstore.common.identifiers.ProductIdentifier;
import lombok.*;
import org.springframework.hateoas.RepresentationModel;

import java.math.BigDecimal;

@EqualsAndHashCode(callSuper = true)
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class OrderResponseModel extends RepresentationModel<OrderResponseModel> {

    String orderId;
    String productId;
    String name;
    BigDecimal price;
    String customerId;
     String streetAddress;
     String postalCode;
     String city;
     String province;
     String employeeId;
     String lastName;
     String deliveryStatus;
     BigDecimal shippingPrice;
     BigDecimal totalPrice;

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public String getProductId() {
        return productId;
    }

    public void setProductId(String productId) {
        this.productId = productId;
    }

    public String getCustomerId() {
        return customerId;
    }

    public void setCustomerId(String customerId) {
        this.customerId = customerId;
    }

    public String getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(String employeeId) {
        this.employeeId = employeeId;
    }
}

Schema:

CREATE TABLE IF NOT EXISTS orders (
    id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
    order_id VARCHAR(36) UNIQUE,
    product_id VARCHAR(36),
    name VARCHAR(50),
    price DECIMAL(10, 2),
    customer_id VARCHAR(36),
    street_address VARCHAR(50),
    postal_code VARCHAR(50),
    city VARCHAR(50),
    province VARCHAR(50),
    employee_id VARCHAR(36),
    last_name VARCHAR(50),
    delivery_status VARCHAR(50),
    shipping_price DECIMAL(10, 2),
    total_price DECIMAL(10, 2),
    FOREIGN KEY (product_id) REFERENCES products(product_id),
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id),
    FOREIGN KEY (employee_id) REFERENCES employees(employee_id),
    INDEX idx_customer_id (customer_id),
    INDEX idx_employee_id (employee_id),
    INDEX idx_order_id (order_id)
);

getOrders function:

@Override
public List<OrderResponseModel> getOrders() {
    List<Order> orders = orderRepository.findAll();
    return orderResponseMapper.entityListToResponseModelList(orders);
}

And the postman response as well.

I've tried adding getters and setters myself, without the use of lombok. I've tried mapping my entityListToResponseModel. I played around with my Request Mapper to no avail.

Thanks in advance for the help! And apologies if this question doesn't meet standards, it's my first time posting on here.

2

There are 2 best solutions below

1
Justin Morissette On

Yeah, you just have to make a forEach (in your orderServiceImpl getAllOrders() function) to be able to use the entityListToResponseModel for all your entries individually instead of using your entityListToResponseModelList to be able to properly and completely map each order.

0
Alex Cristea On

Ended up just getting rid of the damned function and implementing it in my service implementation. Leaving the code here for anyone who might need it! entityListToResponseModelList seems to get confused whenever it needs to list off multiple aggregates/entities

    @Override
public List<OrderResponseModel> getOrders() {
    List<OrderResponseModel> orderResponseModelList = new ArrayList<>();
    List<Order> orders = orderRepository.findAll();

    orders.forEach(order -> {
        if(!productRepository.existsProductByProductIdentifier_ProductId(order.getProductIdentifier().getProductId()))
            throw new InvalidInputException(("Invalid input for productId: ") + order.getProductIdentifier().getProductId());

        if(!customerRepository.existsCustomerByCustomerIdentifier_CustomerId(order.getCustomerIdentifier().getCustomerId()))
            throw new InvalidInputException(("Invalid input for customerId: ") + order.getCustomerIdentifier().getCustomerId());

        if(!employeeRepository.existsEmployeeByEmployeeIdentifier_EmployeeId(order.getEmployeeIdentifier().getEmployeeId()))
            throw new InvalidInputException(("Invalid input for employeeId: ") + order.getEmployeeIdentifier().getEmployeeId());

        orderResponseModelList.add(orderResponseMapper.entityToResponseModel(order,
                productRepository.findByProductIdentifier_ProductId(order.getProductIdentifier().getProductId()),
                customerRepository.findByCustomerIdentifier_CustomerId(order.getCustomerIdentifier().getCustomerId()),
                employeeRepository.findByEmployeeIdentifier_EmployeeId(order.getEmployeeIdentifier().getEmployeeId())));
    });

    return orderResponseModelList;
}