【问题标题】:Failed to instantiate instance of destination [subclassDTO]. Ensure that [subclassDTO] has a non-private no-argument constructor无法实例化目标 [subclassDTO] 的实例。确保 [subclassDTO] 具有非私有无参数构造函数
【发布时间】:2020-03-01 09:48:03
【问题描述】:

我正在开发一个 Springboot 应用程序,该应用程序公开了一些不返回原始实体但返回其 DTO 的端点。为了映射我正在使用“org.modelmapper”版本的所有实体:0.7.4 和项目 lombok 以避免实现 getter、setter 和其他一些实用程序。

在我不得不在实体上映射子类列表之前,一切都运行良好。源实体是这样的(我删除了一些属性和休眠注释,因为它们在示例中不是必需的):

package com.pfi.repository.entity.sport;

import com.pfi.repository.entity.address.Address;
import lombok.*;

import java.util.List;

@Getter
@Setter
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
public class SportPlace {

    private Long id;
    private List<AbstractSportField> sportFields;
}

目标 DTO 是:

package dto.sport;

import lombok.*;

import java.util.List;

@Getter
@NoArgsConstructor
@Setter
public class SportPlaceDTO {

    private Long id;
    private List<AbstractSportFieldDTO> sportFields;
}

AbstractSportFieldDTO 和 AbstractSportField 都是抽象类,有两个可能的子类:

package dto.sport;

import dto.reserve.AbstractReserveDTO;

import java.util.List;

public abstract class AbstractSportFieldDTO {

    protected Long id;
    protected List<AbstractReserveDTO> reserves;
    public AbstractSportFieldDTO() {
    }

    public AbstractSportFieldDTO(Long id, List<AbstractReserveDTO> reserves) {
        this.id = id;
        this.reserves = reserves;
    }

    public Long getId() {
        return id;
    }

    public AbstractSportFieldDTO setId(Long id) {
        this.id = id;
        return this;
    }


    public List<AbstractReserveDTO> getReserves() {
        return reserves;
    }

    public AbstractSportFieldDTO setReserves(List<AbstractReserveDTO> reserves) {
        this.reserves = reserves;
        return this;
    }
}

第一个 DTO 子类是:

package dto.sport;


import dto.reserve.AbstractReserveDTO;

import java.util.List;

public class ComboSportFieldDTO extends AbstractSportFieldDTO {

    private List<SportFieldDTO> sportFields;

    public ComboSportFieldDTO(List<SportFieldDTO> sportFields) {
        this.sportFields = sportFields;
    }

    public ComboSportFieldDTO(Long id, List<AbstractReserveDTO> reserves, List<SportFieldDTO> sportFields) {
        super(id, reserves);
        this.sportFields = sportFields;
    }

    public ComboSportFieldDTO() {
        super();
    }

    public List<SportFieldDTO> getSportFields() {
        return sportFields;
    }

    public void setSportFields(List<SportFieldDTO> sportFields) {
        this.sportFields = sportFields;
    }
}

第二个 DTO 子类是:

package dto.sport;

import dto.reserve.AbstractReserveDTO;

import java.util.List;

public class SportFieldDTO extends AbstractSportFieldDTO {

    private Boolean joineable;

    public SportFieldDTO(Long id, List<AbstractReserveDTO> reserves, Boolean joineable) {
        super(id, reserves);
        this.joineable = joineable;
    }

    public SportFieldDTO() {
        super();
    }

    public Boolean getJoineable() {
        return joineable;
    }

    public void setJoineable(Boolean joineable) {
        this.joineable = joineable;
    }
}

那么实体是:

package com.pfi.repository.entity.sport;

import com.pfi.repository.entity.reserve.AbstractReserve;
import java.util.List;

public abstract class AbstractSportField {

    protected Long id;
    protected List<AbstractReserve> reserves;

    public AbstractSportField(Long id, List<AbstractReserve> reserves) {
        this.id = id;
        this.reserves = reserves;
    }

    public AbstractSportField() {

    }

    public Long getId() {
        return id;
    }

    public AbstractSportField setId(Long id) {
        this.id = id;
        return this;
    }

    public List<AbstractReserve> getReserves() {
        return reserves;
    }

    public AbstractSportField setReserves(List<AbstractReserve> reserves) {
        this.reserves = reserves;
        return this;
    }

}

第一个子类是:

package com.pfi.repository.entity.sport;

import com.pfi.repository.entity.reserve.AbstractReserve;

import java.util.List;

public class ComboSportField extends AbstractSportField {

    private List<SportField> sportFields;

    public ComboSportField(Long id, List<AbstractReserve> reserves, List<SportField> sportFields) {
        super(id, reserves);
        this.sportFields = sportFields;
    }

    public ComboSportField(){
        super();
    }

    public List<SportField> getSportFields() {
        return sportFields;
    }

    public ComboSportField setSportFields(List<SportField> sportFields) {
        this.sportFields = sportFields;
        return this;
    }
}

最后一个子类是:

package com.pfi.repository.entity.sport;

import com.pfi.repository.entity.reserve.AbstractReserve;

import javax.persistence.Entity;
import java.util.List;

public class SportField extends AbstractSportField {

    private Boolean joineable;

    public SportField(Long id, List<AbstractReserve> reserves, Boolean joineable) {
        super(id, reserves);
        this.joineable = joineable;
    }

