Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
    Mike Oprea
    @Sorin-M-Oprea

    Hello, coming back to my previous question, I am now faced with another issue.

    I have my mapper set up:

    @Mapper(componentModel = "spring", uses = {TestStepEntityService.class, PageAttributeMapper.class})
    public interface TestStepMapper {
    
        @Mapping(target = "pageAttribute", source = "input.pageAttributeId")
        TestStepEntity inputToEntity(TestStepInputDTO input);
    
    }

    I am trying to map the ID to the actual object and I'm getting java: Can't map property "Long pageAttributeId" to "PageAttributeEntity pageAttribute". Consider to declare/implement a mapping method: "PageAttributeEntity map(Long value)".

    Thing is that method is declared in my PageAttributeEntityService.class :

    @Service(value = "PageAttributeEntityService")
    @Builder
    @javax.transaction.Transactional
    @NoArgsConstructor
    @AllArgsConstructor
    public class PageAttributeEntityService {
    
        @Autowired
        private PageAttributeRepository repository;
    
        @Named("FindPageAttributeById")
        public PageAttributeEntity getEntityById(Long id) {
            return repository.findById(id).orElseThrow(() -> new ApiRequestException("Could not find entity with ID: " + id));
        }
    ...

    Thing is even if I use @Mapping(target = "pageAttribute", source = "input.pageAttributeId", qualifiedByName = "FindPageAttributeById") or @Mapping(target = "pageAttribute", source = "input.pageAttributeId", qualifiedByName = {"PageAttributeEntityService", "FindPageAttributeById"}) to specify the exact location where the method is at, I'm still getting java: Can't map property "Long pageAttributeId" to "PageAttributeEntity pageAttribute". Consider to declare/implement a mapping method: "PageAttributeEntity map(Long value)".

    What am I doing wrong? There are no other methods that return a PageAttributeEntity type and accepts an argument a Long value.

    2 replies
    Carlos Bouzón García
    @bougar
    Hello all,
    I am using mapstruct to map between DTOs and model enties. One of the entity contains a list of child objects (Animals which should be a dog, cat, elephant, ...). At this moment, what is the best way to implement Downcast Mapping in mapstruct? I have found one solution in the following link: https://stackoverflow.com/questions/39773923/mapstruct-generic-map-and-map-combined-list-of-children-objects. This solution works but in my opinion is a little dirty. Maybe, do you have a better proposal? I am open to all opinions and ideas. Thanks for your help in advance :)
    1 reply
    Mike Oprea
    @Sorin-M-Oprea

    Hello, coming with yet another question. I have 2 entities which have a Many-To-Many relationship using a composite key and intermediate entity. Below is the code for them:

    public class PackageEntity extends BaseEntity {
    
        // Other fields
    
        @Nullable
        @JsonIgnore
        @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "testPackage", orphanRemoval = true)
        private List<PackageTemplateEntity> testTemplateList = new ArrayList<>();
    
        // Getters, Setters and Constructors
    }

    The above is considered the parent entity. Now comes the child entity:

    public class TestTemplateEntity extends BaseEntity {
    
        // Other fields
    
        @Nullable
        @JsonIgnore
        @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "testPackage", orphanRemoval = true)
        private List<PackageTemplateEntity> testPackageList = new ArrayList<>();
    
        // Getters, Setters and Constructors
    }

    The Many-To-Many is handled by this PackageTemplateEntity which is in-between the two with a composite key. So, composite key is:

    public class PackageTemplateKey implements Serializable {
    
        @Column(name = "Package_Id")
        Long packageId;
    
        @Column(name = "Template_Id")
        Long templateId;
    }

    And the intermediary entity is:

    @Table(name = "Package_Template", uniqueConstraints = @UniqueConstraint(columnNames = {"Package_Id", "Template_Id"}))
    public class PackageTemplateEntity {
    
        @JsonIgnore
        @EmbeddedId
        private PackageTemplateKey id;
    
        @JsonIgnore
        @MapsId("packageId")
        @JoinColumn(name = "Package_Id")
        @ManyToOne(optional = false, targetEntity = PackageEntity.class)
        private PackageEntity testPackage;
    
        @MapsId("templateId")
        @JoinColumn(name = "Template_Id")
        @ManyToOne(optional = false, targetEntity = TestTemplateEntity.class)
        private TestTemplateEntity testTemplate;
    
        @CreationTimestamp
        @Column(name = "Created_At", updatable = false)
        private ZonedDateTime createdDate;
    
        @ConstructorParameters({"id", "testPackage", "testTemplate"})
        public PackageTemplateEntity(PackageTemplateKey id, PackageEntity testPackage, TestTemplateEntity testTemplate) {
            this.setId(id);
            this.setTestPackage(testPackage);
            this.setTestTemplate(testTemplate);
        }
    }

    I've omitted Getters, Setters and Constructors but they are there.

    So, I have PackageEntity, TestTemplateEntity. They both have respective DTOs. Both PackageTemplateEntity and the composite Key PackageTemplateKey don't have DTOs.

    Is there a way for MapStruct to map things in a way that:

    • Given an output of PackageEntity, the field with name List<PackageTemplateEntity> testPackageList would show a List<TestTemplateEntity>
    • Given a PackageDTO, the above field List<PackageTemplateEntity> testPackageList would map to a List<TestTemplateDTO>

    At the moment, I am mapping it like this in my PackageDTO and it translates correctly but I would prefer to do it in MapStruct:

    Optional.ofNullable(packageEntity.getTestTemplateList()).ifPresent(testTemplateList -> { // testTemplateList is of type <PackageTemplateEntity>
        var testTemplateDtoList = new ArrayList<TestTemplateDTO>();
        testTemplateList.forEach(templatePackage -> testTemplateDtoList.add(new TestTemplateDTO(templatePackage.getTestTemplate())));
        this.setTestTemplateList(testTemplateDtoList);
    });
    4 replies
    aaysenur
    @aaysenur
    Hello all, I am new with Mapstruct. Tyring to add generated sources to my source code. I am using maven, I tried to change output directory with build-helper-maven-plugin but build failed. Could somebody help me please? Thanks for help in advance
    1 reply
    deall0c
    @deall0c:matrix.org
    [m]
    I am trying to run the test suite for mapstruct (master), but the test suite keeps failing for the integration tests. Any suggestions?
    [INFO] Reactor Summary for itest-supertypegeneration-aggregator 1.0.0:
    [INFO] 
    [INFO] itest-supertypegeneration-aggregator ............... SUCCESS [  0.437 s]
    [INFO] itest-supertypegeneration-generator ................ FAILURE [  0.025 s]
    [INFO] itest-supertypegeneration-usage .................... SKIPPED
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD FAILURE
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  0.493 s
    [INFO] Finished at: 2021-07-06T18:35:40-05:00
    [INFO] ------------------------------------------------------------------------
    [ERROR] Failed to execute goal on project itest-supertypegeneration-generator: Could not resolve dependencies for project org.mapstruct.itest:itest-supertypegeneration-generator:jar:1.0.0: Could not find artifact org.mapstruct:mapstruct-processor:jar:1.5.0-SNAPSHOT -> [Help 1]
    org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal on project itest-supertypegeneration-generator: Could not resolve dependencies for project org.mapstruct.itest:itest-supertypegeneration-generator:jar:1.0.0: Could not find artifact org.mapstruct:mapstruct-processor:jar:1.5.0-SNAPSHOT
    I am using mvn clean test -DskipDistribution=true
    deall0c
    @deall0c:matrix.org
    [m]
    ^ignore this, looks like installing the snapshot artifact is a pre-requisite, switching to mvn clean install fixed it
    Filip Hrisafov
    @filiphr
    Good spot @deall0c:matrix.org, the way the integration tests work, they require having the processor installed in the local repo.
    jeonjhong
    @jeonjhong
    hi, i'm trying to use mapstruct with spring jpa and mybatis.
    i figured out mapperImpl class was generated with @Mapper (org.mapstruct.mapper). however when i try to test with Junit5 , org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type exception occurred. how can i fix it?
    6 replies
    Zegveld
    @Zegveld
    Hey, I'm working on the 131 issue. Still need to check if my change also supports abstract class/interfaces as parent classes (probably not yet), but I've got it working already for parent/child combination where the parents can also be instantiated.
    9 replies
    hazemkmammu
    @hazemkmammu

    Hi everyone!

    Is there an alternative to creating a method signature in the mapper interface for each set of classes we want to map?

    With Dozer, you can call org.dozer.DozerBeanMapper.map(Object, Class<T>) without declaring a method representing the conversion. I realize that Dozer users reflection and mapstruct uses code generation, but still wondering if there is any way we can avoid declaring these methods for each new class we create.

    I am looking for something like

    @MapTo(CarDto.class)
    class CarEntity{
    ...
    }
    
    @MapTo(Car.class)
    class CarDto{
    ...
    }

    If we could somehow get a handle on the mapstruct generation, we could write our own annotation processors and generate the Mapper interfaces based on our custom annotations like above.

    I couldn't find any related answers on google. I apologize if I missed something. Thanks to you all in advance for your attention. Cheers!

    6 replies
    hazemkmammu
    @hazemkmammu

    Do the current versions of lombok, mapstruct and lombok-mapstruct-binding work out of the box?

    With

    plugins {
        id 'org.springframework.boot' version '2.5.2'
        id 'io.spring.dependency-management' version '1.0.11.RELEASE'
        id 'java'
    }
    
    dependencies {
        implementation 'org.springframework.boot:spring-boot-devtools'
        implementation 'org.springframework.boot:spring-boot-starter-actuator'
        implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
        implementation 'org.springframework.boot:spring-boot-starter-data-rest'
        implementation 'org.springframework.boot:spring-boot-starter-security'
        implementation 'org.springframework.boot:spring-boot-starter-web'
        implementation 'org.mapstruct:mapstruct:1.4.2.Final'
        compileOnly 'org.projectlombok:lombok'
        runtimeOnly 'com.h2database:h2'
        annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
        annotationProcessor 'org.projectlombok:lombok:1.18.20'
        annotationProcessor 'org.projectlombok:lombok-mapstruct-binding:0.2.0'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
        testImplementation 'org.springframework.security:spring-security-test'
    }

    I am getting a java.lang.ClassNotFoundException: Cannot find implementation for com.example.UserMapper.

    Spring Tool Suite 4 
    Version: 4.11.0.RELEASE
    Build Id: 202106180608
    
    Eclipse Platform
    Version: 4.20.0.v20210611-1600
    Build id: I20210611-1600

    What am I doing wrong?

    14 replies
    Arthur Gregório
    @arthurgregorio

    Hi guys, quick question (i think)

    I'm using kotlin and having some trouble to convert a list of objects to a new list of strings, those strings come from a property of the source bean. I already tried something like this:

    @Mapper(config = MappingConfiguration::class)
    interface UserToUserViewConverter : Converter<User, UserView> {
    
        @Mappings(
            Mapping(source = "externalId", target = "id"),
            Mapping(source = "grants", target = "roles")
        )
        override fun convert(user: User): UserView?
    
        fun grantsToRoles(grants: List<Grant>): List<String> {
            return grants
                .map { it.authority }
                .map { it.name }
                .toCollection(arrayListOf())
        }
    }

    But this gives me an error when mapstruct try to generate the classes:

    UserToUserViewConverter.java:16: error: Can't map collection element "Grant" to "String ". Consider to declare/i
    mplement a mapping method: "String map(Grant value)".
    [ERROR]     public abstract java.util.List<java.lang.String> grantsToRoles(@org.jetbrains.annotations.NotNull

    If i change the code to use a new method as suggested in the message:

    @Mapper(config = MappingConfiguration::class)
    interface UserToUserViewConverter : Converter<User, UserView> {
    
        @Mappings(
            Mapping(source = "externalId", target = "id"),
            Mapping(source = "grants", target = "roles")
        )
        override fun convert(user: User): UserView?
    
        fun grantsToRoles(grants: List<Grant>): List<String> {
            return grants
                .map { it.authority }
                .map { it.name }
                .toCollection(arrayListOf())
        }
    
        fun grantToString(grant: Grant): String {
            return grant.authority.name
        }
    }

    A weird code is generated:

    @Override
    public List<String> grantsToRoles(List<Grant> grants) {
        if ( grants == null ) {
            return null;
        }
    
        List<String> list = new ArrayList<String>( grants.size() );
        for ( Grant grant : grants ) {
            list.add( grantToString( grant ) );
        }
    
        return list;
    }
    
    @Override
    public String grantToString(Grant grant) {
        if ( grant == null ) {
            return null;
        }
    
        String string = new String();
    
        return string;
    }

    Accoding to the documentation the second code is ok but i can't understand why the method to convert a grant to string is returning a new string and not the code i wrote there..

    2 replies
    merlyn42
    @merlyn42:matrix.org
    [m]
    Hello, I'm wondering what the best way to translate between two strings is? For example I have two systems that are communicating province/state codes as strings, one system uses "QC" as Quebec and another uses "PQ", other than that the systems agree on their codes. I can manually create a default implementation in a Mapper interface and use the @Named annotation to force that default implementation to be used for state/province fields, but I was hoping to use something like @ValueMapping for enums. I know ideally these values would be enums but I don't have control over that at the moment.
    3 replies
    merlyn42
    @merlyn42:matrix.org
    [m]
    Yes that's a good idea but my data classes are currently completely generated from schemas and I can't think of an elegant way to insert the intermediate enum types before the mapping
    1 reply
    I'll look into that. It's good to know I'm not missing an obvious way to do it as is anyway. Thanks!
    Rafał Podgórski
    @ravpod
    I have a problem how can I turn on this plugin? I installed it and I have no completion of target, source or something
    1 reply
    In IntelliJ Plugin is enabled
    I write about MapStruct Support Plugin for Intellij
    jeonjhong
    @jeonjhong
    hello, is there any way to generate *Impl.java file automatically when .java is saved such as lombok?
    i'm using VSCODE. compileJava is the only way to achive it?
    5 replies
    Shaoyong King
    @collery
    hello ,ask for help. when i defined a convertConfig to use some basic converter. example interger to Boolean . componentMode is spring 。 in my project ,it‘s work .but when anothor projects use. it can not find the basic converter bean . because it do not impl my basic converter .
    this the code .
    Shaoyong King
    @collery

    // defualt config
    @MapperConfig(componentModel = "spring", uses = {BaseEnumConverter.class, CommonConverter.class},
    nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, unmappedTargetPolicy = ReportingPolicy.IGNORE)
    public interface ConverterConfig {
    }

    //basic common convert
    @Mapper(componentModel = "spring", uses = {}, nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
    public interface CommonConverter {
    DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm");
    DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy.MM.dd");

    /**
     * 将SQL Timestamp转换成毫秒级时间戳
     *
     * @param timestamp SQL Timestamp
     * @return 毫秒级时间戳
     */
    default Long sqlTimestamp2MilliSecLong(Timestamp timestamp) {
        return timestamp.getTime();
    }
    
    .....

    }

    // example
    @Mapper(config = ConverterConfig.class)
    public interface SaaSQueryConverter {
    ClientEntryPageListRequest toPageList(SaaSQuery response);
    }

    // impl
    public class SaaSQueryConverterImpl implements SaaSQueryConverter {

    // it‘s auto generator, but i can’t find spring bean ,because CommonConverter it's not my code
    @Autowired
    private CommonConverter commonConverter;

    .....

    }

    Shaoyong King
    @collery
    image.png
    Dennis Melzer
    @MelleD

    Hello everybody,
    I'm trying to convert a set to set with a wrapper object around it. I have already tested with qualiedBy but somehow I always get the same error message.

    
    @Mapper( uses = {  SetMapper.class }, componentModel = MapperConstant.COMPONENT_MODEL,
             unmappedTargetPolicy = ReportingPolicy.IGNORE )
    public abstract class LabelMapper {
    
       abstract Set<Label> fromStringLabels( final JsonNullable<Set<String>> labels );
    
       abstract Set<Label> to( Set<String> labels );
    
       abstract Label fromNameLabel( final String name );
    }
    
    @Qualifier
    @Target( ElementType.METHOD )
    @Retention( RetentionPolicy.SOURCE )
    public @interface JsonNullableSet {
    }
    
    @Mapper( componentModel = MapperConstant.COMPONENT_MODEL )
    public class SetMapper {
    
       @JsonNullableSet
       public <T> Set<T> fromJsonNullableSet( final JsonNullable<Set<T>> nullable ) {
          if ( nullable.isPresent() ) {
             final Set<T> value = nullable.get();
             return ObjectUtils.isEmpty( value ) ? Collections.emptySet() : value;
          }
          return Collections.emptySet();
       }

    error message is: Can't generate mapping method from non-iterable type to iterable type.

    This will work:

       abstract Label fromNameLabel( final String name );
    
       Set<Label> fromStringLabels( final JsonNullable<Set<String>> labels ) {
          return to( setMapper.fromJsonNullableSet( labels ) );
       }
    
       abstract Set<Label> to( Set<String> labels );
    77 replies
    Filip Hrisafov
    @filiphr
    I'd like to let everyone know that we have released our first instalment of the upcoming 1.5 MapStruct release. You can read more about it https://twitter.com/filiphr/status/1417122085621600257
    garage_logician
    @garage_logician:matrix.org
    [m]
    I am trying to map a set of Characteristics to a Product bean. The values of the Characteristics come from several different beans within a IncoimingDataModel bean. How can I use MapStruct to effectively map the set of Characteristics from the IncomingDataModel into a Product bean? I am using Spring boot as well if that matters
    ronMilne
    @ronMilne
    Using 1.5.0.beat1 Map<String, Object> complains when trying to map to string property with Consider to declare/implement a mapping method: "String map(Object value)". wouldn't it be possible for the mapper to determine whether the map item is assignable such as checking instanceof?
    jcbodnar
    @jcbodnar

    I have a source class that has a list of an abstract type (List<Widget> widgets). I want to break the list up for the target and call setters that are specific for the actual widget implementation (setNameWidget(), setAddressWidget(), etc).

    What is the best way to do this?

    1 reply
    ChangKwon Jeong
    @spearkkk
    Hello, guys !
    I have a question about mapstuct to use from LocalDateTime(java.date) to String.
    I know that I can assign dateFormat value in Mapping annotation.
    But I want to set dateFormat in globally.
    Could I set dateFormat and apply it all of mappers?
    8 replies
    Dennis Melzer
    @MelleD

    Hi, everyone,

    is there a possibility to filter out the null values ​​of every list / set / collection before mapping? That would be very helpful, especially with sets.

    jcbodnar
    @jcbodnar

    I have a mapper with two methods:

    DonationFormLayoutEntity apiToEntity(DonationForm source, @Context WidgetMapper widgetMapper);
    
    @InheritInverseConfiguration
    DonationForm entityToApi(DonationFormLayoutEntity source, @Context WidgetMapper widgetMapper);

    I have three @AfterMapping methods. The first two should apply to apiToEntity() and the last to entityToApi():

    @AfterMapping
    default void filterEmptyNotificationEmails(@MappingTarget DonationFormLayoutEntity donationFormLayoutEntity) {...)
    
    @AfterMapping
    default void mapWidgetsToWidgetEntities(DonationForm source, @MappingTarget DonationFormLayoutEntity target, @Context WidgetMapper widgetMapper) {...}
    
    @AfterMapping
    default void mapWidgetEntitiesToWidgets(DonationFormLayoutEntity source, @MappingTarget DonationForm target, @Context WidgetMapper widgetMapper) {...}

    When I build my project the first two @AfterMapping methods are included in the code generated for apiToEntity() but the last is not included in the code for entityToApi().

    What am I doing wrong?

    6 replies
    jcbodnar
    @jcbodnar

    Alright, I have an annotation:

    @Retention(RetentionPolicy.CLASS)
    @Mapping(target = "formId", ignore = true)
    @Mapping(target = "id", expression = "java(java.util.UUID.randomUUID())")
    @Mapping(source = "order", target = "widgetOrder")
    public @interface ToWidgetEntity {
    }

    When I use it on a mapping method:

    @ToWidgetEntity
    WidgetEntity apiToEntity(Widget source);

    IntelliJ gives me a warning about Unmapped target properties: id, widgetOrder,

    I'm using the latest version of the plugin.

    4 replies
    Tinashe Walter Zulu
    @tinzulu

    Hi team i'm getting this error EmployeeMapper.java:[12,9] The type of parameter "employee" has no property named "office.name".
    im using java 8, IDE is Eclipse with the mapstruct 1.4.2.Final my classess are as follows.
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class EmployeeResponse {
    private int id;
    private String name;
    private String surname;
    private String workemail;
    private String personalemail;
    private String address;
    private String extension;
    private String cellnumber;
    private String officenumber;
    private String gender;
    private String department;
    private String title;
    private String designation;
    private String imageUrl;
    private Date dob;
    private boolean status;
    private String supervisorName;
    private String officeName;
    }
    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    @Entity
    public class Employee implements Serializable{
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
    private String surname;
    private String workemail;
    private String personalemail;
    private String address;
    private String extension;
    private String cellnumber;
    private String officenumber;
    private String gender;
    private String department;
    private String title;
    private String designation;
    private String imageUrl;
    private Date dob;
    private boolean status;
    @ManyToOne
    @JoinColumn(name = "office_id", referencedColumnName = "id")
    private Office office;
    @ManyToOne
    @JoinColumn(name = "supervisor_id", referencedColumnName = "id")
    private Employee supervisor;
    }
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Entity
    public class Office {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
    private String district;
    private String Province;
    private boolean status;
    }
    I'm trying to map the Employee and EmployeeResponse as given above.
    @Mapper(componentModel = "spring")
    public interface EmployeeMapper {

    @Mapping(source = "employee.office.name", target = "officeName")
    EmployeeResponse mapToResponse(Employee employee);

    }

    3 replies
    Robert Pospisil
    @robp94
    Is it possible to target a parmeter inside a list? I want to map the bool parameter from the top object in the nested list object like this:
    interface Mapper{
        @Mapping(target = "listItemDtos.bool", source = "bool")//listItemDtos.bool <- does not work
        ItemDto getDto(Item item);
    }
    
    public class Item {
    
        public Boolean bool;
        public List<ListItem> listItems;
    }
    
    public class ListItem {
        public String something;
    }
    
    public class ItemDto {
        public List<ListItemDto> listItemDtos;
    }
    
    public class ListItemDto {
        public Boolean bool;
        public String something;
    }
    1 reply
    Mike Oprea
    @Sorin-M-Oprea

    Hello, I am in a weird situation. I have the following entity:

    @Entity
    @Getter
    @Setter
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @Table(name = "Building_Block")
    @Where(clause = "is_active = true")
    public class BuildingBlockEntity extends BaseEntity {
    
        @NotNull
        @Column(name = "Building_Block_Type", unique = true)
        private String buildingBlockType;
    
        @Nullable
        @ToString.Exclude
        @JoinColumn(name = "Building_Block_Id")
        @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
        private List<TestStepEntity> testStepList = new ArrayList<>();
    
        @ManyToOne
        @JsonIgnore
        private TestTemplateEntity testTemplate;
    }

    My BaseEntity is an abstract class:

    @Getter
    @Setter
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @RequiredArgsConstructor
    @MappedSuperclass
    public abstract class BaseEntity {
    
        @Id
        @Min(100)
        @Max(Integer.MAX_VALUE)
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        protected Long id;
    
        @CreationTimestamp
        @Column(name = "Created_At", updatable = false)
        protected ZonedDateTime createdDate;
    
        @UpdateTimestamp
        @Column(name = "Updated_At")
        protected ZonedDateTime updatedDate;
    
        @Column(name = "Is_Active")
        protected Boolean active = true;
    }

    I have an entity service class which map the respective repository. In there, I have a method for saving an entity:

        public BuildingBlockEntity saveEntity(BuildingBlockEntity entity) {
            return repository.save(entity);
        }

    I also have a dto service class where I am calling the above method to save the entity. The below method is what handles the updating of the entity:

        public BuildingBlockOutputFilterChildrenDTO updateObject(Long id, BuildingBlockInputDTO input) {
            return mapper.entityToOutputFilterChildren(entityService.saveEntity(mapper.updateEntityFromInput(input, entityService.getEntityReferenceById(id))));
        }

    The respective mappers for the above are these:

        @Mapping(target = "active", ignore = true)
        @Mapping(target = "createdDate", ignore = true)
        @Mapping(target = "updatedDate", ignore = true)
        @Mapping(target = "testTemplate", ignore = true)
        @Mapping(target = "id", source = "input.buildingBlockId", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
        @Mapping(target = "buildingBlockType", source = "input.name", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
        @Mapping(target = "testStepList", source = "input.testStepList", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
        BuildingBlockEntity inputToEntity(BuildingBlockInputDTO input);
    
        @Mapping(target = "name", source = "entity.buildingBlockType", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
        BuildingBlockOutputFilterChildrenDTO entityToOutputFilterChildren(BuildingBlockEntity entity);
    
        @Mapping(target = "active", ignore = true)
        @Mapping(target = "createdDate", ignore = true)
        @Mapping(target = "updatedDate", ignore = true)
        @Mapping(target = "testTemplate", ignore = true)
        @Mapping(target = "id", source = "input.buildingBlockId", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
        @Mapping(target = "buildingBlockType", source = "input.name", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
        BuildingBlockEntity updateEntityFromInput(BuildingBlockInputDTO input, @MappingTarget BuildingBlockEntity entity);

    --- This is part 1

    Mike Oprea
    @Sorin-M-Oprea

    Now, let's dissect that method from the dto service class:

    return mapper.entityToOutputFilterChildren(entityService.saveEntity(mapper.updateEntityFromInput(input, entityService.getEntityReferenceById(id))));

    mapper.entityToOutputFilterChildren() - This is supposed to convert the resulting saved entity class to an output object.
    entityService.saveEntity() - This calls the entity service to save the entity with the object in the argument. The method used to save the entity is the built-in method in the JpaRepository interface.
    (mapper.updateEntityFromInput(input, entityService.getEntityReferenceById(id)) - This is supposed to update an existing instance of the entity with the given ID.

    Now, the code here should do:

    1. Grab the entity with the given ID
    2. Update the entity instance with the input object
    3. Save the updated entity
    4. Convert the resulting saved entity to an output object
    5. Return that object

    Problem is, I get the following error message:

    2021-08-16 18:02:15.714 ERROR 27380 --- [nio-8081-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper   : Invalid column name 'true'.
    2021-08-16 18:02:15.736 ERROR 27380 --- [nio-8081-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not execute statement] with root cause

    This happens on the save method (when the entity is supposed to be saved) and to be honest, there is no column with the name true in any of my entities.

    If I change my dto service method and make it like this (basically not use MapStruct) everything works exactly as intended:

        public BuildingBlockOutputFilterChildrenDTO updateObject(Long id, BuildingBlockInputDTO input) {
            var x = entityService.getEntityReferenceById(id);
            x.setBuildingBlockType(input.getName());
            var y = entityService.saveEntity(x);
            return mapper.entityToOutputFilterChildren(y);
        }

    The code that MapStruct generates is this one:

        @Override
        public BuildingBlockEntity updateEntityFromInput(BuildingBlockInputDTO input, BuildingBlockEntity entity) {
            if ( input == null ) {
                return null;
            }
    
            if ( input.getBuildingBlockId() != null ) {
                entity.setId( input.getBuildingBlockId() );
            }
            if ( input.getName() != null ) {
                entity.setBuildingBlockType( input.getName() );
    
            else {
                entity.setBuildingBlockType( "" );
            }
            if ( input.getTestStepList() != null ) {
                if ( entity.getTestStepList() == null ) {
                    entity.setTestStepList( new ArrayList<TestStepEntity>() );
                }
                testStepMapper.updateEntityListFromInputList( input.getTestStepList(), entity.getTestStepList() );
            }
            else {
                entity.setTestStepList( null );
            }
    
            return entity;
        }

    I don't see any issues with the code that MapStruct generated but I am clueless as to why I am getting that SQL statement error message. If I print the objects, this is what I get:

    Input: BuildingBlockInputDTO(buildingBlockId=null, name=aaaaabbbwwwwqqq, testStepList=null) -> Has new name
    Entity: BuildingBlockEntity{id=44, createdDate=2021-08-16T16:40:36.204929700+02:00[Europe/Berlin], updatedDate=2021-08-16T17:37:02.228008200+02:00[Europe/Berlin], active=true, buildingBlockType='aaaaabbbwwwwqqq', testStepList=null, testTemplate=null} -> Field buildingBlockType gets filled correctly with new name.

    Now what am I doing wrong here? I am simply trying to make a PUT request to update the name of my object and also cover cases where I would like to update other fields, not just the name.

    PS: I am using Spring Boot with JPA.

    1 reply
    crgardner
    @crgardner
    Can someone give an example of creating a builder with a factory, as mentioned here: https://mapstruct.org/documentation/dev/reference/html/#mapping-with-builders : "The Object factories are also considered for the builder type. E.g. If an object factory exists for our PersonBuilder then this factory would be used instead of the builder creation method." I tried to create an example of a Builder that lives outside the Car object. I created a standalone factory for that builder. I then created a CarDto to Car mapper that "@Mapper uses that BuilderFactory." Is that possible? Otherwise, what does the text I cited mean?
    32 replies
    Jason Wang
    @jasondashwang

    I am a bit confused looking at the documentation on how to use a custom transformation strategy for enums. I am doing this in Kotlin, but a Java solution is fine too.

    I am using 14.2

    I have this

    package org.mapstruct.custom.spi
    import org.mapstruct.ap.spi.EnumTransformationStrategy
    
    class StripLowerCaseEnumTransformationStrategy : EnumTransformationStrategy {
        companion object {
            const val NAME = "stripLowerCase"
        }
    
        override fun getStrategyName(): String {
            return NAME
        }
    
        override fun transform(value: String, configuration: String): String {
            return value.removePrefix(configuration).toLowerCase()
        }
    }

    But when I try to use it within my EnumMappers, it says it cannot be found. What step am I missing?

    @Mapper
    interface EnumMapper {
      @ValueMappings(
            ValueMapping(source = "UNRECOGNIZED", target = MappingConstants.NULL),
            ValueMapping(source = "PERSON_STATUS_INVALID", target = MappingConstants.NULL)
        )
      @EnumMapping(
            nameTransformationStrategy = StripLowerCaseEnumTransformationStrategy.NAME,
            configuration = "PERSON_STATUS_"
        )
      fun fromProto(status: PersonStatusProto): PersonStatus
    
      @InheritInverseConfiguration
      fun toProto(status: PersonStatus): PersonStatusProto
    }

    Here is the error: error: There is no registered EnumTransformationStrategy for 'stripLowerCase'. Registered strategies are: prefix, stripPrefix, stripSuffix, suffix.

    I also have created the file resources/META-INF/services/org.mapstruct.ap.spi.EnumTransformationStrategy with the value org.mapstruct.custom.spi.StripLowerCaseEnumTransformationStrategy.

    8 replies
    Jason Wang
    @jasondashwang
    Screen Shot 2021-08-27 at 12.02.40 AM.png
    Jason Wang
    @jasondashwang
    Screen Shot 2021-08-27 at 12.13.47 PM.png
    load("@brex_rules_kotlin//:def.bzl", "kt_jvm_library")
    
    kt_jvm_library(
        name = "brex_mapstruct",
        exported_plugins = [
            ":brex_mapstruct_plugin"
        ],
        srcs = glob(["src/main/kotlin/brex/mapstruct/*.kt"]),
        visibility = ["//visibility:public"],
        deps = [
            "//libraries/kotlin/mapstruct:mapstruct_plugin",
        ],
    )
    
    java_plugin(
        name = "brex_mapstruct_plugin",
        generates_api = True,
        processor_class = "org.mapstruct.ap.MappingProcessor",
        visibility = ["//visibility:public"],
        deps = [
            "@maven//:org_mapstruct_mapstruct_processor",
        ],
    )
    Dennis Melzer
    @MelleD

    Hey together,

    I am currently stuck with a mapping and do not know of a nice solution.

    The mapper looks like:

       @Mapping( target = "items", source = "foos" )
       FooResponse map( List<Foo> foos );

    I get always the error: Can't generate mapping method from iterable type to non-iterable type.

    If I use someting like this, the generated code works, but i have dummy property.

       @Mapping( target = "items", source = "foos" )
       FooResponse map( Integer dummy, List<Foo> foos );

    The generated code should look like this:

    public class FooMapperImpl {
    
       public FooResponse map( List<Foo> foos ){
          FooResponse fooResponse = new FooResponse();
    
          if ( namespaces != null ) {
             fooResponse.setItems( namespaceWhitelistListToNamespaceDtoList( namespaces ) );
          }
       }
    
       protected FooDto namespaceWhitelistToNamespaceDto(Foo foo) {
          if ( namespaceWhitelist == null ) {
             return null;
          }
    
          FooDto fooDto = new FooDto();
    
          fooDto.setName( foo.getName() );
    
          return fooDto;
       }
    
    }

    Currenlty there is a unnecessary property in the generated code
    Anybody an idea?

    7 replies
    Rawi01
    @Rawi01

    In my current project we use a generic wrapper class (Field) and added a mapper to unwrap the actual value. This works fine. If the unwraped type is not equal to the target type mapstruct uses a built-in conversion but without an additional null check. This can lead to NPE or like in the example below to strings filled with "null". Is there some way to tell mapstruct to add null checks? I know that I can create null-safe custom mappers but thats a lot of work.

    @Mapper
    public interface TestMapper {
        Target mapSourceToTarget(Source source);
    
        default <T> T mapField(Field<T> field) {
            Integer.valueOf(null)
            return field == null ? null : field.getValue();
        }
    
        static class Field<T> {
            @Getter
            private T value;
        }
    
        @Data
        static class Source {
            private Field<Integer> value;
        }
    
        @Data
        static class Target {
            private String value;
        }
    }
    public class TestMapperImpl implements TestMapper {
    
        @Override
        public Target mapSourceToTarget(Source source) {
            if ( source == null ) {
                return null;
            }
    
            Target target = new Target();
    
            if ( source.getValue() != null ) {
                target.setValue( String.valueOf( mapField( source.getValue() ) ) );
            }
    
            return target;
        }
    }

    I'm using 1.5.0.Beta1 at the moment.

    3 replies
    bonaluo
    @bonaluo
    @filiphr I can not get prompt for code completion, is it matter with lombok
    9 replies
    public interface ClassABConverter {
        ClassABConverter INSTANCE = Mappers.getMapper(ClassABConverter.class);
    
    //    @Mapping(target = "b",source = "aSpecial")
        @Mappings(value = {
                @Mapping(target = "BSpecial",source = "ASpecial")
        })
        ClassB toClassB(ClassA classA);
    
    }
    AdrianLeeElder
    @AdrianLeeElder

    Hi there. I'm trying to figure out how to improve the debug output in Intellij to figure out which mappers are failing. I have (simplified):

    public interface TypeMapper<T, O> {
        O convert(T t);
    }

    I have several mapper classes that extend from this type. When a property fails to map, I get something like:

    /TypeMapper.java:[15,11] Can't map property "java.util.List<java.lang.String> obj.property" to "java.util.Collection<? extends com.app.MyObj> obj.tags". Consider to declare/implement a mapping method: "java.util.Collection<? extends com.app.MyObj> map(java.util.List<java.lang.String> value)".

    I have several mapper classes this could be failing in, but because I'm extending TypeMapper<T, O>, the debug output shows this interface instead of the implementation that is failing. Is there a way to improve this output?

    1 reply
    niklas
    @niklas:matrix.niklasfi.de
    [m]
    Hi, I have a project built on mapstruct 1.4.2.Final in combination with spring boot 2.5.4 and lombok 1.18.20. I have set up maven-compiler-plugin as described here: https://mapstruct.org/documentation/stable/reference/html/#_set_up im am getting an javax.annotation.processing.FilerException: Attempt to recreate a file for type core.mapstruct.MapStructMapperImpl when executing mvn package a second time. Is this a known issue or am I doing something wrong?