使用 CompletableFuture 时如何保持 JPA Fetch 子数据?

问题描述

嗨,我尝试多线程处理我的函数,以执行不同条件的查询,但我认为 JPA 在我执行此操作时丢失了它的持久包,因为数据不会显示并且出现错误。我将首先从我的同步代码开始,因为它可以工作,我将展示我的异步代码不起作用。

这是我的实体类

TransSalesOrder 类

@OnetoMany(mappedBy = "transSalesOrder")
private List<TransDeliveryOrder> transDeliveryOrder;

TransDeliveryOrder 类

@OnetoMany(mappedBy = "deliveryOrder",orphanRemoval = true,cascade = CascadeType.ALL)
private List<TransDeliveryOrderDetail> transDeliveryOrderDetail;

@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "transSalesOrderId")
private TransSalesOrder transSalesOrder;

这里是 TransDeliveryOrderDetail 类:

@JsonIgnoreProperties({ "hibernateLazyInitializer","handler" })
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "delivery_order_id",referencedColumnName = "id")
private TransDeliveryOrder deliveryOrder;

这是我的同步工作方式: 这是我的函数,执行 3 个其他函数

public List<TransSalesOrderOnlyResponseDto> findUnAssignedSO() {
    List<TransSalesOrderOnlyResponseDto> transSalesOrdersResponseNew = findSOHaveItemLeftOverOnly();
    transSalesOrdersResponseNew.addAll(findPureUnAssignedSO());
    transSalesOrdersResponseNew.addAll(findSalesOrderWithBpsjInDeliveryOrder());

    return transSalesOrdersResponseNew;
} 

这里是执行的 3 个函数

private List<TransSalesOrderOnlyResponseDto> findSOHaveItemLeftOverOnly() {
    List<TransSalesOrder> transSalesOrders = iTransSalesOrderQdslRepository.findSOHaveLeftOverButDone();
    return buildTransSalesOrdersResponseNew(transSalesOrders);
}

private List<TransSalesOrderOnlyResponseDto> findPureUnAssignedSO() {
    return iSalesOrderMapper.entityToSOOnlyDto(iTransSalesOrderQdslRepository.findUnAssignedSalesOrder());
}

private List<TransSalesOrderOnlyResponseDto> findSalesOrderWithBpsjInDeliveryOrder() {
    List<TransSalesOrder> transSalesOrders = iTransSalesOrderQdslRepository.findSalesOrderWithBpsjInDeliveryOrder();

    return buildTransSalesOrdersBpsjOnlyResponseNew(transSalesOrders);
}

这种方式是可行的,但后来我尝试异步我的 3 个方法,因为我的 3 个函数可以以异步方式执行,它们不必相互等待完成

这是我使用 CompletableFuture 的异步方式:

public List<TransSalesOrderOnlyResponseDto> findUnAssignedSO() {
    List<TransSalesOrderOnlyResponseDto> transSalesOrdersResponseNew = new ArrayList<>();

    findSOHaveItemLeftOverOnly()
            .thenAccept( transSalesOrdersResponseNew::addAll )
            .thenCompose( v -> findPureUnAssignedSO() )
            .thenAccept( transSalesOrdersResponseNew::addAll )
            .thenCompose( v -> findSalesOrderWithBpsjInDeliveryOrder() )
            .thenAccept( transSalesOrdersResponseNew::addAll )
            .join();

    return transSalesOrdersResponseNew;
}

private CompletableFuture<List<TransSalesOrderOnlyResponseDto>> findSOHaveItemLeftOverOnly() {
    return CompletableFuture.supplyAsync(() -> {
        List<TransSalesOrder> transSalesOrders = iTransSalesOrderQdslRepository.findSOHaveLeftOverButDone();
        return buildTransSalesOrdersResponseNew(transSalesOrders);
    });
}

private CompletableFuture<List<TransSalesOrderOnlyResponseDto>> findPureUnAssignedSO() {
    return CompletableFuture.supplyAsync(() -> iSalesOrderMapper.entityToSOOnlyDto(iTransSalesOrderQdslRepository.findUnAssignedSalesOrder()));
}

private CompletableFuture<List<TransSalesOrderOnlyResponseDto>> findSalesOrderWithBpsjInDeliveryOrder() {
    return CompletableFuture.supplyAsync(() -> {
        List<TransSalesOrder> transSalesOrders = iTransSalesOrderQdslRepository.findSalesOrderWithBpsjInDeliveryOrder();

        return buildTransSalesOrdersBpsjOnlyResponseNew(transSalesOrders);
    });
}

当我运行 findUnAssignedSO 函数时,出现此错误

org.hibernate.LazyInitializationException: 延迟初始化失败 角色集合: com.bit.microservices.b2b.warehouse.entity.TransSalesOrder.transSalesOrderDetail, 无法初始化代理 - 没有会话

当我调试它时,我发现子数据没有加载,这是我尝试的选项:

  • 我尝试使用 @Transactional 保持 JPA 附加,但它不起作用。
  • 我不想在与 EAGER 的@OnetoMany 关系中更改我的 fetchType,我在互联网上阅读了所有内容,但稍后会出现性能问题
  • 使用 Set insted of List,这也会导致性能问题

我没有显示我的存储库,因为我使用 JpaRepository 和 QueryDSL,这是混合的,但我对任何不需要附加到 QueryDSL 的解决方案持开放态度。如何正确获取数据?

解决方法

获取性能是一个困难的话题,我完全理解您不想在实体级别更改获取类型或模式。通常,这可以通过使用实体图来定义关联图来解决,以获取每个查询的关联图。在您的特定情况下,问题在于线程不会获取以后需要的所有状态。当您稍后在不同的线程中访问该状态时,代理不再引用持久性上下文(因为它绑定到事务/线程)并且因您看到的 LazyInitializationException 而失败。您可以通过使用实体图来解决这个问题,这样就不会发生延迟加载,或者通过启用 hibernate.enable_lazy_load_no_trans 属性来按需创建会话来获取数据,但我认为这是 @EntityView(TransSalesOrder.class) public interface TransSalesOrderOnlyResponseDto { @IdMapping Long getId(); String getName(); List<TransDeliveryOrderDto> transDeliveryOrder; @EntityView(TransDeliveryOrder.class) interface TransDeliveryOrderDto { @IdMapping Long getId(); String getName(); } } 的完美用例{3}}。

我创建了该库以允许在 JPA 模型和自定义接口或抽象类定义的模型之间轻松映射,例如类固醇上的 Spring Data Projections。这个想法是,您可以按照自己喜欢的方式定义目标结构(域模型),并通过 JPQL 表达式将属性(getter)映射到实体模型。

您的用例的 DTO 模型可能如下所示,其中包含 Blaze-Persistence Entity-Views:

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

查询是将实体视图应用于查询的问题,最简单的就是通过 id 查询。

Page<TransSalesOrderOnlyResponseDto> findAll(Pageable pageable);

Spring Data 集成允许您像使用 Spring Data Projections 一样使用它:Blaze-Persistence Entity Views

  char local_7 [32];
  long local_78;

  printf("Give it a try");
  gets(local_7);
      if (local_78 != 0x4141414141414141) {
        if (local_78 == 0x1122334455667788) {
          puts ("That's won")
        }
        puts("Let's continue");
      }

最好的部分是,它只会获取实际需要的状态!