DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Comparing ModelMapper and MapStruct in Java: The Power of Automatic Mappers
  • How To Best Use Java Records as DTOs in Spring Boot 3
  • OpenAPI From Code With Spring and Java: A Recipe for Your CI
  • How AI Is Rewriting Full-Stack Java Systems: Practical Patterns with Spring Boot, Kafka and WebSockets

Trending

  • The Hidden Bottlenecks That Break Microservices in Production
  • Working With Cowork: Don’t Be Confused
  • Why Good Models Fail After Deployment
  • Improving DAG Failure Detection in Airflow Using AI Techniques
  1. DZone
  2. Coding
  3. Java
  4. That’s How You Can Use MapStruct With Lombok in Your Spring Boot Application

That’s How You Can Use MapStruct With Lombok in Your Spring Boot Application

In this article, you will find code examples and explanations on how to work with MapStruct, Lombok, and Spring Boot in an efficient way.

By 
Viacheslav Aksenov user avatar
Viacheslav Aksenov
·
Jul. 03, 23 · Tutorial
Likes (17)
Comment
Save
Tweet
Share
23.6K Views

Join the DZone community and get the full member experience.

Join For Free

Hi! My name is Viacheslav Aksenov, and I am a senior software developer. Most often, I have to write code using Java and Kotlin. I have accumulated many examples of how to simplify boilerplate code using simple but effective libraries.

Introduction

When you implement services of any size, you often need to move data from one structure to another. Often this is the same data that is used on different layers of logic — in business logic, at the database level, or at the controller level for transfer to front-end applications.

To transfer this data, you have to repeat a lot of boilerplates. It's exhausting. I want to draw your attention to a library that will help you save your energy. Meet MapStruct!

With this library, you can only specify the structure mapping scheme. And the implementation will be collected by the library itself.

Where To Find It

The latest release can be found in Maven central repository.

You can add it to your pom.xml:

XML
 
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.5.5.Final</version>
</dependency>


And you need to add an annotation processor to your plugins:

XML
 
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.5.1</version>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <annotationProcessorPaths>
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>1.5.5.Final</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>


If you use Gradle, it can be added to your build.gradle:

XML
 
implementation "org.mapstruct:mapstruct:${mapstructVersion}"
annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"


How It Works

Let's take some data classes for example:

Java
 
public class CatEntity {
    private Long id;
    private String name;
    private String color;
    // getters and setters
}
 
public class CatDto {
    private Long id;
    private String name;
    private String color;
    // getters and setters
}


And that's all code we need to write for its implementation:

Java
 
@Mapper
public interface CatMapper {

    CatEntity toEntity(CatDto dto);

    CatDto toDto(CatEntity entity);
}


The implementation of this interface will be created by MapStruct itself:

Java
 
@Generated
public class CatMapperImpl implements CatMapper {

    @Override
    public CatEntity toEntity(CatDto dto) {
        if ( dto == null ) {
            return null;
        }

        CatEntity catEntity = new CatEntity();

        catEntity.setId( dto.getId() );
        catEntity.setName( dto.getName() );
        catEntity.setColor( dto.getColor() );

        return catEntity;
    }

    @Override
    public CatDto toDto(CatEntity entity) {
        if ( entity == null ) {
            return null;
        }

        CatDto catDto = new CatDto();

        catDto.setId( entity.getId() );
        catDto.setName( entity.getName() );
        catDto.setColor( entity.getColor() );

        return catDto;
    }
}


How To Use MapStruct With Java’s Records

In Java 14, there were added record classes. MapStruct can handle them too:

Java
 
public class CatEntity {
    private Long id;
    private String name;
    private String color;
    // getters and setters
}
 
public record CatRecord(
    Long id,
    String name,
    String color
) {
}


And if we create an interface for mapping:

Java
 
@Mapper
public interface CatRecordMapper {

    CatEntity toEntity(CatRecord record);

    CatRecord toRecord(CatEntity entity);
}


Then Mapstruct will generate the realization as:

Java
 
@Generated
public class CatRecordMapperImpl implements CatRecordMapper {

    @Override
    public CatEntity toEntity(CatRecord record) {
        if ( record == null ) {
            return null;
        }

        CatEntity catEntity = new CatEntity();

        catEntity.setId( record.id() );
        catEntity.setName( record.name() );
        catEntity.setColor( record.color() );

        return catEntity;
    }

    @Override
    public CatRecord toRecord(CatEntity entity) {
        if ( entity == null ) {
            return null;
        }

        Long id = null;
        String name = null;
        String color = null;

        id = entity.getId();
        name = entity.getName();
        color = entity.getColor();

        CatRecord catRecord = new CatRecord( id, name, color );

        return catRecord;
    }
}


How To Use MapStruct With Project Lombok

In the Java world, there is a big and widely known library — Project Lombok. It also allows for a decrease in boilerplate code that developers have to write. More details about this library you can find on the official website.

To add this library to your project, you need to add it to your pom.xml:

XML
 
<dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.28</version>
        <scope>provided</scope>
</dependency>


 And also, you need to add it to annotation processors:

XML
 
