2

I have a basic Spring boot app and I am trying to map a list of entities to list of DTOs using Mapstruct (version 1.3.0.Final).

Source:

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.sql.Timestamp;

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder(toBuilder = true)
    @Table(name = "source")
    @Entity(name = "Source")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    public class Source implements Serializable {

        private static final long serialVersionUID = 964150155782995534L;

        @Id
        @JsonIgnore
        @Column(name = "id")
        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SourceSeq")
        @SequenceGenerator(sequenceName = "source_id_seq", allocationSize = 1, name = "SourceSeq")
        private long id;

        @NotNull
        @Size(min = 36, max = 36)
        @Column(name = "uuid", nullable = false, length = 36)
        private String uuid;

        @Column(name = "user_id")
        private Long userId;

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

        @Column(name = "user_org_id")
        private Long userOrgId;

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

        @Column(name = "account_number")
        private Integer accountNumber;

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

        @Column(name = "billing_delay")
        private Integer billingDelay;

        @Column(name = "default_billing_delay")
        private Integer defaultBillingDelay;

        @Column(name = "billing_enabled")
        private Boolean billingEnabled = true;

        @JsonIgnore
        @CreationTimestamp
        @Column(name = "created_date")
        private Timestamp createdDate;

        @JsonIgnore
        @UpdateTimestamp
        @Column(name = "updated_date")
        private Timestamp updatedDate;
    }

Target:

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class Target implements Serializable {

    private static final long serialVersionUID = 8939532280496355293L;

    @ApiModelProperty(hidden = true)
    private String uuid;

    @ApiModelProperty(value = "user ID", example = "123456", dataType = "int64", position = 1)
    private Long userId;

    @ApiModelProperty(value = "username", example = "myUser", position = 2)
    private String username;

    @ApiModelProperty(hidden = true)
    private String firstName;

    @ApiModelProperty(hidden = true)
    private String lastName;

    @ApiModelProperty(value = "user organization ID", example = "71836", dataType = "int64", position = 3)
    private Long userOrgId;

    @ApiModelProperty(value = "user organization name", example = "Org Inc", position = 4)
    private String userOrgName;

    @ApiModelProperty(value = "account number", example = "987654", position = 5)
    private Integer accountNumber;

    @ApiModelProperty(value = "account name", example = "My Mapping Acc", position = 6)
    private String accountName;

    @ApiModelProperty(value = "billing delay (in days)", example = "60", position = 7)
    private Integer billingDelay;

    @ApiModelProperty(value = "default billing delay (in days)", example = "30", position = 8)
    private Integer defaultBillingDelay;

    @ApiModelProperty(value = "is billing enabled?", example = "true", position = 9)
    private Boolean billingEnabled = true;

    @ApiModelProperty(hidden = true)
    private Date createdDate;
}

Mapper:

import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

import java.util.List;

@Mapper
public interface MyMapper {

    MyMapper MAPPER = Mappers.getMapper(MyMapper.class);


    // Target toTarget(Source source);
    // I have tried using this as well but my target mapped list only contains billingEnabled = true for every object in the response list. MapperImpl class also included below. Without toTarget method get a compilation error (also included below)
    // Response: 
        /*[

      {
        "billingEnabled": true
      },
      {
        "billingEnabled": true
      },
      {
        "billingEnabled": true
      }
    ]*/

    List<Target> toTargets(List<Source> sources);
}

MapperImpl:

import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2019-09-16T00:06:14-0700",
    comments = "version: 1.3.0.Final, compiler: javac, environment: Java 1.8.0_202 (Oracle Corporation)"
)
public class MyMapperImpl implements MyMapper {

    @Override
    public Target toTarget(Source source) {
        if ( source == null ) {
            return null;
        }

        Target target = new Target();

        return target;
    }

    @Override
    public List<Target> toTargets(List<Source> sources) {
        if ( sources == null ) {
            return null;
        }

        List<Target> list = new ArrayList<Target>( sources.size() );
        for ( Source source : sources ) {
            list.add( toTarget( source ) );
        }

        return list;
    }
}

Error:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.5.1:compile (default-compile) on project my-project: Compilation failure
[ERROR] /Users/user8812/workspace/my-project-/src/main/java/MyMapper.java:[17,23] Can't map Collection element "Source source" to "Target target". Consider to declare/implement a mapping method: "Target map(Source value)".

I'm looking to have Target list with the same field names mapped without another individual toTarget method as that has worked for me in another project with an older Mapstruct version.

1

3 Answers 3

2

The exception thrown by MapStruct during compilation is telling you how to fix it:

Can't map Collection element "Source source" to "Target target". Consider to declare/implement a mapping method: "Target map(Source value)".

You could even place this method signature inside the same interface you've shown us.

Edit

It seems like the default global configuration for MapStruct has been changed in the application. Try applying this annotation to you "source to target" method inside the interface:

@BeanMapping(ignoreByDefault = false)

Or you can explicitly map their fields with the @Mapping annotation.

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

3 Comments

Are you referring to this? Target toTarget(Source source) I have already tried this (commented part in interface) but this is mapping only one field. (response also included)
@Niv yes, sorry I didn't see that. I updated my answer, can you give it a try?
Didn't work, right?
0

Since the field names and types in Target type and in Source type are the same, wouldn't it be more beneficial and easier to use BeanUtils.copyProperties(source, target)? It is very straightforward and allows stating of ignored fields.

1 Comment

If different fields are added, or if some of their names change, then he would need to change his mapping method. Using a mapping library is way more convenient for this use case.
0

I think declaration should have annotation in MyMapper interface.

@Mapping(source = "sources", target = "targets")
List<Target> toTargets(List<Source> sources);

Reference: https://mapstruct.org/

1 Comment

I've actually tried that but what are you referring to with "targets" here? ERROR] COMPILATION ERROR : [INFO] ------------------------------------------------------------- [ERROR] /Users/user8812/workspace/my-project-/src/main/java/MyMapper.java:[18,5] Unknown property "targets" in result type java.util.List<Target>. Did you mean "empty"?

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.