简体   繁体   中英

Mapstruct with Spring Data Jdbc

I'm using Spring Data Jdbc and I have 2 aggregates that are related with a reference id.

public class ResourceEntity {

@Id
@With
private final UUID id;
private String institutionId;
private String version;  
private Long resourceTypeId;

public class ResourceTypeEntity {

@Id @With
private final Long id;
private String name;

I want to map it a GRPC message which will be translated

public class Resource {
    private String institutionId;
    private String version;  
    private String name; <-- This should be mapped after lookup the ResourceTypeEntity byId
}

I have created a ResourceMapper like this

@Mapper(unmappedTargetPolicy = org.mapstruct.ReportingPolicy.IGNORE,
        collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED,
        nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
public interface ResourceMapper {

    ResourceMapper mapper = Mappers.getMapper(ResourceMapper.class);

    @Mapping(target = "name", source = "resourceTypeId", ????
    Resource toResource(ResourceEntity resourceEntity);

    List<Resource> toResources(List<ResourceEntity> resourceEntities);

Essentially I want to use the resourceTypeRepository.findById(resourceTypeId) to get the ResourceTypeEntity and map the name.

How to do that?

Thanks

You have no chance to achieve that through an interface as long as you need to autowire the repository for sake of fetching the object and its name for further mapping. You can, however, use an abstract class instead which is fully compatible with MapStruct class generating including the Spring component model.

@Mapper(
        componentModel = "spring",
        unmappedTargetPolicy = org.mapstruct.ReportingPolicy.IGNORE,
        collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED,
        nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
public abstract static class ResourceMapper {

    @Autowired
    private ResourceTypeRepository repository;

    public abstract Resource toResource(ResourceEntity resourceEntity);

    public abstract List<Resource> toResources(List<ResourceEntity> resourceEntities);

    @AfterMapping
    public void afterMapping(@MappingTarget Resource resource, ResourceEntity entity) {
        
        long id = resourceEntity.getResourceTypeId();

        // Call the repository to fetch ResourceTypeEntity by resourceTypeId
        // The method results in Optional<ResourceTypeEntity> so you might want to 
        // ... throw an exception or use a default value if no entity by id is found
        String name = repository.findById(id)
            .map(ResourceTypeEntity::getName)
            .orElse(null);                      

        resource.setName(name);
    }
}
@Autowire
private ResourceMapper resourceMapper;

void foo() {
    ResourceEntity resourceEntity = ...
    Resource resource = resourceMapper.toResource(resourceEntity);
}

Remember this is rather a service than just a entity-dto mapper as long as the logics inside is not trivial and is dependant on the database connection and data. I'd rather create a @Service which uses interface ResourceMapper with a method that doesn't fetch the data (does the service layer instead) but passes name through @Context :

Resource toResource(ResourceEntity entity, @Context String name);

@AfterMapping
void toResourceAfterMapping(
    @MappingTarget Resource resource, ResourceEntity entity, @Context String name
) {
    resource.setName(name);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM