Core Java

Map Iterable to Object with MapStruct

In Java, working with Iterable types like lists are common, but sometimes we need to map a collection of objects (such as a list of entities) to a single non-iterable object (like a DTO or summary object). This is where MapStruct comes in. MapStruct is a Java annotation-based mapper that simplifies the process of converting between different types, including iterable to non-iterable transformations. Let us delve into understanding how to use Java MapStruct for mapping an Iterable to an object.

1. Understand the Use Case

Imagine you have a list of Product entities and you need to map it to a summary object, such as a ProductSummaryDTO, containing an aggregate of information from the list. The challenge here is converting the list (an iterable type) into a single object (a non-iterable type). Let’s take an example:

  • We have a list of products with price and quantity fields.
  • We want to map it to a single object, ProductSummaryDTO, which contains total price and total quantity.

2. Map Iterable to Non-Iterable Type

MapStruct doesn’t directly support converting iterables to single objects out of the box, but we can implement custom mapping methods to achieve this. By using MapStruct’s @Mapper annotation and custom mapping logic, we can transform iterables into objects.

2.1 Solution

Let’s go through the process step-by-step:

2.1.1 Create Product Entity

The class is a simple Java class (also known as POJO – Plain Old Java Objects) that models a product with two attributes – price and quantity.

public class Product {
    private double price;
    private int quantity;

    // Constructors, Getters, and Setters
    public Product(double price, int quantity) {
        this.price = price;
        this.quantity = quantity;
    }

    public double getPrice() {
        return price;
    }

    public int getQuantity() {
        return quantity;
    }
}

2.1.2 Create ProductSummaryDTO

The ProductSummaryDTO class is a DTO class that represents a summary of multiple products. It contains two key pieces of information:

  • totalPrice: The total price of all products.
  • totalQuantity: The total quantity of products.

This class is commonly used when we need to represent an aggregate view of product data (such as the combined price and quantity of a list of products).

public class ProductSummaryDTO {
    private double totalPrice;
    private int totalQuantity;

    // Constructors, Getters, and Setters
    public ProductSummaryDTO(double totalPrice, int totalQuantity) {
        this.totalPrice = totalPrice;
        this.totalQuantity = totalQuantity;
    }

    public double getTotalPrice() {
        return totalPrice;
    }

    public int getTotalQuantity() {
        return totalQuantity;
    }
}

2.1.3 Custom Mapping Logic using MapStruct

We will use MapStruct to write a custom mapper method that will take a list of Product objects and map them to ProductSummaryDTO.

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper
public interface ProductMapper {

    ProductMapper INSTANCE = Mappers.getMapper(ProductMapper.class);

    default ProductSummaryDTO mapToProductSummary(Iterable<Product> products) {
        double totalPrice = 0;
        int totalQuantity = 0;

        for (Product product : products) {
            totalPrice += product.getPrice();
            totalQuantity += product.getQuantity();
        }

        return new ProductSummaryDTO(totalPrice, totalQuantity);
    }
}

The code defines a:

  • @Mapper: This is the MapStruct annotation that marks the interface as a mapper.
  • mapToProductSummary: This is the custom mapping method that iterates through the Iterable<Product> and aggregates the total price and quantity.
  • Inside the method, we loop through each product, sum the prices and quantities, and return a new ProductSummaryDTO with the results.

2.1.4 Code Output

Now let’s test the mapper:

import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Product> productList = Arrays.asList(
            new Product(10.5, 2),
            new Product(20.0, 3),
            new Product(5.5, 1)
        );

        ProductSummaryDTO summary = ProductMapper.INSTANCE.mapToProductSummary(productList);
        
        System.out.println("Total Price: " + summary.getTotalPrice());
        System.out.println("Total Quantity: " + summary.getTotalQuantity());
    }
}

When the code is executed the following output will be logged on the IDE console.

Total Price: 36.0
Total Quantity: 6

3. Conclusion

In this article, we demonstrated how to use MapStruct to map an Iterable (a list of products) to a single object (a summary DTO). While MapStruct doesn’t natively support this out-of-the-box, by writing custom mapping methods, we can easily handle complex mappings. This solution is particularly useful for scenarios where we need to aggregate data from collections into a single object. MapStruct’s flexibility and ease of use make it a powerful tool for transforming data between different types in a clean, maintainable way.

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button