<annotationProcessorPaths>
    <path>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.28</version>
    </path>
</annotationProcessorPaths>


For Gradle, it is a little bit simpler. Just add it to  build.gradle

XML
 
implementation 'org.projectlombok:lombok:1.18.28'
annotationProcessor "org.projectlombok:lombok:1.18.28"


And an important step! To integrate Project Lombok with MapStruct, you need to add a binding library:

annotationProcessor 'org.projectlombok:lombok-mapstruct-binding:0.2.0'


For our example, it is enough to use @Data annotation, which adds getters and setters.

Java
 
@Data
public class CatDto {
    private Long id;
    private String name;
    private String color;
}

@Data
public class CatEntity {
    private Long id;
    private String name;
    private String color;
}


For the same mapper interface, it will generate the following implementation:

Java
 
public interface CatMapper {
    CatEntity toEntity(CatDto dto);
    CatDto toDto(CatEntity entity);
}

@Generated
public class CatMapperImpl implements CatMapper {

    @Override
    public CatEntity toEntity(CatDto dto) {
        if ( dto == null ) {
            return null;
        }

        CatEntity catEntity = new CatEntity();

        catEntity.setId( dto.getId() );
        catEntity.setName( dto.getName() );
        catEntity.setColor( dto.getColor() );

        return catEntity;
    }

    @Override
    public CatDto toDto(CatEntity entity) {
        if ( entity == null ) {
            return null;
        }

        CatDto catDto = new CatDto();

        catDto.setId( entity.getId() );
        catDto.setName( entity.getName() );
        catDto.setColor( entity.getColor() );

        return catDto;
    }
}


How To Add Spring Boot Logic Into Mapper

Sometimes it is required to retrieve some fields from Spring's beans. Let's assume that we don't store weight information in the database, which is not reachable in Entity. Instead of it, we have some Spring's service that provides this information.

Java
 
@Data
public class CatDto {
    private Long id;
    private String name;
    private String color;
    private Integer weight;
}


@Data
public class CatEntity {
    private Long id;
    private String name;
    private String color;
}
And there is service that provides weight information:

@Service
public class CatWeightProvider {

    public Integer getWeight(String name) {
        // some logic for retrieving weight info
        return 5;
    }
}


To use this bean to retrieve weight info inside the mapper interface should be replaced with an abstract class with the method that describes all additional logic.

Java
 
@Mapper(componentModel = "spring")
public abstract class CatMapper {

    @Autowired
    private CatWeightProvider provider;

    @Mapping(target = "weight", source = "entity.name", qualifiedByName = "retrieveWeight")
    public abstract CatDto toDto(CatEntity entity);

    @Named("retrieveWeight")
    protected Integer retrieveWeight(String name) {
        return provider.getWeight(name);
    }
}


In this case, MapStruct will generate the realization of this abstract class:

Java
 
@Generated
@Component
public class CatMapperImpl extends CatMapper {

    @Override
    public CatDto toDto(CatEntity entity) {
        if ( entity == null ) {
            return null;
        }

        CatDto catDto = new CatDto();

        catDto.setWeight( retrieveWeight( entity.getName() ) );
        catDto.setId( entity.getId() );
        catDto.setName( entity.getName() );
        catDto.setColor( entity.getColor() );

        return catDto;
    }
}


How To Ignore Field and Map Fields With Different Names

For example, fields in the database entity and fields in dto have different names. 

And for example, we need to ignore field weight in dto layer.

Java
 
@Data
public class CatDto {
    private String name;
    private String color;
    private Integer weight;
}

@Data
public class CatEntity {
    private Long idInDatabase;
    private String nameInDatabase;
    private String colorInDatabase;
}


It can be done with the following parameters:

Java
 
@Mapper
public interface CatMapper {

    @Mapping(target = "weight", ignore = true)
    @Mapping(target = "name", source = "entity.nameInDatabase")
    @Mapping(target = "color", source = "entity.colorInDatabase")
    CatDto toDto(CatEntity entity);
}


So, implementation from MapStruct will be:

Java
 
@Generated
public class CatMapperImpl implements CatMapper {

    @Override
    public CatDto toDto(CatEntity entity) {
        if ( entity == null ) {
            return null;
        }

        CatDto catDto = new CatDto();

        catDto.setName( entity.getNameInDatabase() );
        catDto.setColor( entity.getColorInDatabase() );

        return catDto;
    }
}


Conclusion

We have covered the most popular scenarios that arise when developing applications with multiple layers. Thus, the combination of Project Lombok and MapStruct libraries can significantly save the developer's time and effort on boilerplates. You can find code examples on my GitHub.
Thank you for your attention!

Data transfer object Library Spring Boot Java (programming language) Data mapping

Opinions expressed by DZone contributors are their own.

Related

  • Comparing ModelMapper and MapStruct in Java: The Power of Automatic Mappers
  • How To Best Use Java Records as DTOs in Spring Boot 3
  • OpenAPI From Code With Spring and Java: A Recipe for Your CI
  • How AI Is Rewriting Full-Stack Java Systems: Practical Patterns with Spring Boot, Kafka and WebSockets

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook