Return 延迟初始化此集合时为空集合

Return empty collection when this collection is lazy intitialized

我有这个对象:

实体

@Entity
public class someClass{
    private String name;
    private String labelKey;

    @ManyToMany(cascade = {CascadeType.PERSIST,CascadeType.MERGE}, fetch = FetchType.LAZY)
    private Set<Product> products = new HashSet<>();
}

DTO

public class someClass{
    private String name;
    private String labelKey;
    private Set<Product> products = new HashSet<>();
}

我的问题是,当我得到这个对象但产品被延迟初始化时,当我使用 Dozer 将实体映射到 DTO 时,我得到一个 LaziInitializedException,然后我想在我得到延迟初始化的产品时得到它,这个产品将return 空集。 这可能吗?

谢谢你的时间,对不起我的英语,这不是我的母语。

您可以 create/modify 您的 Getter 这样:

public Set<Product> getProducts() {
   if (products == null) {
       return new HashSet<>();
       //or products = new HashSet<>(), but I'm not sure of the side effects as far as database framework is concerned.
   }
   return products;
}

尝试将您的服务 class 或方法标记为 @Transactional 以让 Spring 处理会话管理。

public class ServiceUsingSomeClass {
    final SomeClassRepository someClassRepository;

    //Constructor ...
    
    @Transactional
    showProducts() {
       someClassRepository.findAll();
       // Do something with Set<Product>

    }
    
}

如果您只想避免在使用 Dozer 进行 DTO 映射的情况下获取关联,您可以将其配置为忽略源对象中的 products 字段,方法是扩展 DozerConverter 并使用该自定义转换器。

我也觉得也许这意味着你的目标类型真的不需要 products 字段开头,因为您不会填充它。

如果您的代码库中有很多这样的地方,请考虑使用投影来仅获取当前目的所需的属性。

@fella7ena 提出了一个关于@Transactional 的观点,但这实际上是无关的——您仍然可以在事务中遇到 LazyInitializationException。发生这种情况是因为 Hibernate 忘记了 java bean 的持久状态和数据库状态之间的关系。如果您真的想从数据库中获取 products 关联,则必须使用 eager fetchtype(导致 n+1 问题)、批处理或实体图。

正如您在本教程中看到的那样 here 您可以指示 dozer 从映射中排除某些字段。

如果你这样做,推土机将不会调用你的实体class的getProducts方法,因此不会抛出异常LaziInitializedException

同时,因为您的 DTO 对象是用空 HashSet 字段产品初始化的,所以这将保留在 DTO 的末尾。

所以您的要求将起作用,其中您的实体是为产品延迟初始化的,而您的 DTO returns 是一个空列表,同时映射发生在 dozer.

这是 dozer 的映射器所需的配置。

BeanMappingBuilder mappingExclusion = new BeanMappingBuilder() {
    @Override
    protected void configure() {
        mapping(SomeClassEntity.class, SomeClassDto.class).exclude("products");
    }
};
mapper = new DozerBeanMapper();
mapper.addMapping(mappingExclusion); 

然后你可以使用它来做如下映射

mapper.map(someClassEntityInstance, someClassDtoInstance);