Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapstruct does not use builders defined by Lombok

Solution:

I had to change the ordering of my mapstruct and lombok annotationProcessorPaths.

I had to place mapstruct above lombok, then it worked.

I updated the pom below to the working version, so there is no non-working-code in here.

I also converted the lombok version back to the current release and not using the edge-version.


Original Problem:

I have 2 more or less identical sets of classes (see example below)

  • one set are the DTOs of my API, which I want to have immutable, using Lombok's @Value and @Builder
  • one set are the entities that are going to be stored in the database. With Lombok's @Data

Initially I set the project up to use:

  • Lombok 1.18.12
  • Mapstruct 1.3.1
  • Java 11
  • Maven

I found the Lombok documentation explaining how to add the annotation-processor to the maven-plugin https://projectlombok.org/setup/maven

But when executing I still get Error:(16,25) java: ClassX does not have an accessible parameterless constructor.

Searching for this message I found some 2 to 3 years of problems, but nothing up to date. Also I saw, that the issue was resolved for those posts.

In at least one of the posts it was mentioned, that it worked, when splitting the project into modules. And this worked for me as well. When I move the DTOs to another maven module, build them there and set the dependency it works, but this is definitely not the project-structure I want to have. Also since I might need to move my entities out as well and I don't want to create a new module for each Pojo-structure I'm creating.

I also found that post on the Lombok Edge version: https://projectlombok.org/download-edge The second point in the change-list is

BREAKING CHANGE: mapstruct users should now add a dependency to lombok-mapstruct-binding. This solves compiling modules with lombok (and mapstruct).

So I tried that as well. I added the repository to my pom, added lombok-mapstruct-binding and set the lombok version to edge-SNAPSHOT

But even after a clean the compile step fails.


In between I changed my DTOs to use @Data as well, but I would like to change this back.

Finally here are some examples and details on the code.

DTOs

@Data
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = BDto.class, name = "b"),
        @JsonSubTypes.Type(value = CDto.class, name = "c")
})
public abstract class ADto {
    private long id;
    private String type;
    private Set<String> metadata;
    private Set<String> tags;
}

@Data
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BDto extends ADto {
    private String path;

    @Builder
    private BDto(long id, String path, Set<String> metadata, Set<String> tags) {
        super(id, "b", metadata, tags);
        this.path = path;
    }
}

@Data
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CDto extends ADto {
    private String name;
    private Set<A> collection;

    @Builder
    private CDto(long id, String name, Set<A> collection, Set<String> metadata, Set<String> tags) {
        super(id, "c", metadata, tags);
        this.collection = collection;
        this.name = name;
    }
}

Entities

@Entity
@Table
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type")
@AllArgsConstructor
@NoArgsConstructor
@Getter
public abstract class A extends PanacheEntityBase {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected long id;

    @Column(name = "type", insertable = false, updatable = false)
    private String type;

    /* ... */
}

@Entity
@DiscriminatorValue("b")
@NoArgsConstructor
@Getter
@ToString
public class B extends A {
    public B(long id, String path, Set<String> metadata, Set<Tag> tags) {
        super(id, "b", metadata, tags);
        this.path = path;
    }

    public B(String path) {
        super(0, "b", new HashSet<>(), new HashSet<>());
        this.path = path;
    }

    @Column(name = "path")
    @Setter
    private String path;
}

@Entity
@DiscriminatorValue("c")
@NoArgsConstructor
@Getter
public class C extends A {
    public C(long id, String name, List<A> collection, Set<String> metadata, Set<Tag> tags) {
        super(id, "c", metadata, tags);
        this.collection = collection;
        this.name = name;
    }

    @Column(name = "name")
    private String name;

    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "c_id")
    @OrderBy("order")
    List<A> collection;
}

Mappers

public interface AMapper {
    default String tagToDto(Tag tag) {
        return tag.getTag();
    }

    default Tag tagFromDto(String tag) {
        return Tag.createIfNotExists(tag);
    }
}

@Mapper()
public interface BMapper extends AMapper {
    @Override
    @Mapping(target = "tags",
            qualifiedByName = "tagToDto")
    BDto toDto(B b);

    @Override
    @Mapping(target = "tags",
            qualifiedByName = "tagToEntity")
    B toEntity(BDto b);
}

@Mapper()
public interface CMapper extends AMapper {
    @Override
    @Mapping(target = "tags",
            qualifiedByName = "tagToDto")
    CDto toDto(C b);

    @Override
    @Mapping(target = "tags",
            qualifiedByName = "tagToEntity")
    C toEntity(CDto b);
}

Pom

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <artifactId>dummy</artifactId>
    <groupId>dummy</groupId>
    <version>0.1.0</version>
    <packaging>pom</packaging>

    <properties>
        <compiler-plugin.version>3.8.1</compiler-plugin.version>
        <maven.compiler.parameters>true</maven.compiler.parameters>

        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <lombok.version>1.18.12</lombok.version>
        <mapstruct.version>1.3.1.Final</mapstruct.version>
    </properties>

    <repositories>
        <repository>
            <id>projectlombok.org</id>
            <url>https://projectlombok.org/edge-releases</url>
        </repository>
    </repositories>

    <dependencies>
        <!-- other stuff -->

        <!-- Tools -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <!-- <scope>provided</scope> -->
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${mapstruct.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>${mapstruct.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${compiler-plugin.version}</version>
                <configuration>
                    <annotationProcessorPaths>
                        <annotationProcessorPath>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${mapstruct.version}</version>
                        </annotationProcessorPath>
                        <annotationProcessorPath>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </annotationProcessorPath>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
like image 602
user3503659 Avatar asked Jun 14 '20 14:06

user3503659


1 Answers

With lombok (1.18.18) and mapstruct (1.4.2.Final) everything worked after I:

  1. added plugin lombok-mapstruct-binding
  2. added lombok-mapstruct-binding to annotationProcessorPaths section of plugin maven-compiler-plugin

links:

  • github example pom.xml: https://github.com/mapstruct/mapstruct-examples/blob/master/mapstruct-lombok/pom.xml
  • from https://mapstruct.org/faq/ : If you are using Lombok 1.18.16 or newer you also need to add lombok-mapstruct-binding in order to make Lombok and MapStruct work together.
like image 86
Filomat Avatar answered Sep 18 '22 12:09

Filomat