Project Lombok and Mapstruct with Spring Boot

In this article, I show the configuration and usage of two useful tools which are Project Lombok and Mapstruct with Spring Boot, to reduce a lot of code.

Content:

  • The Lombok and Mapstruct plugins
  • Lombok
  • Mapstruct

If you want more details, check this video.

All the code is available in the following repository.

Today, I’m gonna show you two useful libraries: Mapstruct and Lombok. Those libraries will reduce a lot of code. They will write code for you. Lombok is used to generate the setters, getters, constructors and builders. And Mapstruct is used to generate mappers. I use it to map DTO objects to data objects.

The Lombok and Mapstruct plugins

Before starting, you will need to install some plugins in your IDE. This is not mandatory but it will help you with autocomplete and detecting errors easily. If you use IntelliJ, you can go to the IntelliJ plugins store and look for those plugins, both Lombok and Mapstruct.

If you use Eclipse, for the Lombok file, download the Lombok JAR file from the Maven Central Repository (https://mvnrepository.com/artifact/org.projectlombok/lombok). Run it by double-clicking or with java -jar, and an installer window will appear detecting your Eclipse installation. Follow the instructions and the plug-in will be installed. And for Mapstruct, go to the plugins marketplace of Eclipse, https://marketplace.eclipse.org/content/mapstruct-eclipse-plugin, download the file and load it into your client marketplace. Follow the instructions and everything should be ready.

Lombok

Now that you have your IDE ready, let’s start adding the first dependency to the project.

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>

Lombok will generate getters, setters, constructors and builders for me. Let’s open a DTO class and try it adding the some annotations.

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class UserDto {
    ...
}

@AllArgsConstructor will give me the constructor with all the arguments and @NoArgsConstructor will generate me the constructor with no argument. @Data will generate me all the getters, setters, toString and hashcode methods. And @Builder will give me a builder.

The builder allows me to create an entity easily with the needed parameters.

var user = UserDto.builder()
   .id(1)
   .login("login")
   .build();

For the last useful annotation, I will add it to the services.

 @RequiredArgsConstructor
 @RestController
 @RequestMapping("/v1")
 public class AuthenticationController {

    private final UserService userService;
    private final UserAuthenticationProvider userAuthenticationProvider;

With @RequiredArgsConstructor I changed the autowired services to final fields. This way, the annotation will generate a constructor with those fields. This constructor will be the one used by Spring to inject the services from its context.

Mapstruct

Mapstruct will need two Maven dependencies.

        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>1.3.1.Final</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.3.1.Final</version>
        </dependency>

To use Mapstruct, I’ve created a dedicated package for all the mappers. Inside will be located all the mappers separated by entities.

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

    UserDto toUserDto(User user);

    UserSummaryDto toUserSummary(User user);

    List<UserSummaryDto> toUserSummaryDtos(List<User> users);

    @Mapping(target = "userDto.id", source = "id")
    @Mapping(target = "userDto.firstName", source = "firstName")
    @Mapping(target = "userDto.lastName", source = "lastName")
    ProfileDto userToProfileDto(User user);

    @Mapping(target = "password", ignore = true)
    User signUpToUser(SignUpDto signUpDto);
}

The first annotation, @Mapper(componentModel = “spring”), declares the interface as a mapper to be implemented by Mapstruct. And I specify that I use Spring as dependency injector. This way, the mapper will be available inside the Spring context to be autowired as a service.

Then, I just declare the methods per mapper from a source entity to a different target object. By default, Mapstruct will map each field which has the same name.

For the lists, I need to have almost the method for a single element. The iteration will be handled by Mapstruct.

If I need a specify mapping, using inner fields of the source object, or having different namings from the source to the target, I have the @Mapping annotation. Inside this annotation, I specify the target field and the source field. If I have different levels, inner fields, I can use the dot notation.

And finally, if I want to ignore some fields, I have the ignore = true option to avoid a field to be mapped.

Here is the an extract of the resulting implementation which is generated by Mapstruct after the project compilation.

@Component
public class UserMapperImpl implements UserMapper {
    public UserMapperImpl() {
    }

    public UserDto toUserDto(User user) {
        if (user == null) {
            return null;
        } else {
            UserDto userDto = new UserDto();
            userDto.setId(user.getId());
            userDto.setFirstName(user.getFirstName());
            userDto.setLastName(user.getLastName());
            userDto.setLogin(user.getLogin());
            return userDto;
        }
    }

    public UserSummaryDto toUserSummary(User user) {
        if (user == null) {
            return null;
        } else {
            UserSummaryDto userSummaryDto = new UserSummaryDto();
            userSummaryDto.setId(user.getId());
            userSummaryDto.setFirstName(user.getFirstName());
            userSummaryDto.setLastName(user.getLastName());
            return userSummaryDto;
        }
    }

    public List<UserSummaryDto> toUserSummaryDtos(List<User> users) {
        if (users == null) {
            return null;
        } else {
            List<UserSummaryDto> list = new ArrayList(users.size());
            Iterator var3 = users.iterator();

            while(var3.hasNext()) {
                User user = (User)var3.next();
                list.add(this.toUserSummary(user));
            }

            return list;
        }
    }

    ...
}

References

All the code is available in the following repository.

My New ebook, How to Master Git With 20 Commands, is available now.

2 responses to “Project Lombok and Mapstruct with Spring Boot”

  1. […] rest of the class annotations are Lombok annotations. With those annotations, I don’t need to create the constructors, getters and […]

    Like

  2. […] I’ve added Lombok to generate some code. I will also add Mapstruct, but this one isn’t available at Spring […]

    Like

Leave a comment

A WordPress.com Website.