Skip to content

Support generic @Context #3711

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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