0

I have a @MapperConfig looks like this.

@MapperConfig(componentModel = "spring")
public interface SomeEntityTypeMapperConfig {

    @Mapping(target = PROPERTY_NAME_ENTITY)
    @Mapping(source = SomeEntity.ATTRIBUTE_NAME_ID, target = SomeEntityType.PROPERTY_NAME_ID)
    @Mapping(source = SomeEntity.PROPERTY_NAME_CREATED_AT, target = SomeEntityType.PROPERTY_NAME_CREATED_AT)
    @Mapping(source = SomeEntity.PROPERTY_NAME_UPDATED_AT, target = SomeEntityType.PROPERTY_NAME_UPDATED_AT)
    @Mapping(source = SomeEntity.PROPERTY_NAME_CREATED_BY, target = SomeEntityType.PROPERTY_NAME_CREATED_BY)
    @Mapping(source = SomeEntity.PROPERTY_NAME_UPDATED_BY, target = SomeEntityType.PROPERTY_NAME_UPDATED_BY)
    SomeEntityType<?, ?> fromEntity(SomeEntity entity);

    // No @Mapping
    void toEntity(SomeEntityType<?, ?> type, @MappingTarget SomeEntity entity);
}

Here comes my base mapper interface.

public interface SomeEntityTypeMapper<T extends SomeEntityType<?, U>, U extends SomeEntity> {

    T fromEntity(U entity);

    void toEntity(T type, @MappingTarget U entity);
}

And here comes my real mapper.

@Mapper(config = SomeEntityTypeMapperConfig.class)
public interface UserTypeMapper extends SomeEntityTypeMapper<UserType, User> {

    @Mapping(source = User.ATTRIBUTE_NAME_NAME, target = UserType.PROPERTY_NAME_NAME)
    @Override
    UserType fromEntity(User entity);

    @Mapping(source = UserType.PROPERTY_NAME_NAME, target = User.ATTRIBUTE_NAME_NAME)
    @Override
    void toEntity(UserType type, @MappingTarget User entity);
}

And MapStruct generates following impl class with unwanted mappings in it.

public class UserTypeMapperImpl implements UserTypeMapper {

    @Override
    public UserType fromEntity(User entity) {
        if ( entity == null ) {
            return null;
        }
        UserType userType = new UserType();
        userType.setName( entity.getName() );           // explicitly configured
        userType.setId( entity.getId() );               // inherited from the config
        userType.setCreatedAt( entity.getCreatedAt() ); // inherited from the config
        userType.setUpdatedAt( entity.getUpdatedAt() ); // inherited from the config
        userType.setCreatedBy( entity.getCreatedBy() ); // inherited from the config
        userType.setUpdatedBy( entity.getUpdatedBy() ); // inherited from the config
        return userType;
    }

    @Override
    public void toEntity(UserType type, User entity) {
        if ( type == null ) {
            return;
        }
        entity.setName( type.getName() );           // explicitly configured
        entity.setCreatedAt( type.getCreatedAt() ); // UNWANTED!!!
        entity.setUpdatedAt( type.getUpdatedAt() ); // UNWANTED!!!
        entity.setUpdatedBy( type.getUpdatedBy() ); // UNWANTED!!!
        entity.setId( type.getId() );               // UNWANTED!!!
        entity.setCreatedBy( type.getCreatedBy() ); // UNWANTED!!!
    }
}

What did I do wrong and How can I fix it?

Jin Kwon
  • 20,295
  • 14
  • 115
  • 184
  • 1
    I think you're looking for https://mapstruct.org/documentation/stable/api/org/mapstruct/BeanMapping.html#ignoreByDefault-- https://stackoverflow.com/questions/36788642/mapstruct-ignore-automatically-unmapped-properties – Gaargeya Kishan Mar 04 '20 at 06:23

2 Answers2

1

What you are referring as unwanted reverse mapping without any annotations is actually the normal way that MapStruct generates mappings. If the source and target beans have the same property (which they do in your case) MapStruct would create a mapping for it.

In case you don't want to map some properties you can either ignore those one by one or use @BeanMapping( ignoreByDefault = true). With the second option MapStruct would only create mappings for the defined @Mapping.

Filip
  • 19,269
  • 7
  • 51
  • 60
0

I'm sharing what I found.

I needed to annotate the method with @BeanMapping(ignoreByDefault = true).

Interestingly the annotation must be located with leaf mapper interfaces.

@BeanMapping(ignoreByDefault = true) // WORKS!!!
@Mapping(source = UserType.PROPERTY_NAME_NAME, target = User.ATTRIBUTE_NAME_NAME)
@Override
void toEntity(UserType type, @MappingTarget User entity);

Didn't work with configuration nor parent interface.

public interface SomeEntityTypeMapperConfig {

    @BeanMapping(ignoreByDefault = true) // Doesn't work!
    void toEntity(SomeEntityType<?, ?> type, @MappingTarget SomeEntity entity);
}
public interface SomeEntityTypeMapper<T extends SomeEntityType<?, U>, U extends SomeEntity> {

    @BeanMapping(ignoreByDefault = true) // Doesn't work!
    void toEntity(T type, @MappingTarget U entity);
}
Jin Kwon
  • 20,295
  • 14
  • 115
  • 184
  • 1
    Just to be perfectly clear. MapStruct is not generating any reverse mapping code. it just generates mapping code because the properties are the same on both sides. It is implicit. The `@BeanMapping` is not working in the `SomeEntityTypeMapper` because you are overriding that method in your `UserMapper`, for the config I don't know why it isn't working. – Filip Mar 04 '20 at 07:15