使用CriteriaBuilder进行动态查询JPA 2规范

时间:2016-04-18 16:57:08

标签: java spring hibernate spring-data spring-data-jpa

我有以下表格数据:

Product Reason Qty
Pepsi   IN     10
Pepsi   Out    2
Pepsi   In     15
Pepsi   Out    5 
Coke    IN     100
Coke    Out    20
Coke    In     35
Coke    Out    25
Fanta   Out     55

我希望得到如下结果:

Product Qty
Pepsi   18
Coke    90
Fanta   -55

为此我得到了以下查询:

SELECT Product,
       SUM(CASE WHEN reason IN ('IN','REFUND') THEN Qty 
                WHEN reason IN ('OUT','WASTE') THEN -Qty
                ELSE NULL END) AS Qty
FROM stock
GROUP BY Product;

我想使用此link中使用的JPA Specification,我是JPA Specification的新手,有人可以指导我吗?

我有linklink,但我不确定解决问题的正确问题是什么?

修改

我试过下面的代码

@Override
    public Predicate toPredicate(Root<Stockdiary> root, CriteriaQuery<?> query, CriteriaBuilder builder) {

    Path reasonexpression = root.get("reason");
                    Path qtyexpression = root.get("qty");

                    List<String> inList = new ArrayList<String> ();
                    inList.add("IN");

                    List<String> outList = new ArrayList<String> ();
                    outList.add("OUT");

                    Predicate inpredicate = reasonexpression.in(inList);
                    Predicate outpredicate = reasonexpression.in(outList);

                    Expression<Number> sum = builder.sum(
                            builder.<Number>selectCase()  
                                 .when(inpredicate,qtyexpression.as(Double.class))  
                                 .when(outpredicate,builder.neg(qtyexpression).as(Double.class))  
                                 .otherwise(0));

return builder.equal(sum.as(Integer.class), 1)
}

从可分页

中调用它
Page<Stockdiary> page = stockRepository.findAll(spec,pageable);

以下查询: -

select count(stock0_.stock_id) as col_0_0_ from stock stock0_ cross join session_tbl session_tb1_ 
where stock0_.session_id=session_tb1_.session_id and 
cast(sum(case when stock0_.reason in (?) then stock0_.qty when stock0_.reason in (?) then -stock0_.qty else 101 end) as signed)=1 
group by stock0_.reason , stock0_.products_id , session_tb1_.terminal_id

由于case语句在where子句之后,我得到SQL异常。我不知道为什么spring将case放在where子句之后。

我正在尝试进行动态查询,因为我无法使用静态查询。

1 个答案:

答案 0 :(得分:0)

基本上,您希望执行Native Query

特别是,您可以使用 JPA 功能SqlResultSetMapping来定义原生查询与可以保存数据的DTO对象之间的映射。

假设DTO满足您的需求:

class ProductQuantity {
   private String product;
   private Integer quantity;

   public ProductQuantity(String product, Integer quantity) {
      this.product = product;
      this.quantity = quantity;
   }

   public getProduct() { return this.product; }

   public getQuantity() { return this.quantity; }
}

以下定义可以充当本机查询的映射:

@SqlResultSetMapping(
   name = "ProductQuantityMapping",
   classes = @ConstructorResult(
         targetClass = ProductQuantity.class,
         columns = {
            @ColumnResult(name = "product", type = String.class),
            @ColumnResult(name = "qty", type = Integer.class)
         })
)

最后,查询可以调用为:

Query q = entityManager.createNativeQuery("SELECT Product,SUM(CASE WHEN reason IN ('IN','REFUND') THEN Qty WHEN reason IN ('OUT','WASTE') THEN -Qty ELSE NULL END) AS Qty FROM stock GROUP BY Product");

List<ProductQuantity> result = q.getResultList();

for(ProductQuantity pq : result) {
   System.out.println("product: " + pq.getProduct() + ", Quantity: " + pq.getQuantity());
}

有关详细信息/示例,请查看此article