【问题标题】:Can I extend MapStruct methods?我可以扩展 MapStruct 方法吗?
【发布时间】:2021-12-25 08:09:21
【问题描述】:

我正在开发一个库,我希望该库有一个像这样的映射器:

import org.mapstruct.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import spring.graphql.rest.rql.core.nodes.PropertyNode;
import spring.graphql.rest.rql.core.nodes.TraversalMapper;
import spring.graphql.rest.rql.example.dto.AccountDto;
import spring.graphql.rest.rql.example.models.Account;

import java.util.*;

@Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
        nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE, uses = {TraversalMapper.class})
public abstract class AccountMapper {

    @Autowired
    @Qualifier("accountMapperImpl")
    protected AccountMapper accountMapper;

    @Autowired
    protected PostMapper postMapper;

    @Autowired
    protected PersonMapper personMapper;

    @Autowired
    protected CommentMapper commentMapper;

    @IterableMapping(qualifiedByName = "dynamicAccounts")
    public abstract Set<AccountDto> rqlToAccountDtos(Set<Account> entity, @Context StringBuilder currentPath, @Context List<PropertyNode> propertyNodes, @Context List<String> properties, @Context String property);

    // TODO: Implement automatic generation/addition of these mappers
    @Named("dynamicAccounts")
    @Mapping(target = "friends", expression = "java(properties.contains(\"friends\") ? accountMapper.rqlToAccountDtos(entity.getFriends(), currentPath, propertyNodes, properties, \"friends\") : null)")
    @Mapping(target = "posts", expression = "java(properties.contains(\"posts\") ? postMapper.toPostDtos(entity.getPosts(), currentPath, propertyNodes, properties, \"posts\") : null)")
    @Mapping(target = "comments", expression = "java(properties.contains(\"comments\") ? commentMapper.toCommentDtos(entity.getComments(), currentPath, propertyNodes, properties, \"comments\") : null)")
    @Mapping(target = "person", expression = "java(properties.contains(\"person\") ? personMapper.toPersonDto(entity.getPerson(), currentPath, propertyNodes, properties, \"person\") : null)")
    public abstract AccountDto rqlToAccountDto(Account entity, @Context StringBuilder currentPath, @Context List<PropertyNode> propertyNodes, @Context List<String> properties, @Context String property);

    @Named("dynamicAccountsDefaultCollection")
    public Set<AccountDto> toAccountDtosDefault(Collection<Account> entity, @Context List<PropertyNode> propertyNodes) {
        return rqlToAccountDtos(new HashSet<>(entity), new StringBuilder(), propertyNodes, new ArrayList<>(), "");
    }

    @Named("dynamicAccountsDefault")
    public AccountDto toAccountDtoDefault(Account entity, @Context List<PropertyNode> propertyNodes) {
        return rqlToAccountDto(entity, new StringBuilder(), propertyNodes, new ArrayList<>(), "");
    }
}

这意味着内部重新映射。现在,我想添加一个功能,用户可以“覆盖”此方法,例如,他们可以添加一条语句来忽略帐户密码,同时还调用内部方法。

几个例子作为参考点:

import org.mapstruct.*;
import spring.graphql.rest.rql.core.nodes.PropertyNode;
import spring.graphql.rest.rql.example.dto.AccountDto;
import spring.graphql.rest.rql.example.models.Account;

import java.util.List;

@Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
        nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public abstract class RealAccountMapper extends AccountMapper {

    @Mapping(target = "username", ignore = true)
    public abstract AccountDto toAccountDtoDefault(Account entity, @Context List<PropertyNode> propertyNodes);
}

@Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
        nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public abstract class RealAccountMapper {

    @Mapping(target = "username", ignore = true)
    @Mapper(uses = AccountMapper.class, qualifiedByName = "dynamicAccountsDefault")
    public abstract AccountDto toAccountDto(Account entity, @Context List<PropertyNode> propertyNodes);
}

用户应该能够定义一个要忽略的属性,或者例如添加一个指定的源。根据我的实验,即使没有覆盖等,它也只是生成一个单独的映射方法,而不是尝试重用另一个。

这是否可能不需要在用户端实现调用内部方法的自定义方法?如果不是,是否有特殊原因,或者它只是没有作为 MapStruct 中的一个可能功能出现?

【问题讨论】:

    标签: java spring spring-boot mapstruct


    【解决方案1】:

    MapStruct 在编译期间生成代码。这意味着不可能忽略某些属性并为此调用另一个映射器,因为另一个映射器已经实现了。

    作为一项新功能,我们可以在@Mapper 中添加一些内容,这将基本上合并来自父映射器的配置。

    例如

    @Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
            nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE, methodOverrideStrategy = OverrideStrategy.MERGE)
    public abstract class RealAccountMapper extends AccountMapper {
    
        @Override
        @Mapping(target = "username", ignore = true)
        public abstract AccountDto toAccountDtoDefault(Account entity, @Context List<PropertyNode> propertyNodes);
    }
    

    这意味着如果这个新的methodOverrideStrategy(名称不是最终名称)意味着 MapStruct 将对toAccountDtoDefault 方法执行注释合并。

    对于 MapStruct,就好像用户已经写了一样。

        @Named("dynamicAccountsDefault")
        @Override
        @Mapping(target = "username", ignore = true)
        public abstract AccountDto toAccountDtoDefault(Account entity, @Context List<PropertyNode> propertyNodes);
    

    我建议您在 MapStruct issue tracker 中将此作为功能请求提出。

    【讨论】:

      猜你喜欢
      • 2011-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-20
      • 2019-06-27
      • 1970-01-01
      相关资源
      最近更新 更多