Blaze 持久性标准查询:分页结果按标识符排序问题

问题描述 投票:0回答:1

我正在开发一个 Spring Boot 应用程序,使用 Blaze Persistence 构建一个复杂的条件查询来获取分页结果。但是,我遇到一个错误,指出:“查询生成器的项目顺序不能保证生成唯一的元组!还请考虑按实体标识符排序!”

以下是我的实体和我正在使用的条件查询的详细信息:

@Getter
@Setter
@Entity
@Table(name = "orders")
public class OrderEntity {
@Id
private Long orderId;
private String orderCode;

    @OneToMany(mappedBy = "orderEntity", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<OrderAttribute> attributes = new ArrayList<>();
    
    @OneToMany(mappedBy = "orderEntity", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<OrderAction> actions = new ArrayList<>();
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "issue_type_id", nullable = false)
    private IssueType issueType;

}

@Getter
@Setter
@Entity
public class OrderAction {
    @Id
    private Long actionId;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id", nullable = false)
    private OrderEntity orderEntity;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "current_location_id", nullable = false)
    private Location currentLocation;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "next_location_id", nullable = false)
    private Location nextLocation;
}

@Getter
@Setter
@Entity
public class OrderAttribute {
    @Id
    private Long attributeId;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id", nullable = false)
    private OrderEntity orderEntity;
    
    private String key;
    private String value;
}

@Getter
@Setter
@Entity
public class IssueType {
    @Id
    private Long typeId;
    
    @Column(nullable = false, unique = true)
    private String type;
}


@Getter
@Setter
@Entity
public class Location {
    @Id
    private Integer locationId;
    private String locationCode;
    private String name;
    private Integer parentLocationId;
}

@Component
public class OrderRepositoryCustomImpl implements OrderRepositoryCustom {

    @PersistenceContext
    private EntityManager em;
    private final CriteriaBuilderFactory cbf;

    public OrderRepositoryCustomImpl(EntityManager em, CriteriaBuilderFactory cbf) {
        this.em = em;
        this.cbf = cbf;
    }

    @Override
    public Page<OrderListProjection> search(SearchRequest searchRequest, Pageable pageable) {
        CriteriaBuilder<Tuple> cb = cbf.create(em, Tuple.class)
                .from(OrderEntity.class, "o")
                .select("o.orderId", "orderId")
                .select("o.orderCode", "orderCode")
                .select("loc.locationId", "nextLocationId")
                .select("loc.name", "nextLocationName")
                .select("it.type", "issueType")
                .select("oa.value", "attributeValue")
                .select("loc.parentLocationId", "parentLocationId")
                .select("loc.name", "parentLocationName")
                .select("loc.locationCode", "locationCode")
                .innerJoin("o.issueType", "it")
                .leftJoinLateralOnEntitySubquery("o.actions", "topAction", "action")
                .orderByDesc("action.actionId")
                .setMaxResults(1)
                .end()
                .on("1").eqExpression("1")
                .end()
                .leftJoin("topAction.nextLocation", "loc")
                .leftJoinOnEntitySubquery("o", OrderAttribute.class, "oa")
                .end()
                .on("oa.orderEntity").eqExpression("o")
                .on("oa.key").eq("attributeKey").end();


        if (searchRequest.getIssueType() != null) {
            cb.where("it.type").eq(searchRequest.getIssueType());
        }
        if (searchRequest.getParentLocationId() != null) {
            cb.where("loc.parentLocationId").eq(searchRequest.getParentLocationId());
        }
        if (searchRequest.getAttributeValue() != null) {
            cb.where("oa.value").eq(searchRequest.getAttributeValue());
        }

        PaginatedCriteriaBuilder<Tuple> builder = cb.page((int) pageable.getOffset(), pageable.getPageSize());

        pageable.getSort().forEach(order -> {
            String property = order.getProperty();
            if (order.isAscending()) {
                builder.orderByAsc("o." + property);
            } else {
                builder.orderByDesc("o." + property);
            }
        });

        builder.orderByAsc("o.orderId");

        PagedList<Tuple> resultList = builder.withKeysetExtraction(true).getResultList();

        List<OrderListProjection> list = resultList.stream()
                .map(tuple -> new OrderListProjection(
                        tuple.get("orderId", Long.class),
                        tuple.get("orderCode", String.class),
                        tuple.get("nextLocationId", Integer.class),
                        tuple.get("nextLocationName", String.class),
                        tuple.get("issueType", String.class),
                        tuple.get("attributeValue", String.class),
                        tuple.get("parentLocationId", Integer.class),
                        tuple.get("parentLocationName", String.class),
                        tuple.get("locationCode", String.class)))
                .toList();

        return new PageImpl<>(list, pageable, resultList.getTotalSize());
    }
}

错误:查询生成器的项目顺序不能保证生成唯一的元组!还请考虑按实体标识符排序!

我尝试调整 order by 子句以包含实体标识符,但问题仍然存在。

pageable.getSort().forEach(order -> {
 String property = order.getProperty();
    if (order.isAscending()) {
        builder.orderByAsc("o." + property);
    } else {
        builder.orderByDesc("o." + property);
    }
});
    
builder.orderByAsc("o.orderId");
    

我尝试删除上面的代码,问题仍然存在。

我希望查询执行时不会出现错误并返回唯一的元组进行分页。

如何解决在 Blaze 持久性条件查询中按多个字段排序时确保元组唯一的问题?任何关于我所缺少的内容或如何调整我的查询的指导将不胜感激。

spring-boot hibernate pagination criteriaquery blaze-persistence
1个回答
0
投票

您必须使用

pageBy
方法并告诉 Blaze-Persistence 您想要分页的 id 属性。由于您的查询不是直接获取实体,因此 Blaze-Persistence 本身无法解决这个问题。

虽然您正在使用投影,为什么不使用 Blaze-Persistence Entity-Views 呢?它还与 Spring Data 进行了很好的集成,可以处理所有分页细节。您只需传递 Spring 数据

Pageable

© www.soinside.com 2019 - 2024. All rights reserved.