无法实例化目标[subclassDTO]的实例。确保[subclassDTO]具有非私有的无参数构造函数

时间:2019-11-05 03:52:13

标签: java spring-boot inheritance dto modelmapper

我在Springboot应用程序上工作,暴露了一些不返回原始实体但返回其DTO的终结点。要使用“ org.modelmapper”版本:0.7.4和lombok项目映射我正在工作的所有实体,以避免实现实现获取,设置和其他实用程序。

一切正常,直到我不得不在实体上映射子类列表。源实体是这样的(我删除了一些属性和休眠注释,因为在示例中它们是不必要的):

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)

sportPlace包含给定的6个Sportfield,它们是子类的实例。模型映射器将抛出(在列表的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();
    }

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

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

谢谢!

1 个答案:

答案 0 :(得分:0)

我终于在一些朋友的帮助下解决了这个问题,并在此站点上提出了另一个问题。

我的解决方案

另一位开发人员发布了与我的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存储库here上发布了一个问题。最后的评论报告typeMap具有继承性已在ModelMapper 1.0.0 上发布,我发现我正在使用版本 0.7.4

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

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