I've started using Mapstruct to map JPA entities to DTO's. For basic entities, this works great.
My problem: Some entities have lazy loaded collections, containing additional details, that I don't want always want to fetch and map. As a solution I've added a basic superclass with all fields that are always mapped, and a subclass containing the collections. They both represent the same entity, so they use the same source class.
When I try to create a Mapper containing methods to map to both types from the same source, I get an ambiguous mapping methods error, even though the method signature (at least the return type) is different. Am I going about this the wrong way? Can't I use subclasses for DTO's using the same source?
Edit: In case it matters, I'm using mapstruct-jdk8:1.1.0.Final
Edit 2: The example below was just an example, on the top of my head. When I've actually used the code, it worked. It turns out my issue is with something that wasn't included in the example. It appears that the error occurs when I add a method for mapping a Collection of Tickets. This probably means the issue is not (directly?) related with inheritance. I'm probably missing some configuration, but I'm not sure what to look for.
Simple example:
Ticket entity
public class Ticket {
private long id;
private String title;
private Set<Comment> comments;
// Getters and setters
}
Ticket DTO
public class TicketDTO {
private long id;
private String title;
// Getters and setters
}
Ticket with comments DTO
public class TicketWithCommentsDTO extends TicketDTO {
private List<CommentDTO> comments;
// Getters and setters
}
Ticket Mapper interface
@Mapper(uses= { CommentMapper.class })
public interface TicketMapper {
TicketDTO mapToTicketDTO(Ticket ticket);
List<TicketDTO> mapToTicketDTOList(Collection<Ticket> tickets); // Adding this method or the last method causes the error
TicketWithCommentsDTO mapToTicketWithCommentsDTO(Ticket ticket);
List<TicketWithCommentsDTO> MapToTicketWithCommentDTOList(Collection<Ticket> tickets);
}
Comment Mapper interface
@Mapper
public interface CommentMapper {
CommentDTO toCommentDTO(Comment comment);
List<CommentDTO> toCommentDTOList(Collection<Comment> comments);
}
The error thrown:
Ambiguous mapping methods found for mapping collection element to
dto.TicketDTO: dto.TicketDTO mapToTicketDTO(model.Ticket ticket),
dto.TicketWithCommentsDTO mapToTicketWithCommentsDTO(model.Ticket ticket).
We can ignore unmapped properties in several mappers by setting the unmappedTargetPolicy via @MapperConfig to share a setting across several mappers. We should note that this is a simple example showing the minimal usage of @MapperConfig, which might not seem much better than setting the policy on each mapper.
Java Prime Pack Using Mapstruct we can pass the default value in case source property is null using defaultValue attribute of @Mapping annotation.
MapStruct is a code generator that greatly simplifies the implementation of mappings between Java bean types based on a convention over configuration approach. The generated mapping code uses plain method invocations and thus is fast, type-safe and easy to understand.
Well, this turned out to be a simple fix, it was indeed a missing configuration issue. What was missing was the @IterableMapping
annotation.
Once I set the elementTargetType
to the correct types, everything worked as expected.
The correct Mapper code
@Mapper(uses = { CommentMapper.class })
public interface TicketMapper {
TicketDTO mapToTicketDTO(Ticket ticket);
@IterableMapping(elementTargetType = TicketDTO.class)
List<TicketDTO> mapToTicketDTOList(Collection<Ticket> tickets);
TicketWithCommentsDTO mapToTicketWithCommentsDTO(Ticket ticket);
@IterableMapping(elementTargetType = TicketWithCommentsDTO.class)
List<TicketWithCommentsDTO> mapToTicketWithCommentDTOList(Collection<Ticket> tickets);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With