Design of multiple child dtos into a single request spring boot

前端 未结 1 1352
温柔的废话
温柔的废话 2020-12-18 08:03

So, I have a VehicleDto:

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


        
相关标签:
1条回答
  • 2020-12-18 08:27

    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.

    0 讨论(0)
提交回复
热议问题