7

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>
user3503659
  • 95
  • 1
  • 7
  • 2
    The `lombok-mapstruct-binding` should be in the `annotationProcessorPaths` as well. – Filip Jun 14 '20 at 17:33
  • @Filip I updated the code above. I added `lombok-mapstruct-binding` to the `annotationProcessorPaths` as you suggested, but sadly I still get the same error. Maybe another question: Do I really have to go with the edge version of lombok, or should it work with version 1.18.12 as well? – user3503659 Jun 14 '20 at 18:45
  • It should work with 1.18.12 as well. – Filip Jun 15 '20 at 07:27
  • 2
    @Filip I found a solution, that kinda confuses me. When changing the order of the lombok and mapstruct annotationProcessorPaths it works. So having mapstruct first in the list and lombok second. I then also tested it with the `edge-SNAPSHOT` version and the ordering of `lombok-mapstruct-binding` did not change anything, but lombok had to be after mapstruct for it to work. A final thing I tested on accident was to switch the order back and not clearing (`mvn clear`) and the compile worked. So I guess there is something with the build classes in the target folder. – user3503659 Jun 16 '20 at 17:55
  • yes, you can also take a look at the accepted answer here https://stackoverflow.com/questions/65955000/how-to-instruct-mapstruct-to-use-lombok-builder it's also the official mapstruct documentation https://mapstruct.org/documentation/stable/reference/html/#_set_up – Joand Jan 31 '22 at 10:06

1 Answers1

2

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:

Filomat
  • 753
  • 1
  • 11
  • 16