Sitemap

Part 2: Advanced Mapping with MapStruct — Using @MappingTarget in Spring Boot

4 min readAug 24, 2024

--

Welcome back to my series on leveraging MapStruct in Spring Boot! In the previous article, we have explored the fundamentals of using MapStruct for mapping between different object models. Today, we’re diving into a more advanced topic: How to update existing objects using the @MappingTarget annotation.

If you missed the previous article, don’t worry. Here’s a quick recap: MapStruct is a powerful annotation processor that simplifies object-to-object mapping in Java applications. It generates mapping code automatically, reducing boilerplate and improving maintainability. Now, let’s build on that foundation by examining how @MappingTarget can help you update existing objects efficiently.

Recap: The Need for Object Mapping In many applications, especially those dealing with data transfer objects (DTOs) and entities, you often need to update an existing object rather than creating a new one. This is common in scenarios such as updating user profiles, processing incoming data, or integrating with third-party services.

Introducing @MappingTarget

The @MappingTarget annotation in MapStruct is designed to address this exact scenario. It allows you to map properties from a source object onto an existing target object, rather than creating a new instance. This can be particularly useful when you want to apply updates to an existing entity or model.

Practical Example:

Mapping Shipping Events to Orders To illustrate the use of @MappingTarget, let’s consider a practical example. Imagine you have a Spring Boot application where you receive ShippingEvent objects containing updated order information. Your task is to update an existing Order entity with the details from these events.

Here’s how you can use MapStruct and @MappingTarget to achieve this:

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

// Mapping from ShippingEvent to Order
@Mapping(source = "orderDto", target = "order")
default Order mapShippingEventToOrder(ShippingEvent shippingEvent) {
Order order = new Order();
updateOrderFromOrderDto(shippingEvent.getOrderDto(), order);
return order;
}

// Map OrderDTO to an existing Order
void updateOrderFromOrderDto(OrderDTO orderDto, @MappingTarget Order order);
}

Let’s break down this ‘mapShippingEventToOrder’ method above in detail, and see what it does exactly step by step.

Understanding ‘mapShippingEventToOrder’

The ‘mapShippingEventToOrder’ method is a custom default method in the MapStruct mapper interface. It showcases how to handle a more complex mapping scenario where an existing object needs to be updated based on data from a different object.

Method Signature :

default Order mapShippingEventToOrder(ShippingEvent shippingEvent)

This is a default method in the ‘OrderMapper’ interface. This method receives a ‘ShippingEvent’ object as input, which contains an ‘OrderDTO’ as one of its properties and returns an ‘Order’ object.

Creating a New ‘Order’ Instance:

Order order = new Order();

An empty ‘Order’ object is instantiated. This serves as the target entity that will be updated with data from the ‘OrderDTO’.

Updating the ‘Order’ Object:

updateOrderFromOrderDto(shippingEvent.getOrderDto(), order);

This line calls the ‘updateOrderFromOrderDto’ method, passing the ‘OrderDTO’ (extracted from the ‘ShippingEvent’ object) and the newly created ‘Order’ object. Thus, this method will copy the values from the ‘OrderDTO’ into the existing ‘Order’ object.

Get bectorhimanshu’s stories in your inbox

Join Medium for free to get updates from this writer.

The ‘@MappingTarget’ annotation in ‘updateOrderFromOrderDto’ tells MapStruct to update the existing ‘Order’ instance with the values from the ‘OrderDTO’.

Returning the Updated ‘Order’ Object:

return order;

Finally, the method returns the ‘Order’ object, now populated with the data from the ‘OrderDTO’.

Example Scenario :

Let’s consider a concrete example to illustrate this:

Example :

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ShippingEvent
{
private OrderDTO orderDto;
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OrderDTO {
private String orderId;
private String productName;
private int quantity;
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Order {
private String orderId;
private String productName;
private int quantity;
}

Example Data :

ShippingEvent:

ShippingEvent shippingEvent = new ShippingEvent();
OrderDTO orderDto = new OrderDTO();
orderDto.setOrderId("12345");
orderDto.setProductName("Laptop");
orderDto.setQuantity(1);
shippingEvent.setOrderDto(orderDto);

Execution :

Calling ‘mapShippingEventToOrder’:

@Autowired
OrderMapper orderMapper; // get the mapper instance (e.g., through dependency injection)

Order order = orderMapper.mapShippingEventToOrder(shippingEvent);

Result :
The ‘order’ object will be updated with:

'orderId': "12345"
'productName': "Laptop"
'quantity': 1

In a nutshell, in this way, the ‘mapShippingEventToOrder’ method facilitates the mapping of data from ‘ShippingEvent’ to a new ‘Order’ object, efficiently using the ‘updateOrderFromOrderDto’ method to apply the necessary updates.

mapShippingEventToOrder(ShippingEvent shippingEvent):
This method creates a new Order instance and then delegates the updating of this instance to the updateOrderFromOrderDto method. This approach ensures that you can handle the creation of the Order object while still leveraging the @MappingTarget functionality for updates.

updateOrderFromOrderDto(OrderDTO orderDto, @MappingTarget Order order): Here’s where @MappingTarget shines. By annotating the order parameter with @MappingTarget, MapStruct knows to update the existing Order object with values from the OrderDTO. This avoids unnecessary object creation and is more efficient when working with persistent entities.

Why Use @MappingTarget?

Performance: Updating existing objects instead of creating new ones can reduce memory usage and garbage collection overhead, leading to better performance.
Integration with Persistence Frameworks: When working with JPA or similar ORM frameworks, maintaining and updating existing entity instances is crucial for ensuring consistency within the persistence context.
Code Clarity: Using @MappingTarget makes your intent clear: you are updating an existing instance rather than replacing it. This can lead to more readable and maintainable code.

Conclusion:

In this second part of the series on MapStruct, we’ve explored how to use the @MappingTarget annotation to update existing objects efficiently. This technique is invaluable for handling scenarios where you need to apply changes to existing entities or models, especially in applications with complex data interactions.

By incorporating @MappingTarget into your MapStruct mappings, you can optimize your code for performance and clarity, making your Spring Boot applications both efficient and easy to maintain.

Stay tuned for more such interested topics related to different technologies in our upcoming articles. Happy coding!

--

--

bectorhimanshu
bectorhimanshu

Written by bectorhimanshu

I'm a Software Developer passionate about modern technologies. I love sharing practical insights and simplifying complex concepts through my tech blogs.