Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I have a relationnal (heavy) database model with a lot of table dependencies and foreign keys. We have choosen to use DTOs in order to simplify data representation ton front and hide database mode complixity.

But we have DTO with nested DTO. And we have Mapper implementation classes to set data with small business/functional logic.

The question is if it is a good pratice that a mapper class calls mapper (etc.) or is it a best way to have a main class handling all mapper classes ? (Example 1 or2)

Example 1 :

@Component
public class ActorMapperImpl implements ActorMapper {

    @Autowired
    private InsurerMapper insurerMapper;

    @Autowired
    private PersonMapper personMapper;

    @Autowired
    private CorrespondentMapper correspondentMapper;

    ....

    @Override
    public ActorDto mapToDto(Acteur actor) {
        final ActorDto actorDto;
        
        if (actor != null) {
            ....
            
            actorDto.setPerson(personMapper.personneToPersonDto(actor.getPersonne()));

            
            if (actor.getInsurer() != null) {
                actorDto.setInsurer(insurerMapper.entityToDto(actor.getInsurer()));
            } else if (actor.getCorrespondantAssureur() != null) {
                actorDto.setInsurer(correspondentMapper.correspondentToInsurerDto(actor.getCorrespondantAssureur()));
            }

            ....

            // intermediate
            final Intermediaire intermediate = actor.getIntermediaire();
            if (intermediate != null) {
                .....
                if (person != null) {
                    intermediateDto = personMapper.personneToPersonDto(person);
                    intermediateDto.setQuality(quality);
                } 
                .....
            }
        .....

Example 2 :

@Service
public class FinancialSlipOrchestratorImpl implements FinancialSlipOrchestrator {

    .....
   

    @Autowired
    private FinancialSlipMapper financialSlipMapper;

    @Autowired
    private PersonMapper personMapper;

    

    ..... some public / private methods

    

    private FinancialSlipDto fullMapToDto(FinancialSlip financialSlip) {
        .....
    
    
        // Financial slip
        var financialSlipDto = financialSlipMapper.mapToDto(financialSlip);

        // person
        financialSlipDto.setIssuerPerson(personMapper.personneToPersonDto(financialSlip.getIssuerPerson()));
        
        ....

        // RIB
        financialSlipDto.setRib(ribMapper.mapToDto(financialSlip.getRib()));

        return financialSlipDto;
    }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
380 views
Welcome To Ask or Share your Answers For Others

1 Answer

I would say that it's ok for one mapper to call another and think this is a perfect use case for Blaze-Persistence Entity Views.

I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.

A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:

@EntityView(Acteur.class)
public interface ActorDto {
    @IdMapping
    Long getId();
    String getName();
    PersonDto getPerson();
    default InsurerDto getInsurer() {
        return getMainInsurer() != null ? getMainInsurer(): getCorrespondantAssureur();
    }
    @Mapping("insurer")
    InsurerDto getMainInsurer();
    InsurerDto getCorrespondantAssureur();
    IntermediaireDto getIntermediaire();

    @EntityView(Person.class)
    interface PersonDto {
        @IdMapping
        Long getId();
        String getName();
    }
    @EntityView(Insurer.class)
    interface InsurerDto  {
        @IdMapping
        Long getId();
        String getName();
    }
    @EntityView(Intermediaire.class)
    interface IntermediaireDto {
        @IdMapping
        Long getId();
        String getName();
        Integer getQuality();
        PersonDto getPerson();
    }
}

Querying is a matter of applying the entity view to a query, the simplest being just a query by id.

ActorDto a = entityViewManager.find(entityManager, ActorDto.class, id);

The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

The best thing about this is, it will only fetch the data that is actually necessary.

If you use DTOs for flushing back changes as well, you will be delighted to hear that Blaze-Persistence Entity-Views also supports that in a very efficient manner. This will allow you to get rid of all those manually written mappers :)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...