Save time using Lombok + MapStruct
Write code is great, but sometimes is better generate code than type them. For this article I’ll present some options, such as Lombok and MapStruct.
Why?
As I said before, sometimes writing a repeated piece of code is not a good idea, since this is a DRY (Don’t Repeat Yourself).
Back in time, to 2000’s 👴 working with EJB 2.x the tool to generated code was XDoclet with Apache Ant if I’m not wrong and I have to say, I don’t miss that time.
After the arrival of the Java Annotations changed the game.
Lombok uses the Java Annotations feature to generate code for us ❤️, not all code but the repeated code.
On the other hand we have the MapStruct to map objects, in this article to map the entity to client response.
How?
- Lombok
1.1. Add the dependency
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
1.2. At the code
1.2.1. Log
Add the annotation @Slf4j and the object log will be available for use. Look at line 3, when the annotation was declared and line 20, where the object is available for use.
2021-06-19 21:44:01.475 INFO 4278 --- [nio-8092-exec-1] com.costa.luiz.lombok.CountryController : Received 3 countries
1.2.2. Data
Writes for you all the following annotation at once: Setter/Getter/ToString/EqualsAndHashCode/RequiredArgsConstructor
Useful for JPA entities like the entity Country.
1.2.3. Builder
Builder it is. Avoid generating again the Builder when an attribute is modified. I’ll modify the class above to show the annotation, and add the @NoArgsConstructor and @AllArgsConstructor too, since it is required for JPA.
2. MapStruct
To use MapStruct, at least two steps are required.
2.1 Add the dependency and add it to the build section.
- Dependency
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>
- Build section. To make Lombok and MapStruct work together is requires the code between the commented excerpts.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<!-- START - To work together-->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<!-- END - To work together-->
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
2.2. At the code
Declare an interface annotated with @Mapper
, an instance, line 4, and the method to map the objects, line 6.
This will generate the class below. ⚠️ The class shouldn’t be modified.
Declare a @Bean
for CountryMapper
After the configuration is time to take advantage on Rest Controller
The class below shows a test for the Mapper
Class diagram
After some classes this is the final relationship between the classes
Environment
To run this example in your local environment you need to have:
- Java 11+
- Maven
Conclusion
Code by configuration is a good practice to tackle repeated tasks, although for complex projects and many Annotation Processors can be hard, but easier than code everything 😉.