When working with RESTful API's, there is always a clash for using DAO's to transfer data between microservices. The use of DAO's to transfer data between microservices opens up alot of issues, say for example there could be some fields in DAO which is not needed or there are fields that you need to add which is expected by other service. In either case, you need to update the DAO. The problem here is that DAO object should always be mapped according to the table it is working with. In the case explained above you need to update the DAO object. This is wrong. DAO should only be used for accessing data and no other purpose.
To answer this, we have DTO's which come into picture. Like DAO is used only for accessing the data say a mapping of your Database table, DTO's only purpose is to represent object while transferring it to any other service.
Now the question comes in of where the mapping and how the mapping should look like. To answer this question, we have MapStruct.
MapStruct is a code generator that simplifies the implementation of mappings between Java bean types based on a convention-over-configuration approach. It automates the tedious and error-prone task of writing mapping code, making your code cleaner and easier to maintain. This guide will show you how to use the @Mapper
and @Mapping
annotations in Spring Boot for MapStruct with practical code examples.
1. Setup Your Spring Boot Project
First, ensure you have a Spring Boot project. If you don’t have one, you can create a new one using Spring Initializr or your preferred method. Include the following dependencies in your pom.xml
:
xml<dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>1.5.0.Final</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.5.0.Final</version> <scope>provided</scope> </dependency>
2. Create Source and Destination Classes
Let's create two simple Java classes for source and destination objects.
Source Class:
javapackage com.example.demo.model; public class Source { private String firstName; private String lastName; private int age; // Getters and Setters public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
Destination Class:
javapackage com.example.demo.dto; public class Destination { private String fullName; private int age; // Getters and Setters public String getFullName() { return fullName; } public void setFullName(String fullName) { this.fullName = fullName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
3. Define a Mapper Interface
Next, create a mapper interface and annotate it with @Mapper
. Use the @Mapping
annotation to specify the mapping between source and destination fields.
javapackage com.example.demo.mapper; import com.example.demo.dto.Destination; import com.example.demo.model.Source; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; @Mapper public interface SourceDestinationMapper { SourceDestinationMapper INSTANCE = Mappers.getMapper(SourceDestinationMapper.class); @Mapping(source = "firstName", target = "fullName") @Mapping(source = "lastName", target = "fullName", ignore = true) // Optional, to show ignoring field Destination sourceToDestination(Source source); @Mapping(source = "fullName", target = "firstName") Source destinationToSource(Destination destination); }
4. Use the Mapper in Your Service or Controller
Now, you can use the generated mapper in your service or controller to perform the mapping.
Example Service:
javapackage com.example.demo.service; import com.example.demo.dto.Destination; import com.example.demo.mapper.SourceDestinationMapper; import com.example.demo.model.Source; import org.springframework.stereotype.Service; @Service public class MappingService { private final SourceDestinationMapper mapper = SourceDestinationMapper.INSTANCE; public Destination convertToDestination(Source source) { return mapper.sourceToDestination(source); } public Source convertToSource(Destination destination) { return mapper.destinationToSource(destination); } }
Example Controller:
javapackage com.example.demo.controller; import com.example.demo.dto.Destination; import com.example.demo.model.Source; import com.example.demo.service.MappingService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/mapping") public class MappingController { @Autowired private MappingService mappingService; @PostMapping("/toDestination") public Destination toDestination(@RequestBody Source source) { return mappingService.convertToDestination(source); } @PostMapping("/toSource") public Source toSource(@RequestBody Destination destination) { return mappingService.convertToSource(destination); } }
5. Testing the Mapping
Run your Spring Boot application and use tools like Postman or curl to test the endpoints.
Convert Source to Destination:
bashcurl -X POST http://localhost:8080/api/mapping/toDestination -H "Content-Type: application/json" -d '{"firstName":"John","lastName":"Doe","age":30}'
Convert Destination to Source:
bashcurl -X POST http://localhost:8080/api/mapping/toSource -H "Content-Type: application/json" -d '{"fullName":"John","age":30}'
Conclusion
In this guide, you learned how to use MapStruct in a Spring Boot application to map between different data transfer objects using the @Mapper
and @Mapping
annotations. This approach significantly reduces the boilerplate code required for such mappings and helps maintain clean and maintainable code. Happy coding!
0 Comments