Iurii
Iurii

Reputation: 1795

MapStruct requires Impl class

I have next classes:

Mapper

public interface DeviceTokensMapper {

    DeviceTokensMapper INSTANCE = Mappers.getMapper(DeviceTokensMapper.class);

    @Mappings({
            @Mapping(source = "tokenName", target = "tokenName"),
            @Mapping(source = "userOsType", target = "osType"),
    })

    DeviceTokensDTO toDeviceTokensDTO(DeviceTokens deviceTokens);
}

Entity:

@Entity
public class DeviceTokens {

    @Id
    @Column(name="token_name", nullable = false)
    private String tokenName;

    @Column(name = "os", nullable = false)
    @Enumerated
    private UserOSType userOsType;

    public DeviceTokens() {}

    public DeviceTokens(String tokenName, UserOSType userOSType) {
        this.tokenName = tokenName;
        this.userOsType = userOSType;
    }

    public String getTokenName() {
        return tokenName;
    }

    public void setTokenName(String tokenName) {
        this.tokenName = tokenName;
    }

    public UserOSType getUserOsType() {
        return userOsType;
    }

    public void setUserOsType(UserOSType userOsType) {
        this.userOsType = userOsType;
    }
}

DTO:

public class DeviceTokensDTO {

    private String tokenName;

    private UserOSType osType;

    public DeviceTokensDTO() {}

    public DeviceTokensDTO(String tokenName, UserOSType osType) {
        this.tokenName = tokenName;
        this.osType = osType;
    }

    public UserOSType getOsType() {
        return osType;
    }

    public void setOsType(UserOSType osType) {
        this.osType = osType;
    }

    public String getTokenName() {
        return tokenName;
    }

    public void setTokenName(String tokenName) {
        this.tokenName = tokenName;
    }
}

And method from spring service class where I use this mapping:

@Transactional
public DeviceTokensDTO storeToken(String tokenId, UserOSType userOsType) {
    DeviceTokens deviceTokens = new DeviceTokens(tokenId, userOsType);
    DeviceTokens newDevice = deviceTokensRepository.save(deviceTokens);

    return DeviceTokensMapper.INSTANCE.toDeviceTokensDTO(newDevice);
}

When I run above method I see next exception:

ERROR [dispatcherServlet]:? - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler processing failed; nested exception is java.lang.ExceptionInInitializerError] with root cause java.lang.ClassNotFoundException: dto.DeviceTokensMapperImpl

So why mapper require implementation class? Could please someone advise? Thanks.

Upvotes: 21

Views: 56618

Answers (10)

Demobilizer
Demobilizer

Reputation: 748

In your Mapper class, use @Mapper(componentModel = "spring")

and to generate Impl classes using mapstruct, you need both of following dependencies.

        <!-- Dependencies for Mapper -->
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${org.mapstruct.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>${org.mapstruct.version}</version>
        </dependency>

Upvotes: 3

Rahul Bhooteshwar
Rahul Bhooteshwar

Reputation: 1955

If you are using Project lombok along with mapstruct then you will need to configure both under maven compiler plugin, as both of them generate source at compile time.

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

Upvotes: 5

FBH
FBH

Reputation: 793

In your build.gradle add

compile group: 'org.mapstruct',name: 'mapstruct-jdk8',version: 1.2.0.Final



annotationProcessor group: 'org.mapstruct',name: 'mapstruct-processor',version: 1.2.0.Final

Enable annotation Processing in your setting

Bonus : add plugin intellij for mapstruct

Upvotes: 2

L&#234; văn Huy
L&#234; văn Huy

Reputation: 371

if you use maven, you need to add mapstruct-processor dependency as follows:

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-jdk8</artifactId>
    <version>1.2.0.Final</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.2.0.Final</version>
</dependency>

Upvotes: 34

Wentao Wan
Wentao Wan

Reputation: 111

I have met the same problem in my project with gradle. And I replace the build.gradel from

compile 'org.mapstruct:mapstruct:1.2.0.CR2'

to

compile 'org.mapstruct:mapstruct-jdk8:1.1.0.Final'
compile 'org.mapstruct:mapstruct-processor:1.1.0.Final'

Then sh build clean&build. It works now!

Upvotes: 4

James
James

Reputation: 300

I ran into this problem because I didn't run ./gradlew clean build (gradlew.bat for Windows) after creating/editing the mapper class or related classes.

Also, something I found was useful is ./gradlew clean build -x test works, but doesn't run all your test, which was a lot in my case.

Upvotes: 0

sh87
sh87

Reputation: 1133

In my case I had wrapped <plugin> within <pluginManagement> tags to workaround an eclipse (Mars) bug as follows

<pluginManagement>
 <plugin> ... </plugin> 
</pluginManagement>

Removing <pluginManagement> tags fixed it for me.

Upvotes: 1

Omtara
Omtara

Reputation: 3001

Are you using Maven? If yes, then most probably you have missed the mapstruct-processor configuration under the maven compiler plugin.

The proper configuration is as follows:

<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
        <version>${org.mapstruct.version}</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.5.1</version>
            <configuration>
                <source>1.6</source> <!-- or higher, depending on your project -->
                <target>1.6</target> <!-- or higher, depending on your project -->
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

Upvotes: 6

daliborp
daliborp

Reputation: 171

Do you have both mapstruct-processor-xx and mapstruct-xx libraries included in your project?

I had the same problem and I realized that I forgot to include mapstruct-processor-xx.

Upvotes: 13

Gunnar
Gunnar

Reputation: 18970

MapStruct generates code at compile time, and the call to Mappers.getMapper(DeviceTokensMapper.class); will look for the generated implementation of the mapper interface. For some reason it seems to be missing in your deployment unit (WAR etc.).

Btw. when working with Spring as your DI container, you can use @Mapper(componentModel="spring") and you will be able to obtain mapper instances via dependency injection instead of using the Mappers factory.

Upvotes: 20

Related Questions