Quarkus中MapStruct映射ProductDto至ProductEntity并关联已有CategoryEntity
问题描述
现有以下实体类(已简化无关字段):
@Entity class CategoryEntity { private Long id; private String name; } @Entity class ProductEntity { private Long id; private String name; private CategoryEntity category; }
对应的DTO定义:
record ProductDto(String name, String categoryName) { }
Service层处理逻辑如下:
public ProductEntity processProduct(ProductDto productDto) { // 查询并校验分类存在性 CategoryEntity categoryEntity = categoryService.findCategoryByName(productDto.categoryName()); ProductEntity productEntity = productMapper.toEntity(productDto); productEntity.setCategory(categoryEntity); // 其他业务处理 // ... return productEntity; }
需求是实现MapStruct Mapper,在将ProductDto转换为ProductEntity时直接使用已查询到的CategoryEntity,避免手动调用setCategory方法。
曾尝试传入CategoryEntity作为参数,但出现错误:
@Mapper interface ProductMapper { @Mapping(target = "category", source = "categoryEntity") ProductEntity toEntity(ProductDto dto, CategoryEntity categoryEntity); }
该写法会导致categoryEntity.id被错误赋值给productEntity.id。
环境:基于Quarkus框架,使用MapStruct 1.5.5版本。
解决方案
问题根源是MapStruct的默认映射规则:它会自动匹配所有同名属性,由于CategoryEntity和ProductEntity都包含id字段,导致MapStruct错误地将分类实体的id赋值给产品实体的id。只需通过显式配置映射规则即可解决。
正确的Mapper实现
@Mapper interface ProductMapper { // 忽略产品实体的id字段,避免被错误映射 @Mapping(target = "id", ignore = true) // 指定分类字段来源为传入的categoryEntity参数 @Mapping(target = "category", source = "categoryEntity") ProductEntity toEntity(ProductDto dto, CategoryEntity categoryEntity); }
逻辑说明
@Mapping(target = "id", ignore = true):明确告知MapStruct不处理ProductEntity的id字段,彻底阻断分类实体id的错误赋值路径。@Mapping(target = "category", source = "categoryEntity"):直接指定产品实体的category字段取值为传入的categoryEntity参数,完成目标赋值。- 产品实体的
name字段会被MapStruct自动匹配DTO的name字段,无需额外配置(字段名一致时默认生效)。
Service层调用优化
修改后Service层可直接传入两个参数,无需手动设置分类:
public ProductEntity processProduct(ProductDto productDto) { CategoryEntity categoryEntity = categoryService.findCategoryByName(productDto.categoryName()); // 直接通过Mapper完成所有映射 ProductEntity productEntity = productMapper.toEntity(productDto, categoryEntity); // 其他业务处理 // ... return productEntity; }
进阶:自定义映射逻辑(可选)
如果需要更精细的控制,比如仅映射分类实体的部分属性,可添加自定义方法:
@Mapper interface ProductMapper { @Mapping(target = "id", ignore = true) @Mapping(target = "category", source = "categoryEntity") ProductEntity toEntity(ProductDto dto, CategoryEntity categoryEntity); // 自定义分类实体到分类实体的映射(仅保留需要的属性) default CategoryEntity mapCategory(CategoryEntity source) { CategoryEntity target = new CategoryEntity(); target.setId(source.getId()); target.setName(source.getName()); return target; } }
不过在当前场景下,直接传入完整的分类实体即可,无需此步骤。
内容的提问来源于stack exchange,提问作者zmila
相关产品推荐
相关产品推荐

