MapStruct implementation is not working in Spring Boot Web Application
Asked Answered
C

10

18

I am a newbie to Spring Boot and MapStruct Tool.

Earlier, A Project(written by other team using these technologies) is not starting up. Then, I had made some changes in Mapper Abstract Class but now mapper object is coming as null on application startup.

Mapper Abstract Class:

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

    public UserAndEmployeeMapper INSTANCE = Mappers.getMapper( UserAndEmployeeMapper.class );

    @Mapping(source = "username", target = "name")
    @Mapping(source = "ssn", target = "ssn", defaultValue = "xxxxxx" )
    @Mapping(target = "salary", constant = "34.67")
    @Mapping(target = "dob", dateFormat = "dd/MM/yyyy", constant = "10/12/2002")
    public abstract Employee mapToEmployee(User user);

    public abstract List<Employee> mapToEmployee(List<User> users);

    @Mapping(source = "name", target = "username")
    public abstract User mapToUser(Employee employee);

    public abstract List<User> mapToUser(List<Employee> employees);

}

LoginServiceImpl class

@Service("loginService")
public class LoginServiceImpl implements LoginService{

    private static final AtomicLong counter = new AtomicLong();

    @Autowired
    private EmployeeDao employeeDao;

    private UserAndEmployeeMapper userAndEmployeeMapper;
...

}

