当前位置: 首页 > 知识库问答 >
问题:

ModelMapper-定义显式映射时的非法SourceGetter错误

祁宾白
2023-03-14

第一部分

我使用JavaModelMapper库(http://modelmapper.org/)来管理我的实体和DTO之间的映射。我有一个联系人(实体)和一个联系人视图(DTO)。我有一个字符串字段在接触中不存在的联系人称为"类型"。它的值应该只是实体子类的名称。我尝试过这样做这个自定义映射:

modelMapper.typeMap(Contact.class, ContactView.class).addMappings(mapper -> {   
   mapper.map(src -> src.getClass().getSimpleName(), ContactView::setType);
});

我在:mapper处得到一个编译错误。地图(src)-

定义了非法的SourceGetter

组织上有1个错误。springframework。豆。工厂支持简单安装策略。在org上实例化(simpleinstationstrategy.java:185)~[spring-beans-5.3.2.jar:5.3.2]。springframework。豆。工厂支持构造器分解器。实例化(ConstructorResolver.java:653)~[spring-beans-5.3.2.jar:5.3.2]。。。省略了33个公共框架

我甚至尝试使用转换器,同样的结果:

modelMapper.typeMap(Contact.class, ContactView.class).addMappings(mapper -> {
  Converter<Class, String> toName = ctx -> ctx.getSource() == null ? null : ctx.getSource().getSimpleName();
  mapper.using(toName).map(Contact::getClass, ContactView::setType);
});

你知道如何解决这个问题吗?

第二部分

根据建议的答案,我尝试向ModelMapper添加一个转换器类。这是我配置ModelMapper Bean的地方:

@Configuration
public class Mapper {
    @Autowired
    private ContactTypeRepository contactTypeRepository;

    @Bean
    public ModelMapper getMapper() {
        ModelMapper modelMapper = new ModelMapper();
        modelMapper.getConfiguration()
                .setMatchingStrategy(MatchingStrategies.STRICT);

        modelMapper.typeMap(ContactTag.class, ReferenceEntityView.class).addMappings(mapper -> {
            mapper.map(src -> src.getTag().getCode(), ReferenceEntityView::setCode);
            mapper.map(src -> src.getTag().getValue(), ReferenceEntityView::setValue);
        });

        modelMapper.typeMap(Person.class, PersonView.class).addMappings(mapper -> {
            mapper.skip(PersonView::setName);
            mapper.map(Person::getName, PersonView::setLastName);
        });

        modelMapper.addConverter(new ContactConverter());

        return modelMapper;
    }

    class ContactConverter implements Converter<Contact, ContactView>  {
        private ModelMapper localMapper = new ModelMapper();

        @Override
        public ContactView convert(MappingContext<Contact, ContactView> context) {
            Contact contact = context.getSource();
            ContactView contactView = localMapper.map(contact, ContactView.class);
            ContactType contactType = contactTypeRepository.getByCode(context.getSource().getClass().getSimpleName().toLowerCase());
            contactView.setType(localMapper.map(contactType, ReferenceEntityView.class));
            return contactView;
        }
    }
}

这是我使用ModelMapper Bean生成DTO的地方:

@RestController
@RequestMapping(value = "/contacts")
public class ContactController {
    @Autowired
    private ContactRepository contactRepository;
    @Autowired
    private ModelMapper modelMapper;
    
    @GetMapping(value = "/{id}")
    @ResponseStatus(HttpStatus.OK)
    public ContactView findById(@PathVariable("id") Long id){
        Contact c = contactRepository.getOne(id);
        ContactView cv = modelMapper.map(c, ContactView.class);
        return cv;
    }
}

由于某些原因,转换器的convert方法未被调用,ContactView对象的“type”字段为空。ModelMapperbean上的其他映射工作正常。

共有2个答案

麹飞航
2023-03-14

试试我的藏书。这是一个注释处理器。这意味着jdk将在您编译项目之前为您生成类。所以它在编译时完成工作。您可以使用生成的类,就像您自己编写的任何其他类一样。你可以看到生成类的来源,所以没有更多的魔法。这个库能够为您生成DTO类。你不需要改变原来的类。除了在原始类上配置注释之外,您还可以选择创建一个新的配置类,并在此基础上配置注释。该库支持复制和继承原始类的所有属性,并在此基础上删除修改或添加属性。对于你的问题:

// this will generate a DTO class named "ContactView". 
// (This is the default name which just append 'View') 
// You can change this name using genName attribute.
@ViewOf(value=Contact.class, includePattern = ".*")
public class ContactViewConfigure {
    // Add a new property. 
    // Make sure there is no property named 'type' already in Contact. 
    // Or you need use @OverrideViewProperty
    // There are multi way to add new property.
    // In this way, you use a public static method accept the original class instance as the unique argument. 
    // The new property name is decide by the attribute 'type' of @NewViewProperty. 
    // So the method name is not important.
    @NewViewProperty("type")
    public static String type(Contact contact) {
        return contact.getClass().getSimpleName()
    }
}

// This is the generated class.
public class ContactView {
    // other properties
    ...
    private String type;
    // getters and setters (By default only getters are generated)
    ...
    // many constructors
    ...
    public static ContactView read(Contact source) {
        ContactView out = new ContactView();
        // initialize other properties
        ...
        out.type = ContactViewConfigure.type(source);
        // initialize other properties
        ...
        return out;
    }

    // other read method, such as read list, set and map.
    ...
    // other generated methods
    ...
}

// use like this.
Contact contact = ...
ContactView dto = ContactView.read(contact);

在某些情况下,beanknife比ModelMapper强大得多。例如,如果发生错误,您可以检查生成类的源(通常位于/target/generated source/annotations中,在IDE上可能不同),并查看原因。如果它真的是一个bug,您可以将这个问题提交给github,我会尽快处理它。

这里有更多的例子。

洪祺
2023-03-14

这是因为ModelMapper的实现

 public boolean isValid(M member) {
  return !Modifier.isStatic(member.getModifiers()) && !member.isSynthetic();
}

isSynthetic方法的文档中说

如果此成员是由编译器引入的,则返回true;否则返回false。当且仅当编译器引入此成员时,返回:true。

我想这就是为什么它不能例外。

对于类似的情况,我们引入了一个特定的映射器类,使用modelMapper作为基本映射器,并设置另一个字段:

 class ContactMapper{
 ...
 public ContactView toView(Contact contact){
 ContactView contactView = modelMapper.map(contact,ContactView.class);
 contactView.setType(contact.getClass().getSimpleName());
 return contactView;
 }

为了使其与整体映射一致,您可以将其定义为转换器,并像下面这样将其注册到映射中

class ContactConverter implements Converter<Contact, ContactView>  {
        private final ModelMapper localMapper = new ModelMapper();

        @Override
        public ContactView convert(MappingContext<Contact, ContactView> context) {
            Contact contact = context.getSource();
            ContactView contactView = localMapper.map(contact, ContactView.class);
            contactView.setType(contact.getClass().getSimpleName());
            return contactView;
        }
    }

    ModelMapper modelMapper = new ModelMapper();
    modelMapper.addConverter(new ContactConverter());
 类似资料:
  • 为了编写所需的最少代码量,我尝试让ModelMapper生成其隐式映射,并且只为那些无法自动映射的属性编写显式属性映射。 如果我让ModelMapper使用以下命令生成隐式映射: 它抱怨具有多个可能的映射。然后,我尝试使用以下方法修复该问题: 然而,我发现ModelMapper仍在抱怨,因为实际上在上抛出了一个异常,所以它没有机会访问我的自定义映射代码。 如果我反转这两个语句,我会得到一个错误:

  • 我将ModelMapper与Spring一起使用。在我的控制器类中,我正在自动连接ModelMapper bean: 我想在控制器方法中的模型类和DTO之间进行显式映射,例如: 然后使用映射器映射类。 我的问题是,在每个控制器方法调用中添加显式映射是正确的吗?对象建模映射器的内存大小会开始增长吗? 另一个解决方案是在创建ModelMapperbean时只添加一次映射,但我认为在bean配置中添加映

  • 我与ModelMapper框架有麻烦。请解释为什么我看到以下行为。 我在build.gradle有以下依赖性 和一个类客户: 我还有一个地图绘制工具: 还有一个测试 在fred()中,方法输出是非红色的“Customer{name=fred,age=40}”(“Customer{name=null,age=40}”)。你能解释一下为什么吗?为什么我在第一个方法中看不到输出“George”?

  • 我有一个父类和子类,其各自的DTO如下 当我试图将父映射到父映射到父映射到父映射时,我得到了堆栈溢出错误。 请帮我解决这个问题。

  • 我在使用模型映射器使用执行父子实体更新时遇到了一个问题。 描述如下 父类: 儿童班: 通过这种安排,我能够成功地创建和查询站点地址组合。但是,我在进行更新时遇到了一个问题。我正在尝试使用modelmapper语句更新站点或地址,如下所示: 期望在传入站点中更改的所有字段(即来自请求的站点)应替换站点中的现有字段。但是,我从modelmapper中得到一个Stackoverflow错误。 我们是否有

  • 问题内容: 假设我有课: 我想将其翻译为object : 使用默认的ModelMapper设置,我可以通过以下方式实现: 但是,可能会发生该对象。在这种情况下,MyTarget还将是: 我想以这种方式指定自定义映射,(伪代码): 有人知道如何编写适当的转换器来实现这一目标吗? 问题答案: 无法直接使用ModelMapper,因为ModelMapper 方法会检查source是否为null,在这种情