How to use Mapstruct in Kotlin Spring Boot with the injection of Spring Boot Services and Repositories
When passing a DTO to the Spring Boot REST controller, some relationships between entities are represented by using the id / unique property instead of the entire object. We can make the conversion and error handling easier by injecting Spring Boot services and repositories into the Mapstruct mapper to avoid repetitive code.
First, I would like to thank Muhammad Usama and Antonello Zanini's articles on the introduction of using Spring Boot with the Mapstruct library. Please check out their articles, which I referenced a lot in the source code:
- Avoiding Boilerplate Code With MapStruct, Spring Boot and Kotlin (Premium Required)
- How to use MapStruct with Spring Boot (Kotlin) painlessly
The source code for the demonstration is on my GitHub. Feel free to check them out.
Installing MapStruct to project
Sync the Gradle dependencies and install the Mapstruct packages.
Setting up Entities and the Repository to inject
We first set up two classes, AppUser
and Department
. Each user belongs to one department.
We then set up the DepartmentRepository
for us to inject, you can choose to inject the service layer into the mapper. The demonstration is only for simplification.
Setting up the Dto
Create the UserDto
as the mapping target
Setting up the Mapper
First, we define the mapper class as an abstract class, annotate with @Mapper(componentModel = "spring")
Then, inject the previously created DepartmentRepository
, create a lateinit var
and perform dependency inject with with @Autowired
.
Now, on the actual mapper, use @Named
annotation to annotate a method that uses the DepartmentRepository
to obtain the department id.
Finally, create an abstract function that maps the DTO to the entity and marks with the @Mapping
annotation when converting the department name since the names between the source and target classes are not the same.
Use the qualifiedByName
property of the @Mapping
annotation class such that the department's information conversion will be using the function we just created.
Finally, run the Gradle tasks to create the implementation classes of the mapper.
#With in the working directory./gradlew clean
./gradlew kaptKotlin
Perform Testing
Of course, we would like to create some tests to ensure that the mappers are good enough for us to implement.
First, we create some seed data by creating a new file: src/test/resources/import.sql
. Then, add the following SQL statement:
insert into department (department_id, department_name) values ('DEPT_A', 'Department A');
Then, we set up the test class by using @SpringBootTest
. By using @Autowired
, we inject the mapper into the test class.
The first test tests if the Dto can be converted to the entity by the mapper if the department id can be found in the database:
The second test tests if an error will be thrown if the department id does not exist in the database:
Thank you for your reading, and happy coding.