Hibernate 使用复杂DTO管理复杂查询的最佳方法

Hibernate 使用复杂DTO管理复杂查询的最佳方法,hibernate,spring-boot,jpa,spring-data-jpa,dto,Hibernate,Spring Boot,Jpa,Spring Data Jpa,Dto,我正在使用SpringJPA规范创建条件查询。我的代码如下所示: public CollectionWrapper<OrderTableItemDto> getOrders(Integer page, Integer size, String search,

我正在使用SpringJPA规范创建条件查询。我的代码如下所示:

 public CollectionWrapper<OrderTableItemDto> getOrders(Integer page,
                                         Integer size,
                                         String search,
                                         OrderShow show) {

    Pageable pageable = PageRequest.of(page, size, Sort.by(OrderEntity_.ID).descending());

    try {
        Specification<OrderEntity> specifications = buildOrderSpecification(show, search);
        Page<OrderEntity> orders = orderDao.findAll(specifications, pageable);

        List<OrderTableItemDto> result = orders.getContent().stream().map(order -> {
            OrderTableItemDto orderTableItemDto = new OrderTableItemDto();
            orderTableItemDto.setCreatedDate(order.getCreatedDate());
            orderTableItemDto.setCustomerId(order.getCustomer().getId());
            orderTableItemDto.setId(order.getId());
            orderTableItemDto.setStatus(order.getStatus());
            orderTableItemDto.setTotalCost(order.getTotalCost());
            orderTableItemDto.setOrderType(order.getOrderType());
            orderTableItemDto.setOrderTypeLabel(order.getOrderType().getLabel());
            if(order.getOrderInShippingMethod() != null) {
                orderTableItemDto.setShipped(OrderShippingStatus.DELIVERED.equals(order.getOrderInShippingMethod().getStatus()));
            } else {
                orderTableItemDto.setShipped(false);
            }
            StringBuilder sb = new StringBuilder();
            sb.append("#")
                    .append(order.getId())
                    .append(" ");
            if(order.getOrderType().equals(OrderType.WEB_STORE)) {
                sb
                        .append(order.getBilling().getFirstName())
                        .append(" ")
                        .append(order.getBilling().getLastName());

            } else {
                sb.append(order.getCustomer().getFullName());
            }

            orderTableItemDto.setTitle(sb.toString());
            return orderTableItemDto;
        }).collect(Collectors.toList());
        return new CollectionWrapper<>(result, orders.getTotalElements());
public CollectionWrapper getOrders(整型页面,
整数大小,
字符串搜索,
订单展示){
Pageable Pageable=PageRequest.of(page,size,Sort.by(OrderEntity.ID).descending());
试一试{
规范规范=buildOrderSpecification(显示、搜索);
页面顺序=orderDao.findAll(规范,可分页);
列表结果=orders.getContent().stream().map(订单->{
OrderTableItemDto OrderTableItemDto=新的OrderTableItemDto();
orderTableItemDto.setCreatedDate(order.getCreatedDate());
orderTableItemDto.setCustomerId(order.getCustomer().getId());
orderTableItemDto.setId(order.getId());
orderTableItemDto.setStatus(order.getStatus());
orderTableItemDto.setTotalCost(order.getTotalCost());
orderTableItemDto.setOrderType(order.getOrderType());
orderTableItemDto.setOrderTypeLabel(order.getOrderType().getLabel());
if(order.getOrderInShippingMethod()!=null){
orderTableItemDto.setShipped(OrderShippingStatus.DELIVERED.equals(order.getOrderInShippingMethod().getStatus());
}否则{
orderTableItemDto.setShipped(假);
}
StringBuilder sb=新的StringBuilder();
某人加上(#)
.append(order.getId())
.附加(“”);
if(order.getOrderType().equals(OrderType.WEB_商店)){
某人
.append(order.getBilling().getFirstName())
.附加(“”)
.append(order.getBilling().getLastName());
}否则{
sb.append(order.getCustomer().getFullName());
}
orderTableItemDto.setTitle(sb.toString());
返回orderTableItemDto;
}).collect(Collectors.toList());
返回新的CollectionWrapper(结果,orders.getTotalElements());

我被告知这是一种糟糕的方法,我应该使用投影(DTO)从数据库中读取数据,因为创建实体并在之后映射它们是非常昂贵的。问题是我不知道如何将规范与DTO结合起来。为了管理复杂的动态查询,什么是最佳方法(从用户输入中筛选)使用包含嵌套DTO和大量属性的复杂DTO?

我不相信创建实体比直接使用DTO更快的说法。 在创建DTO之前,JPA仍然必须提供一个中间表示。 即使它更快或使用更少的内存,问题是:它是否相关

如果性能是此代码更改的原因,那么您应该做的第一件事是实现适当的基准测试。请参阅

您经常可以使用各种选项来创建DTO

对于派生查询(基于方法名称的查询)或基于
@Query
注释的查询,您可以使用基于类的DTO,即看起来像您的实体但可能具有较少属性的类。 或者,您可以使用只包含感兴趣的值的getter的接口。 在这两种情况下,都可以用投影替换返回类型。 看

如果您使用的是JPQL或Criteria API,则可以使用构造函数表达式。 有关示例,请参见此答案的示例:


当然,当你正在调整这个级别时,你应该考虑JPA是否浪费了性能,并将其与更直接地在数据库上工作的东西进行比较,例如:“代码> JDCBCTMADS,Querydsl或Jooq。

< P>实体,然后创建DTOS通常是浪费的,因为实体通常具有比您更多的列。真的需要。如果你在某个地方使用即时抓取,你也会进行不必要的连接。如果你有嵌套的集合或复杂的表达式想要抓取,请签出一个在JPA之上工作的库,它允许你根据实体模型映射任意结构。它允许你将投影与业务逻辑分离,即。您可以将该DTO应用于现有查询。您的用例的实体视图可以如下所示

@EntityView(OrderEntity.class)
interface OrderTableItemDto {
  @IdMapping
  Long getId();
  Date getCreatedDate();
  String getStatus();
  BigDecimal getTotalCost();
  @Mapping("orderType.label")
  String getOrderTypeLabel();
  @Mapping("CASE WHEN orderInShippingMethod.status = OrderShippingStatus.DELIVERED THEN true ELSE false END")
  boolean isShipped();
  @Mapping("CONCAT('#', id, CASE WHEN orderType = OrderType.WEB_STORE THEN CONCAT(billing.firstName, ' ', billing.lastName) ELSE customer.fullName END)")
  String getTitle();
}
通过Blaze Persistence提供的spring数据集成,您可以定义这样的存储库并直接使用结果

@Transactional(readOnly = true)
interface OrderRepository extends Repository<OrderEntity, Long> {
  Page<OrderTableItemDto> findAll(Specification<OrderEntity> specification, Pageable pageable);
}
@Transactional(readOnly=true)
OrderRepository扩展存储库的接口{
页码findAll(规格说明,可分页);
}
它将生成一个查询,选择您在
OrderTableItemDto
中映射的内容