    public SportField(){
        super();
    }

    public Boolean getJoineable() {
        return joineable;
    }

    public SportField setJoineable(Boolean joineable) {
        this.joineable = joineable;
        return this;
    }
}

当我尝试像这样将 SportPlace 的实例映射到 SportPlaceDTO 时:

modelMapper.map(sportPlace, SportPlaceDTO.class)

de sportPlace 包含给定的 6 个作为子类实例的运动场。模型映射器抛出(在列表的 6 个元素上):

无法实例化目标 dto.sport.AbstractSportFieldDTO 的实例。确保 dto.sport.AbstractSportFieldDTO 有一个非私有的无参数构造函数。

阅读有关如何在 modelmapper 上映射子类的信息,我发现设置 TypeMap,可以像这样在类上设置特定映射:

modelMapper.createTypeMap(ComboSportField.class, ComboSportFieldDTO.class);

出于这个原因,我创建了一个构建器来像这样配置模型映射器

package com.pfi.repository.builder;

import com.pfi.repository.entity.*;;
import dto.reserve.*;
import dto.sport.*;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.stereotype.Service;

@Service
public class MapperBuilder {

    public ModelMapper buildModelMapper() {
        ModelMapper modelMapper = new ModelMapper();
        modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
        mapEntitiesToDTO(modelMapper);
        mapDTOToEntities(modelMapper);
        return modelMapper;
    }

    private void mapEntitiesToDTO(final ModelMapper modelMapper){
        modelMapper.createTypeMap(ComboSportField.class, ComboSportFieldDTO.class);
        modelMapper.createTypeMap(SportField.class, SportFieldDTO.class);
        modelMapper.createTypeMap(Reserve.class, ReserveDTO.class);
        modelMapper.createTypeMap(AppReserve.class, AppReserveDTO.class);

    }
    private void mapDTOToEntities(final ModelMapper modelMapper){
        modelMapper.createTypeMap(ComboSportFieldDTO.class, ComboSportField.class);
        modelMapper.createTypeMap(SportFieldDTO.class, SportField.class);
        modelMapper.createTypeMap(ReserveDTO.class, Reserve.class);
        modelMapper.createTypeMap(AppReserveDTO.class, AppReserve.class);
    }
}

当我像这样创建控制器时,我会使用构建器:

@Autowired
    protected MapperBuilder mapperBuilder;

    BaseController(){
        this.getLogger();
    }

    @PostConstruct
    public void buildMapper(){
        modelMapper = mapperBuilder.buildModelMapper();
    }

我不知道我还能做些什么来映射子类。

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

谢谢!

【问题讨论】:

    标签: java spring-boot inheritance dto modelmapper


    【解决方案1】:

    在一些朋友的帮助和本站的另一个问题的帮助下,我终于解决了这个问题。

    我的解决方案

    另一个开发者发布了一个与我的Here类似的问题。

    这篇文章推荐使用typeMap(但不是我使用的)和“转换器”声明。当一个子类试图映射到AbstractClassDTO 时,它必须实例化一个subclassDTO

    像这样:

    public ModelMapper buildModelMapper() {
        ModelMapper modelMapper = new ModelMapper();
        modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
        mapEntitiesToDTO(modelMapper);
        mapDTOToEntities(modelMapper);
        return modelMapper;
    }
    
    private void mapEntitiesToDTO(final ModelMapper modelMapper) {
        modelMapper.createTypeMap(ComboSportField.class, AbstractSportFieldDTO.class)
            .setConverter(converterWithDestinationSupplier(ComboSportFieldDTO::new));
        modelMapper.createTypeMap(SportField.class, AbstractSportFieldDTO.class)
            .setConverter(converterWithDestinationSupplier(SportFieldDTO::new));
    }
    
    private void mapDTOToEntities(final ModelMapper modelMapper) {
        modelMapper.createTypeMap(ComboSportFieldDTO.class, AbstractSportField.class)
            .setConverter(converterWithDestinationSupplier(ComboSportField::new));
        modelMapper.createTypeMap(SportFieldDTO.class, AbstractSportField.class)
            .setConverter(converterWithDestinationSupplier(SportField::new));
    }
    
    private < S, D > Converter < S, D > converterWithDestinationSupplier(Supplier << ? extends D > supplier) {
        return ctx - > ctx.getMappingEngine().map(ctx.create(ctx.getSource(), supplier.get()));
    }
    

    当我测试此配置时,它仍然无法正常工作。然后我发现发布最后一个问题的同一个开发人员在modelmapper GitHub repo here 上发布了一个问题。最后一条评论说typeMap继承了ModelMapper 1.0.0,我发现我使用的是0.7.4版本。

    所以我将版本更新为当前的(2.3.5),这开始起作用了。 Link to maven repo.

    我希望我的描述可以帮助遇到同样问题的人。谢谢!

    【讨论】:

    • 这个解决方案帮助了我。更新模型映射器版本解决了这个问题。
    【解决方案2】:

    其实我也遇到了同样的问题。

    在我的例子中,modelmapper 抱怨缺少公共构造函数的类实际上被引用为另一个 DTO 类中的属性,映射到其他地方,公共构造函数实际上没有被显式声明。

    我已经添加了这样的公共构造函数,这为我解决了这个问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-06
      • 1970-01-01
      • 2017-09-04
      • 1970-01-01
      • 2013-05-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多