Search code examples
javaspring-bootspring-data-jpamapstruct

Return Entity for static reference tables via mapstruct


3 tables A,B and a reference table status having column status_code which fetches details like description and type. Using Springboot 3.2.5 and JPA and

<mapstruct.version>1.5.5.Final</mapstruct.version>
<lombok-mapstruct-binding>0.2.0</lombok-mapstruct-binding>

Defined mapper for A and B using AEntity and BEntity with their respective DTO's. Each of these classes contain status field which is a FK from status table. How to define the status mapper such that the mapper returns the Entity which is present in the repository for the given status code in the DTO for status object?

As suggested by @Alex, I wrote it as an abstract class like this -

@Mapper(componentModel = "spring")
public abstract class StatusTypeMapper {
    @Autowired
    private StatusTypeRepository StatusTypeRepository;
    StatusTypeMapper INSTANCE = Mappers.getMapper(StatusTypeMapper.class);

    StatusType toEntity(StatusTypeDTO StatusTypeDTO){
        return StatusTypeRepository.findById(StatusTypeDTO.getStatusCode()).get();
    }
} 

and then after compiling I get the below error -

StatusTypeMapperImpl.<init>(StatusTypeMapperImpl.java:12)
    at jdk.internal.reflect.GeneratedConstructorAccessor57.newInstance(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
    at org.mapstruct.factory.Mappers.doGetMapper(Mappers.java:85)
    at org.mapstruct.factory.Mappers.getMapper(Mappers.java:69)
    at org.mapstruct.factory.Mappers.getMapper(Mappers.java:58)
    at myPackage.mapper.StatusTypeMapper.<init>(StatusTypeMapper.java:15)
    at myPackage.mapper.StatusTypeMapperImpl.<init>(StatusTypeMapperImpl.java:12)

Since I am already doing the mapping using mapstruct, I was hoping it to allow me to map the DTO to Enity already present in database in some way..Else I need to fetch it again in my Service layer and it would be rework for nested entities, where this status_code appears multiple times in my parent object


Solution

  • Instead of using repository in mapper we can move the method

    public StatusType toEntity(StatusTypeDTO StatusTypeDTO){
            return StatusTypeRepository.findById(StatusTypeDTO.getStatusCode()).get();
        }
    

    to service layer. Lets rewrite it to use cache:

    @Service
    public StatusTypeService {
    
      @Autowired 
      StatusTypeRepository statusTypeRepository;
    
    
      @Cacheable("status_code")
      public StatusType findStatusTypeById(Long id) {
        return statusTypeRepository.findById(id);
      }
    
      public StatusType toEntity(StatusTypeDTO StatusTypeDTO) {
        return findStatusTypeById(statusTypeDTO.getStatusType.get());
      }
    
    }
    
    

    where the first function will find the entity in database by id and store it in the cache named "status_code" That is similar to store it in the map

    Map <Long, StatusCodeEntity> status_code;
    

    The second function use the first, so when entity requested second time it will be found in cache

    Note, in example I use Long as StatusEntity ID type but it might be different in your case