将多个子dto设计为单个请求的弹簧靴 [英] Design of multiple child dtos into a single request spring boot

查看:146
本文介绍了将多个子dto设计为单个请求的弹簧靴的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以,我有一个VehicleDto:

class VehicleDto {
    private String someId
    private String vType;
    private CarDto car;
    private BikeDto bike;
}

我需要在请求有效负载中包含CarDto或BikeDto.

在请求后的有效负载中,将存在多个字段,这些字段是VehicleDto的属性,例如,此处为someId.现在,此someId也是CarDto和BikeDto的一部分,并且是VehicleDto的子代的任何其他Dto.

因此,当我尝试保存到数据库中时,那里出现了一些问题.

if (vehicleDto.getVType().equals("CAR")) {
    this.saveCar(vehicleDto);
}

private boolean saveCar(TicketSoldCreateDto ticketSoldCreateDto) {
    CarDto carDto = ticketSoldCreateDto.getCar();
    carDto is mapped to Car model
    // Now how do I map the rest of the fields in vehicleDto to Car model??
}

特级车:

@MappedSuperclass
@Data
public abstract class Vehicle extends AbstractBaseEntity {
// fields same as vehicleDto
}

儿童车:

@Entity
@Data
public class Car extends Vehicle {
// Some fields
}

我应该如何设计此类问题?

解决方案

为什么不使用继承而不是DTO的关联,就像实体一样? 然后将这些DTO映射到实体并使用某些映射器返回(我更喜欢 mapstruct ).

我已经在github 上做了一个完整的示例.

DTO:

@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = CarDto.class, name = "car"),
        @JsonSubTypes.Type(value = BikeDto.class, name = "bike")
})
public class VehicleDto {
    private Long id;
    private String type;
    private Integer modelYear;
}

@Data
public class BikeDto extends VehicleDto {
    private String frameType;
}

@Data
public class CarDto extends VehicleDto {
    private Boolean isCabriolet;
}

@JsonTypeInfo @JsonSubTypes 是必需的自动解析Controller中的DTO类型.我的样本控制器接收到VehicleDto,并尝试将其作为Bike实体与DtoMapperVehicleService一起存储在数据库中.最后一步-它再次从数据库中读取它,并以BikeDto响应.

@Controller
public class SampleController {

    @Autowired
    private VehicleService vehicleService;

    @Autowired
    private DtoMapper mapper;

    @PostMapping("/testDto")
    @ResponseBody
    @Transactional
    public BikeDto testDto(@RequestBody VehicleDto vehicleDto) {

        if (vehicleDto instanceof BikeDto)
            vehicleService.saveBike(mapper.toBikeEntity((BikeDto) vehicleDto));

        return mapper.toBikeDto(vehicleService.getBike(vehicleDto.getId()));
    }
}

对于DtoMapper,我使用过Mapstruct,它将Bike实体转换为BikeDto并返回:

@Mapper(componentModel = "spring")
@Component
public interface DtoMapper {

    @Mapping(target = "type", constant = "bike")
    BikeDto toBikeDto(Bike entity);

    Bike toBikeEntity(BikeDto dto);
}

最后,测试此示例的类.它通过BikeDto作为POST正文,并希望它返回.

@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("scratch")
public class SampleDataJpaApplicationTests {

    @Autowired
    private WebApplicationContext context;
    private MockMvc mvc;

    @Before
    public void setUp() {
        this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build();
    }

    @Test
    public void testDto() throws Exception {

        BikeDto bikeDto = new BikeDto();
        bikeDto.setId(42L);
        bikeDto.setType("bike");
        bikeDto.setModelYear(2019);
        bikeDto.setFrameType("carbon");

        Gson gson = new Gson();
        String json = gson.toJson(bikeDto);

        this.mvc.perform(post("/testDto").contentType(MediaType.APPLICATION_JSON).content(json))
                .andExpect(status().isOk())
                .andExpect(content().json(json));
    }
}

POST(BikeDto)正文:

