在数据传输对象设计模式中保留Hibernate延迟加载

时间:2016-12-21 10:02:58

标签: java hibernate design-patterns lazy-loading dto

我通常在持久层中使用Hibernate处理3层应用程序,我注意不要在表示层中使用域模型类。这就是我使用DTO (Data Transfer Object)设计模式的原因。

但是我的实体 - dto映射总是处于两难境地。我是否失去了延迟加载的好处,或者通过引入过滤器调用域模型getters来创建代码的复杂性。

示例: 考虑与实体用户对应的DTO UserDto

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用于过滤映射并映射出所需的内容。

虽然最后一个解决方案允许延迟加载,但它非常繁重,因为必须在服务层中指定optionList

在DTO设计模式中是否有更好的解决方案来保留延迟加载?

2 个答案:

答案 0 :(得分:1)

对于相同的实体持久状态,我不喜欢在某个执行路径中取消初始化对象的字段,而在其他情况下也可能初始化这些字段。这导致太难以维持:

  • 在更好的情况下会导致Nullpointer
  • 如果null也是一个有效选项(因此不会导致NullPointer),则可能意味着数据已被删除,并可能触发意外删除业务规则,而数据实际上仍然存在。

我宁愿创建一个接口和/或类的DTO层次结构,从UserDto开始。填充所有实际的dto实现字段以镜像持久状态:如果有数据,则dto的字段不为空。

那么你只需要向服务层询问你想要的Dto实现:

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

然后在服务层中,您可以拥有:

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

您可以在其中注册将创建和初始化各种UserDto实现的不同构建器。

答案 1 :(得分:0)

我不确定你为什么要延迟加载,但我猜是因为UserDto通过optionList配置提供了多种表示形式? 我不知道你的表示层代码是怎么样的,但我猜你在optionList中有很多if-else代码用于每个元素?

如何使用不同的表示形式,即子类代替?我问这个是因为我想建议试试Blaze-Persistence Entity Views。这里有一个适合您域名的代码示例。

@EntityView(User.class)
public interface SimpleUserView {
    // The id of the user entity
    @IdMapping("id") int getId();

    String getFirstname();
}

@EntityView(Authorization.class)
public interface AuthorizationView {

    // The id of the authorization entity
    @IdMapping("id") int getId();

    // Whatever properties you want
}

@EntityView(User.class)
public interface AuthorizationUserView extends SimpleUserView {

    List<AuthorizationView> getAuthorizations();
}

这些是 DTOs ,其中包含有关实体模型映射的一些元数据。以下是用法:

@Transactional
public <T> T findByName(String name, EntityViewSetting<T, CriteriaBuilder<T>> setting) {
    // Omitted DAO part for brevity

    EntityManager entityManager = // jpa entity manager
    CriteriaBuilderFactory cbf = // query builder factory from Blaze-Persistence
    EntityViewManager evm = // manager that can apply entity views to query builders

    CriteriaBuilder<User> builder = cbf.create(entityManager, User.class)
        .where("name").eq(name);
    List<T> result = evm.applySetting(builder, setting)
        .getResultList();
    return result;
}

现在,如果您像service.findByName("someName", EntityViewSetting.create(SimpleUserView.class))一样使用它,它将生成类似

的查询
SELECT u.id, u.firstname 
FROM User u 
WHERE u.name = :param_1

如果您使用其他视图,例如service.findByName("someName", EntityViewSetting.create(AuthorizationUserView.class)),则会生成

SELECT u.id, u.firstname, a.id 
FROM User u LEFT JOIN u.authorizations a 
WHERE u.name = :param_1

除了能够摆脱手动对象映射外,由于使用了优化的查询,性能也会提高!