Core Java

How to Set Target Property Values to Null in MapStruct

In mapping scenarios, we may encounter situations where certain fields should be explicitly set to null during the mapping process, even when the source property has a non-null value. MapStruct provides ways to control how null values are handled for target properties. This article explores how to set null values intentionally for target properties in MapStruct.

1. Example Setup

MapStruct is a compile-time code generator that simplifies object mappings between Java beans. By default, MapStruct copies the value from the source to the target if it is not null, and ignores null source properties unless configured otherwise. However, sometimes we want to explicitly set a target property to null. For example:

  • When mapping partial updates and applying specific null-handling logic.
  • When updating an entity, and certain fields must be reset.
  • When mapping between different layers and clearing sensitive or irrelevant data.

Here’s an example of two Java classes, a UserDto (source) and a User (target). Each time we update a User from UserDTO, we want to set the lastLoginIp field to null.

public class UserDTO {

    private String username;
    private String email;

    public UserDTO() {
    }

    public UserDTO(String username, String email) {
        this.username = username;
        this.email = email;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "UserDTO{" + "username=" + username + ", email=" + email + '}';
    }

}
public class User {

    private String username;
    private String email;
    private String lastLoginIp;

    public User() {
    }

    public User(String username, String email, String lastLoginIp) {
        this.username = username;
        this.email = email;
        this.lastLoginIp = lastLoginIp;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getLastLoginIp() {
        return lastLoginIp;
    }

    public void setLastLoginIp(String lastLoginIp) {
        this.lastLoginIp = lastLoginIp;
    }

    @Override
    public String toString() {
        return "User{" + "username=" + username + ", email=" + email + ", lastLoginIp=" + lastLoginIp + '}';
    }

}

2. Using @Mapping(target = "propertyName", expression = "java(null)")

If we always want the target property to be set to null when mapping, the simplest way is to use the expression attribute of the @Mapping annotation.

@Mapper
public interface UserMapper {

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

    @Mapping(target = "lastLoginIp", expression = "java(null)")
    void updateUser(UserDTO dto, @MappingTarget User user);
}

@MappingTarget allows updating an existing User object rather than creating a new one, and the expression java(null) ensures that the lastLoginIp field is explicitly set to null each time the mapper is used. We can check and test how the mapper works in practice:

public class MapstructNullDemo {

    public static void main(String[] args) {

        User user = new User();
        user.setUsername("thomas");
        user.setEmail("old@email.com");
        user.setLastLoginIp("192.168.1.10");

        UserDTO dto = new UserDTO();
        dto.setUsername("thomas");
        dto.setEmail("thomas@newmail.com");

        // Perform the mapping
        UserMapper.INSTANCE.updateUser(dto, user);

        IO.println("Username: " + user.getUsername());
        IO.println("Email: " + user.getEmail());
        IO.println("Last Login IP: " + user.getLastLoginIp());
    }

}

Output:

Username: thomas
Email: thomas@newmail.com
Last Login IP: null

If we want to set a property to null only under certain conditions, we can handle that using a default method or custom logic.

@Mapper
public interface UserMapper {

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

    @Mapping(target = "email", expression = "java(mapEmail(dto.getEmail()))")
    void updateUser(UserDTO dto, @MappingTarget User user);
    
     default String mapEmail(String email) {
        if (email != null && email.endsWith("@test.com")) {
            return null; // Hide test emails
        }
        return email;
    }
}

Here, the mapEmail() method defines conditional mapping logic. If the email ends with @test.com, the target property is explicitly set to null.

Testing the Mapper

Let’s test our mapper to verify the null-handling behavior:

public class MapstructNullDemo {

    public static void main(String[] args) {

        User user = new User();
        user.setUsername("thomas");
        user.setEmail("old@email.com");
        user.setLastLoginIp("192.168.1.10");

        UserDTO dto = new UserDTO();
        dto.setUsername("thomas");
        dto.setEmail("thomas@test.com"); 

        UserMapper.INSTANCE.updateUser(dto, user);

        IO.println("Username: " + user.getUsername());
        IO.println("Email: " + user.getEmail()); 
        IO.println("Last Login IP: " + user.getLastLoginIp()); 
    }

}

Output:

Username: thomas
Email: null
Last Login IP: 192.168.1.10

3. Using @Mapping(target = "field", ignore = true) and @AfterMapping

Sometimes we might want to set the field to null after applying additional logic. We can ignore it in the main mapping and set it to null afterward using @AfterMapping.

@Mapper
public interface UserMapper {

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

    @Mapping(target = "lastLoginIp", ignore = true)
    void updateUser(UserDTO dto, @MappingTarget User user);

    @AfterMapping
    default void resetLoginIp(@MappingTarget User user) {
        user.setLastLoginIp(null);
    }
}

Using ignore = true prevents MapStruct from automatically mapping lastLoginIp, and the @AfterMapping method runs immediately after the update, allowing us to reset or modify specific fields. We can even add conditional logic here, for example, to set the field to null only under certain conditions.

4. Using NullValuePropertyMappingStrategy.SET_TO_NULL

The NullValuePropertyMappingStrategy lets us define how MapStruct behaves when source fields are null. Setting the strategy to SET_TO_NULL ensures that any null value in the DTO will nullify the target property.

@Mapper
public interface UserMapper {

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

    @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_NULL)
    @Mapping(target = "lastLoginIp", expression = "java(null)")
    void updateUser(UserDTO dto, @MappingTarget User user);
}
  • SET_TO_NULL ensures that whenever a source property is null, the corresponding target property will also be set to null.
  • Other available strategies include:
    • IGNORE: Leaves the target property unchanged if the source is null (default behavior).
    • SET_TO_DEFAULT: Sets the property to its default value (e.g., 0 for primitives, false for booleans).

Let’s test our mapper to verify the null-handling behavior:

public class MapstructNullDemo {

    public static void main(String[] args) {

        User user = new User();
        user.setUsername("thomas");
        user.setEmail("old@email.com");
        user.setLastLoginIp("192.168.1.10");

        UserDTO dto = new UserDTO();
        dto.setUsername("thomas");
        dto.setEmail(null); // this should nullify the User's email

        UserMapper.INSTANCE.updateUser(dto, user);

        IO.println("Username: " + user.getUsername());
        IO.println("Email: " + user.getEmail()); // null
        IO.println("Last Login IP: " + user.getLastLoginIp()); 
    }

}

Output:

Username: thomas
Email: null
Last Login IP: null

This approach is ideal when we want null values in the DTO to clear corresponding entity fields during updates.

5. Conclusion

In this article, we learned how to set a target property to null when updating an entity from a DTO using MapStruct. We explored three strategies: using expression = "java(null)" to always set a field to null, using ignore = true with @AfterMapping for more flexible post-processing, and using NullValuePropertyMappingStrategy.SET_TO_NULL to automatically propagate null values during mapping.

6. Download the Source Code

This article explored how to use Java MapStruct to set a null value for a specific property.

Download
You can download the full source code of this example here: java mapstruct set null value property

Omozegie Aziegbe

Omos Aziegbe is a technical writer and web/application developer with a BSc in Computer Science and Software Engineering from the University of Bedfordshire. Specializing in Java enterprise applications with the Jakarta EE framework, Omos also works with HTML5, CSS, and JavaScript for web development. As a freelance web developer, Omos combines technical expertise with research and writing on topics such as software engineering, programming, web application development, computer science, and technology.
Subscribe
Notify of
guest

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

0 Comments
Oldest
Newest Most Voted
Back to top button