{
 "id":42,
 "type":"bike",
 "modelYear":2019,
 "frameType":"carbon"
}

您可以在完整示例中观看其他类(实体,服务,存储库),在github .

So, I have a VehicleDto:

class VehicleDto {
    private String someId
    private String vType;
    private CarDto car;
    private BikeDto bike;
}

I need to have either the CarDto or BikeDto in the request payload.

In the post request payload there will be multiple fields which are properties of VehicleDto, for example, here someId. Now, this someId is also a part of CarDto and BikeDto, and any other Dto that is a child of VehicleDto.

So when I try to save into the db, I have some issues there.

if (vehicleDto.getVType().equals("CAR")) {
    this.saveCar(vehicleDto);
}

private boolean saveCar(TicketSoldCreateDto ticketSoldCreateDto) {
    CarDto carDto = ticketSoldCreateDto.getCar();
    carDto is mapped to Car model
    // Now how do I map the rest of the fields in vehicleDto to Car model??
}

Super class Vehicle:

@MappedSuperclass
@Data
public abstract class Vehicle extends AbstractBaseEntity {
// fields same as vehicleDto
}

Child class Car:

@Entity
@Data
public class Car extends Vehicle {
// Some fields
}

How should I design such problems?

解决方案

Why not use inheritance instead of association for DTO's, just like for entities? And then map these DTO's to entites and back with some mapper (I prefer mapstruct).

I've made a complete example on github.

DTO's:

@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = CarDto.class, name = "car"),
        @JsonSubTypes.Type(value = BikeDto.class, name = "bike")
})
public class VehicleDto {
    private Long id;
    private String type;
    private Integer modelYear;
}

@Data
public class BikeDto extends VehicleDto {
    private String frameType;
}

@Data
public class CarDto extends VehicleDto {
    private Boolean isCabriolet;
}

@JsonTypeInfo and @JsonSubTypes are needed to automatically resolve DTO type in Controller. My sample controller receives VehicleDto and tries to store it in database as Bike entity with DtoMapper and VehicleService. Last step - it reads it again from database and responds with BikeDto.

@Controller
public class SampleController {

    @Autowired
    private VehicleService vehicleService;

    @Autowired
    private DtoMapper mapper;

    @PostMapping("/testDto")
    @ResponseBody
    @Transactional
    public BikeDto testDto(@RequestBody VehicleDto vehicleDto) {

        if (vehicleDto instanceof BikeDto)
            vehicleService.saveBike(mapper.toBikeEntity((BikeDto) vehicleDto));

        return mapper.toBikeDto(vehicleService.getBike(vehicleDto.getId()));
    }
}

For DtoMapper I've used Mapstruct, it converts my Bike entity to BikeDto and back:

@Mapper(componentModel = "spring")
@Component
public interface DtoMapper {

    @Mapping(target = "type", constant = "bike")
    BikeDto toBikeDto(Bike entity);

    Bike toBikeEntity(BikeDto dto);
}

And last, test class for this example. It passes BikeDto as POST body and expects it to return back.

@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("scratch")
public class SampleDataJpaApplicationTests {

    @Autowired
    private WebApplicationContext context;
    private MockMvc mvc;

    @Before
    public void setUp() {
        this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build();
    }

    @Test
    public void testDto() throws Exception {

        BikeDto bikeDto = new BikeDto();
        bikeDto.setId(42L);
        bikeDto.setType("bike");
        bikeDto.setModelYear(2019);
        bikeDto.setFrameType("carbon");

        Gson gson = new Gson();
        String json = gson.toJson(bikeDto);

        this.mvc.perform(post("/testDto").contentType(MediaType.APPLICATION_JSON).content(json))
                .andExpect(status().isOk())
                .andExpect(content().json(json));
    }
}

POST (BikeDto) body:

{
 "id":42,
 "type":"bike",
 "modelYear":2019,
 "frameType":"carbon"
}

Other classes (entities, services, repositories) you can watch in complete example on github.

这篇关于将多个子dto设计为单个请求的弹簧靴的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