DEV Community

Raphael De Lio
Raphael De Lio

Posted on

@AfterMapping: Updating lists or custom objects with MapStruct in Java

Twitter | LinkedIn | YouTube | Instagram

MapStruct is a code generator that greatly simplifies the implementation of mappings between Java types, effectively automating the task of updating or converting one object into another.

In this story, I will show how to use the @AfterMapping annotation to trigger custom mapping after the automated mapping has been done by MapStruct. And how we can use it to map one field from one class into several custom objects of a list in another class. Without further ado:

Let's say you have two different types, let's say:

@Data
@AllArgsConstructor
class Address {
    private String street;
    //private List<Street> streets;
    private String city;
}

@Data
@AllArgsConstructor
class AddressDTO {
    private String street;
    private String city;
}
Enter fullscreen mode Exit fullscreen mode

And you want to update an existing Address with the information from AddressDTO, you can simply do it by defining a mapper interface annotated with the @Mapper annotation and defining an update function with the aid of @MappingTarget annotation:

@Mapper
public interface AddressMapper {
    AddressMapper INSTANCE = Mappers.getMapper(AddressMapper.class);

    Address updateAddress(AddressDTO addressDTO, @MappingTarget Address address);
}
Enter fullscreen mode Exit fullscreen mode

By properly using it in your code:

    public static void main(String[] args) {
        var addressDTO = new AddressDTO("Main Street", "New York");
        var address = new Address("Broadway", "New York");

        var addressMapper = AddressMapper.INSTANCE;
        var updatedAddress = addressMapper.updateAddress(addressDTO, address);

        System.out.println(updatedAddress);
    }
Enter fullscreen mode Exit fullscreen mode

All properties from addressDTO will be mapped into address. When we print the updatedAddress property, we should see the same street as in the addressDTO.

However, what if the street was a nested property in the Address object?

@Data
@AllArgsConstructor
class Street {
    private String street;
}

@Data
@AllArgsConstructor
class Address {
    private Street street;
    private String city;
}
Enter fullscreen mode Exit fullscreen mode

Well, then we can simply use the @Mapping annotation to map to our nested property:

@Mapping(source = "street", target = "street.street")
Address updateAddress(AddressDTO addressDTO, @MappingTarget Address address);
Enter fullscreen mode Exit fullscreen mode

And this should work just fine. The tricky part is actually when the mapping is not one-to-one, but actually to specific fields of an object in a specific list, for example. Take a look:

@Data
@AllArgsConstructor
class Address {
    private List<Street> streets;
    private String city;
}
Enter fullscreen mode Exit fullscreen mode

Now, if we want to preserve the actual Street object and only modify its inner fields, the @Mapping annotation won't serve us anything. What we can do instead is use the @AfterMapping annotation and manually set our fields. This is how:

@Mapper
public interface AddressMapper {
    AddressMapper INSTANCE = Mappers.getMapper(AddressMapper.class);

    Address updateAddress(AddressDTO addressDTO, @MappingTarget Address address);

    @AfterMapping
    default void updateStreets(AddressDTO addressDTO, @MappingTarget Address address) {
        address.getStreets().forEach(street -> street.setStreet(addressDTO.getStreet()));
    }
}
Enter fullscreen mode Exit fullscreen mode

MapStruct will automatically call the after-mapping method after the update method has been called. In our after-mapping map, we will replace the name of each street without actually replacing the object itself. Pretty cool, right?

The Role of @AfterMapping

While MapStruct does a great job translating objects, sometimes you need a little extra logic to fine-tune your data. That’s where @AfterMapping comes into play. This annotation allows you to run custom code after the standard mapping process, perfect for when you need to add some custom conditions, or tweak lists like in the previous example.

Stay curious!

GitHub Repository

GitHub - raphaeldelio/mapstruct_after_mapping_example
*Contribute to raphaeldelio/mapstruct_after_mapping_example development by creating an account on GitHub.*github.com

Contribute

Writing takes time and effort. I love writing and sharing knowledge, but I also have bills to pay. If you like my work, please, consider donating through Buy Me a Coffee: https://www.buymeacoffee.com/RaphaelDeLio

Or by sending me BitCoin: 1HjG7pmghg3Z8RATH4aiUWr156BGafJ6Zw

Follow Me on Social Media

Stay connected and dive deeper into the world of Java with me! Follow my journey across all major social platforms for exclusive content, tips, and discussions.

Twitter | LinkedIn | YouTube | Instagram

Top comments (0)