Search code examples
javahibernatedesign-patternslazy-loadingdto

Preserve Hibernate Lazy loading in data transfer object design pattern


I usually work on 3-tier applications using Hibernate in the persistence layer and I take care to not use the domain model classes in the presentation layer. This is why I use the DTO (Data Transfer Object) design pattern.

But I always have a dilemma in my entity-dto mapping. Whether I lose the lazy loading benefict, or I create complexity in the code by introducing filters to call or not the domain model getters.


Example : Consider a DTO UserDto that corresponds to the entity User

public UserDto toDto(User entity, OptionList... optionList) {

        if (entity == null) {
            return null;
        }

        UserDto userDto = new UserDto();
        userDto.setId(entity.getId());
        userDto.setFirstname(entity.getFirstname());


        if (optionList.length == 0 || optionList[0].contains(User.class, UserOptionList.AUTHORIZATION)) {
            IGenericEntityDtoConverter<Authorization, AuthorizationDto> authorizationConverter = converterRegistry.getConverter(Authorization.class);

            List<AuthorizationDto> authorizations = new ArrayList<>(authorizationConverter.toListDto(entity.getAuthorizations(), optionList));
            userDto.setAuthorizations(authorizations);

...
}

OptionList is used to filter the mapping and map just what is wanted.

Although the last solution allow lazy loading but it's very heavy because the optionList must be specified in the service layer.


Is there any better solution to preserve lazy loading in a DTO design pattern ?


Solution

  • For the same entity persistent state, I don't like having fields of an object un-initialized in some execution path, while these fields might also be initialized in other cases. This cause too much headache to maintain :

    • it will cause Nullpointer in the better cases
    • if null is also a valid option (and thus not cause NullPointer), it could mean the data was removed and might trigger unexpected removal business rules, while the data is in fact still there.

    I would rather create a DTO hierarchy of interfaces and/or classes, starting with UserDto. All of the actual dto implementation fields are filled to mirror the persistent state : if there is data, the field of the dto is not null.

    So then you just need to ask the service layer which implementation of the Dto you want :

    public <T extends UserDto> T toDto(User entity, Class<T> dtoClass) {
        ...
    }
    

    Then in the service layer, you could have a :

    Map<Class<? extends UserDto>, UserDtoBUilder> userDtoBuilders = ...
    

    where you register the different builders that will create and initialize the various UserDto implementations.