我正在开发一个Springboot应用程序,该应用程序暴露了一些端点,这些端点不返回原始实体,但返回了DTO。为了映射我正在使用的所有实体,我使用"org.modelmapper"版本:0.7.4和项目lombok来避免实现getter、setter和其他一些实用程序。
一切都很完美,直到我不得不在一个实体上映射一个子类列表。源实体是这样的(我删除了一些属性和hibernate注释,因为它们在示例中是不必要的(:
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;
}
抽象运动场DTO和抽象运动场都是抽象类,有两个可能的子类:
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.Abstract SportFieldDT0具有非专用无参数构造函数。
阅读关于如何在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();
}
我不知道我还能做些什么来映射子类。
有人知道如何解决这个问题吗?
谢谢!
在一些朋友的帮助下,我终于解决了这个问题,并在这个网站上提出了另一个问题。
我的解决方案
另一个开发者在这里发布了一个类似于我的问题。
这篇文章建议使用typeMap
(但不是像我使用的那样(,并声明"converter"。当一个子类试图映射到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上发布了一个问题。最后一条评论报告说,带有继承的typeMap
已经在ModelMapper上发布了1.0.0,我发现我使用的是0.7.4版本
因此,我将版本更新到了当前的(2.3.5(,并开始工作。链接到maven repo。
我希望我的描述能帮助到有同样问题的人。谢谢
实际上我刚刚遇到过同样的问题。
在我的案例中,modelmapper抱怨缺少公共构造函数的类实际上被引用为另一个DTO类中的属性,映射到其他地方,而公共构造函数实际上并没有明确声明。
我添加了这样的公共构造函数,这为我解决了问题