Skip to content

Support generic @Context #3711

@ilia1243

Description

@ilia1243

Use case

Consider the following example mapstruct-jpa-child-parent but with generic JpaContext:

@Data
class ParentDto {
    ChildDto child;
}

@Data
class ChildDto {
    ParentDto parent;
}

@Data
class ParentEntity {
    ChildEntity child;
}

public interface ParentRelation<T> {
    void setChildToParent(T parent);
}

@Data
class ChildEntity implements ParentRelation<ParentEntity> {
    ParentEntity childToParent;
}

class JpaContext<T> {
    T parentEntity;

    @BeforeMapping
    void setEntity(@MappingTarget T parentEntity) {
        this.parentEntity = parentEntity;
    }

    @AfterMapping
    void establishRelation(@MappingTarget ParentRelation<T> childEntity) {
        childEntity.setChildToParent(parentEntity);
    }
}

interface BaseMapper<T, E> {
    default E toEntity(T s) {
        return toEntity(s, new JpaContext<>());
    }

    E toEntity(T s, @Context JpaContext<E> ctx);
}

@Mapper
interface SourceTargetMapper extends BaseMapper<ParentDto, ParentEntity> {
}

In this case MapStruct does not invoke @BeforeMapping and @AfterMapping methods in SourceTargetMapperImpl, although it is allowed by Java assignment rules.

Generated Code

Desired generated code:

@Generated(
    value = "org.mapstruct.ap.MappingProcessor"
)
class SourceTargetMapperImpl implements SourceTargetMapper {

    @Override
    public ParentEntity toEntity(ParentDto s, Ctx ctx) {
        if ( s == null ) {
            return null;
        }

        ParentEntity parentEntity = new ParentEntity();

        ctx.setEntity( parentEntity );

        parentEntity.setChild( childDtoToChildEntity( s.getChild(), ctx ) );

        return parentEntity;
    }

    protected ChildEntity childDtoToChildEntity(ChildDto childDto, Ctx ctx) {
        if ( childDto == null ) {
            return null;
        }

        ChildEntity childEntity = new ChildEntity();

        ctx.establishRelation( childEntity );

        return childEntity;
    }
}

Possible workarounds

Work around of this issue is to create derived class from JpaContext and use extra type parameter in the mapper to supply the derived class. The following example generates exactly the desired Generated Code above.

interface BaseMapper<T, E, C extends JpaContext<E>> {
    default E toEntity(T s) {
        return toEntity(s, getCtx());
    }

    E toEntity(T s, @Context C ctx);

    C getCtx();
}

@Mapper
interface SourceTargetMapper extends BaseMapper<ParentDto, ParentEntity, SourceTargetMapper.Ctx> {
    default Ctx getCtx() {
        return new Ctx();
    }

    class Ctx extends JpaContext<ParentEntity> {
    }
}

MapStruct Version

1.6.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions