2

I am learning to use MapStruct.

I have the following custom mapping configuration that maps a List comes from JPA entity to DTO String that way:

@Mapper(componentModel = "spring")
public interface StatusMapper {

    @Mapping(target = "statusCode",
            expression = "java(entity.getStatuses().get(entity.getStatuses().size() - 1).getStatus())")
    @Mapping(target = "details",
            expression = "java(entity.getStatuses().get(entity.getStatuses().size() - 1).getMessage())")
    StatusResponse map(MyEntity entity);
}

My problem with this solution is that the Java code in the expression actually is a string and the IDE (e.g. IntelliJ) is not checking the syntax of this "java" code. Maybe after a refactor this piece of code will not work anymore because I renamed the related field.

And if I add a null check to the expression then this piece of code will be more longer, and longer code can have more typos.

Can I write here somehow a real java code instead of using this Java expression string?

2
  • 1
    "Maybe after a refactor this piece of code will not work anymore because I renamed the related field" write unit tests for that case Commented Jun 9, 2021 at 16:34
  • and of course you can (flushing "expressions" down...): baeldung.com/mapstruct-custom-mapper Commented Jun 9, 2021 at 16:37

1 Answer 1

2

Keep in mind that MapStruct is an annotation processor, and the expression written in expression will be written one to one in the generated Java class. Therefore, doing a refactoring will lead to a compile error.

Having said that there is an alternative solution for this.

@Mapper(componentModel = "spring")
public interface StatusMapper {

    @Mapping(target = "statusCode", source = "statuses", qualifiedByName = "statusesStatus")
    @Mapping(target = "details", source = "statuses", qualifiedByName = "statusesMessage")
    StatusResponse map(MyEntity entity);

    @Named("statusesStatus")
    default String extractStatus(Collection<Status> statuses) {
        return statuses != null && statuses.isEmpty() ? statuses.get(statuses.get() - 1).getStatus() : null;
    }

    @Named("statusesMessage")
    default String extractStatusMessage(Collection<Status> statuses) {
        return statuses != null && !statuses.isEmpty() ?  statuses.get(statuses.get() - 1).getMessage() : null;
    }
}

Doing this will make sure that MapStruct uses the custom methods that you have defined.


Another potentially nice solution (which does not yet exist in MapStruct) and for which there is an open feature request is Allowing indexing for object list.

The potential solution for that can look like:

@Mapper(componentModel = "spring")
public interface StatusMapper {

    @Mapping(target = "statusCode", source = "statuses[-1].status")
    @Mapping(target = "details", source = "statuses[-1].message")
    StatusResponse map(MyEntity entity);
}

If this is something that is interesting for you, I would suggest voting on that issue.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.