首页 > 解决方案 > Spring modelmapper 映射延迟加载的实体

问题描述

我正在使用 modelmapper ( http://modelmapper.org/ ) 将具有延迟加载的对象列表(通过休眠)的实体映射到具有 modelmapper.map() 的 DTO。

输出的 dto 有一个空值作为列表,尽管实体的列表已填充。当我更改为急切加载时,一切正常,但我的查询变得太大,所以这不是一个选择。

有谁知道modelmapper如何映射代理的休眠实体类?

这是我的父实体:

public class Category {

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "category")
    @OrderBy("sortOrder")
    private SortedSet<CategorySubarea> categorySubareas;
}

dto:

@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public class CategoryDto {

    private LinkedHashSet<@Valid CategorySubareaDto> categorySubareas;
}

这就是我使用映射器的方式(sourceCategory 是实体,destinationCategory 是 dto):

modelMapper.map(sourceCategory, destinationCategory);

当然这样的事情是可行的,但这是一个非常丑陋的解决方法:

            sourceCategory.getCategorySubareas().forEach(subarea -> {
               // do nothing here
            });
            modelMapper.map(sourceCategory, destinationCategory);

标签: javaspringhibernatemodelmapper

解决方案


我知道用 mapstruct 处理惰性/dto 的好方法。只需创建自定义上下文类

public class CycleAvoidingMappingContext {

  private Map<Object, Object> knownInstances = new IdentityHashMap<Object, Object>();
  PersistenceUtil persistenceUnitUtil = Persistence.getPersistenceUtil();

  @SneakyThrows
  @BeforeMapping
  public <T> T getMappedInstance(Object source, @TargetType Class<T> targetType) {
    if (source != null) {
      for (PropertyDescriptor pd : Introspector.getBeanInfo(source.getClass()).getPropertyDescriptors()) {
        if (pd.getWriteMethod() != null && pd.getReadMethod() != null && !"class".equals(pd.getName())) {
          var vvalue = pd.getReadMethod().invoke(source);

          if (vvalue == null) {
            continue;
          }

          var classs = vvalue.getClass();
          if (HibernateProxy.class.isAssignableFrom(classs)) {
            try {
              if (Hibernate.isInitialized(vvalue) == false) {
                pd.getWriteMethod().invoke(source, null);
              }
            } catch (Exception e) {
            }
          }
        }
      }
    }
    return (T) knownInstances.get(source);
  }

  @BeforeMapping
  public void storeMappedInstance(Object source, @MappingTarget Object target) {
    knownInstances.put(source, target);
  }
}

并像这样使用它:

CrmClientMapper.INSTANCE.getDto(client, new CycleAvoidingMappingContext());

它将跳过惰性字段,因此您不会得到惰性初始化异常和对 DB 的不必要查询。只是您用于指定要获取的关系的实体图。


推荐阅读