pom.xml

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven.compiler.plugin.version}</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${mapstruct.jdk8.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
</build>

After I added @Autowired in LoginServiceImpl, application is not starting and following error log is showing

***************************
APPLICATION FAILED TO START
***************************

Description:

Field userAndEmployeeMapper in org.service.impl.LoginServiceImpl required a bean of type 'org.mapper.UserAndEmployeeMapper' that could not be found.


Action:

Consider defining a bean of type 'org.mapper.UserAndEmployeeMapper' in your configuration.

Any suggestions ?

Chally answered 9/6, 2017 at 12:39 Comment(4)
did you try exactly the same action as spring boot error said to you? I mean define UserAndEmployeeMapper spring bean. It seems that Mapper annotation does not work as bean definitionAdieu
@Normal: Declaring Component on top of UserAndEmployeeMapper abstract class gives the same error output.Chally
didn't notice it is abstract, sorry. This is seems to be quite similar problem #32610255Adieu
How do you invoke the application? Via maven / gradle or via the IDE? Where is your Application class?Edeline
C
4

Making abstract class as an interface worked for me.

public interface UserAndEmployeeMapper {
Chally answered 19/6, 2017 at 12:6 Comment(1)
for anyone having trouble finding where does the mapper fail, it is also useful to set unmappedTargetPolicy (e.g. @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.WARN) )Supersede
H
12

try this in pom.xml:

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>17</source>
                <target>17</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>1.4.2.Final</version>
                    </path>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>1.18.22</version>
                    </path>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok-mapstruct-binding</artifactId>
                        <version>0.2.0</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>

and reload Maven (ALT+F5)

Hatter answered 31/3, 2022 at 10:28 Comment(0)
E
6

First of all, public UserAndEmployeeMapper INSTANCE = Mappers.getMapper( UserAndEmployeeMapper.class ); should only be used with the default component model, otherwise you risk to have the UserAndEmployeeMapper not correctly initialized.

The UserAndEmployeeMapper in your LoginServiceImpl must be annotated with @Autowired, otherwise it cannot be injected by Spring, and that's why it is null.

I don't know your package structure. If your Spring Boot application class in the package org then it will pick up the UserAndEmployeeMapperImpl. Otherwise make sure that the spring configuration picks up the UserAndEmployeeMapperImpl.

If everything from above is correctly setup and you are starting the application via an IDE make sure that target/generated-sources or the alternative for Gradle is part of your sources and is picked up. Have a look at the IDE Support to make sure that you have correctly setup the Annotation processor discovery for an IDE. For example, IntelliJ will not invoke the MapStruct Annotation Processor with your current setup, it doesn't pick up the annotationProcessorPaths from the maven compiler.

Edeline answered 9/6, 2017 at 18:30 Comment(4)
Are u talking about this component model @Mapper(componentModel = "spring") ?Chally
Yes I am talking about that oneEdeline
I had changed Mapper class as interface public interface UserAndEmployeeMapper. Now UserAndEmployeerMapper object is initialized in LoginServiceImpl class.Chally
It's really strange why the Spring couldn't inject the bean as an abstract class. It should have worked. It's good that you have solved your problem. Regarding spring, it is actually recommended to use interfaces instead of abstract classesEdeline
C
4

Making abstract class as an interface worked for me.

public interface UserAndEmployeeMapper {
Chally answered 19/6, 2017 at 12:6 Comment(1)
for anyone having trouble finding where does the mapper fail, it is also useful to set unmappedTargetPolicy (e.g. @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.WARN) )Supersede
S
2

springfox-swagger2 dependency is loading older version of mapstruct after excluding it and adding specific version of mapstruct dependency in pom.xml it started generating sources

<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.2.2</version>
<exclusions>
 <exclusion>
 <groupId>org.mapstruct</groupId>
  <artifactId>mapstruct</artifactId>
 </exclusion>
</exclusions>

Added below map struct dependency

<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.3.0.Beta1</version>

Som answered 30/8, 2018 at 13:44 Comment(0)
S
2

For making all mapper classes to qualify as spring bean add following compilerArgs to your maven-compiler-plugin.

<compilerArgs>
<arg>-Amapstruct.suppressGeneratorTimestamp=true</arg>
<arg>-Amapstruct.defaultComponentModel=spring</arg>
</compilerArgs> 

if using maven-processor-plugin add following options

<options>                       
<mapstruct.suppressGeneratorTimestamp>
true
</mapstruct.suppressGeneratorTimestamp>                      
<mapstruct.defaultComponentModel>
spring
</mapstruct.defaultComponentModel>
</options>
Socket answered 23/9, 2018 at 12:29 Comment(0)
P
1

Its simple first you need to create interface as below,

@Mapper(componentModel = "spring")
public interface CompanyMapper {
     @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
     void updateCustomerFromDto(CompanyDto dto, @MappingTarget CompanyBean entity);
}

Now when you run the application it will generate its implementation file it self in the target/generated-source folder

So whenever you want to use CompanyMapper than you only need to autowired it as we use @Mapper(componentModel = "spring") so spring boot can able to inject this.

And also you need to enable the Maven annotation processing by following steps in eclipse :

Step1 :- Right click on the project
Step2 :- Go to Properties
Step3 : Expand Maven from sidebar
Step4 :- Select Annotation Processing
Step5 :- Checked Enable Project Specific settings
Step6 :- Select Experemental Radio button
Step7 :- Apply and Close

Its Done ! I hope its helpful to someone.

Purse answered 14/2, 2022 at 14:46 Comment(0)
H
0

In my case, I believe the error was due to an incomplete build.gradle.

Before

dependencies {
    compile group: 'org.mapstruct', name: 'mapstruct-processor', version: '1.2.0.Final'
}

After

apply plugin: 'net.ltgt.apt'

dependencies {
    compile group: 'org.mapstruct', name: 'mapstruct-processor', version: '1.2.0.Final'
    compile group: 'org.mapstruct', name: 'mapstruct-jdk8', version: '1.2.0.Final'
    apt 'org.mapstruct:mapstruct-processor:1.2.0.Final'
}


buildscript {
    repositories {
        mavenCentral()
        maven {
            url "https://plugins.gradle.org/m2/"
        }
    }
    dependencies {
        classpath("net.ltgt.gradle:gradle-apt-plugin:0.9")
    }
}

Ensure you have configured your build.gradle properly, as described in the docs.

Hawkweed answered 30/9, 2018 at 13:51 Comment(0)
S
0

If using Eclipse, you may have to install the m2e-apt plugin.

It will enable the annotation processor for MapStruct in the IDE.

Schmitz answered 25/10, 2021 at 9:59 Comment(0)
H
0

Make sure you put annotation processor path in the dependency.

<plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>${java.version}</source> <!-- depending on your project -->
                <target>${java.version}</target> <!-- depending on your project -->
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                    <!-- other annotation processors -->
                </annotationProcessorPaths>
            </configuration>
        </plugin>
Haggis answered 29/4, 2022 at 12:2 Comment(0)
F
-1

POM.XML

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.customer-service</groupId>
    <artifactId>customer-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>customer-service</name>
    <description>Microservice Client-Facture avec Spring Boot</description>
    <properties>
        <java.version>11</java.version>
        <spring-cloud.version>2021.0.3</spring-cloud.version>
         <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${org.mapstruct.version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->

        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-ui</artifactId>
            <version>1.5.2</version>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

     <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>1.18.16</version>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
             
                <configuration>
                    <source>17</source> <!-- depending on your project -->
                    <target>17</target> <!-- depending on your project -->
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>1.18.16</version>
                        </path>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                        </path>
                        <!-- other annotation processors -->
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
Faints answered 10/6, 2022 at 10:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.