【问题标题】:JPQL: OR won't work as expectedJPQL:OR 不会按预期工作
【发布时间】:2019-01-03 04:48:44
【问题描述】:

我在存储库中定义了以下方法:

@Query("SELECT t FROM Treatment t WHERE " +
        " (t.promotionCode.promotion.id=:promotionId)  " +
        " order by t.id desc")
Page<Treatment> findByPromotionId(@Param("promotionId")Integer id, Pageable pr);

它按预期工作:我得到了一个处理列表,其中包含属于给定促销的 PromotionCode。

但后来我需要添加第二个促销代码,因此一个治疗最多可以链接到两个促销(两个促销代码可能属于同一个促销,这不是问题)。所以我尝试将新要求添加到查询中:

@Query("SELECT t FROM Treatment t WHERE " +
            " (t.promotionCode.promotion.id=:promotionId)  " +
            " OR " +
            " (t.promotionCode2.promotion.id=:promotionId)  " +
            " order by t.id desc")
Page<Treatment> findByPromotionId(@Param("promotionId")Integer id, Pageable pr);

但我不会工作。生成的SQL是

select ...
from treatment treatment0_ 
  cross join promotion_code promotionc1_ 
  cross join promotion_code promotionc2_ 
where 
  treatment0_.promotion_code_id=promotionc1_.id and
  treatment0_.promotion_code2_id=promotionc2_.id and 
  (promotionc1_.promo_id=? or promotionc2_.promo_id=?)
order by 
  treatment0_.id desc limit ?

如您所见,只要其中一个促销代码为空,则不满足条件。

一些细节,即使它们从代码中很明显:

  • 除了treatment 之外,还有一个名为promotion_code 的表和另一个名为promotion 的表。
  • 所有表都有一个数字 ID(自动递增)。
  • promotion_code_idpromotion_code2_id 是指向 promotion_code 的 FK,它也有一个指向 promotion 的 FK,并且不能为空(所有促销代码都属于促销)。

我想通过任何促销代码列查找与促销相关的所有治疗。两个字段都可能为空。

我该如何解决这个问题?

【问题讨论】:

  • 您确定您正在正确编译并且生成的 SQL 是您发布的方法吗??否则,Treatment 实体以及您如何将其与PromotionCode 关联可能存在问题。另外,如果您可以尝试将其作为本机 sql 传递并允许映射器传回 Page&lt;Treatment&gt;
  • 我对编译很有把握,因为我测试过只检查promotion_code,然后只检查promotion_code2,我在考虑UNION但JPQL似乎不支持它
  • 也许你需要手动加入推广。使用join fetchleft join fetchSee this question
  • @Patrick 谢谢你的建议,我会试试的

标签: java postgresql spring-boot jpa jpql


【解决方案1】:

您可以尝试使用条件 API。

CriteriaBuilder 接口提供了工厂方法,它们接受两个 Expression 操作数(包括 Predicate 实例)并返回一个新的 Predicate 实例:

  Predicate p1 = cb.and(isInUN, isInEU);  // Member of both UN and EU
  Predicate p2 = cb.or(isInOECD, isLarge); // Either OECD member or large

额外的工厂方法可用于不同数量的谓词:

  Predicate p3 = cb.and(p1, isLarge, cb.isTrue(isInOECD));
  Predicate p4 = cb.or(p2, cb.isTrue(isInUN), cb.isTrue(isInEU));

在上面的代码中,非 Predicate 布尔表达式使用 isTrue 方法转换为 Predicate 实例。这是必需的,因为在非二进制版本中,工厂方法只接受 Predicate 实例作为参数。

来源网址:https://www.objectdb.com/java/jpa/query/jpql/logical#Criteria_Query_Logical_Operators_

【讨论】:

  • 我的存储库只是一个接口,所以我尽量避免这种解决方案,但我会记住以防万一,谢谢!
【解决方案2】:

我试图模仿 UNION,它奏效了:

SELECT t
FROM Treatment t 
WHERE t IN (SELECT t FROM Treatment t 
      WHERE t.promotionCode.promotion.id = :promotionId)
  OR t IN (SELECT t FROM Treatment t
      WHERE t.promotionCode2.promotion.id = :promotionId)
ORDER BY t.id desc

在此之前我尝试了 LEFT JOIN FETCH 选项

SELECT t 
FROM Treatment t 
LEFT JOIN t.promotionCode.promotion as pc
LEFT JOIN t.promotionCode2.promotion as pc2
WHERE 
  (pc.id=:promotionId)  
OR (pc2.id=:promotionId)  
order by t.id desc

SELECT t 
FROM Treatment t 
LEFT JOIN t.promotionCode as pc
LEFT JOIN t.promotionCode2 as pc2
WHERE 
  (pc.promotion.id=:promotionId)  
OR (pc2.promotion.id=:promotionId)  
order by t.id desc

但他们没有工作(我得到了很长的堆栈跟踪,说在启动应用程序时查询不正确),

【讨论】:

    猜你喜欢
    • 2014-12-15
    • 1970-01-01
    • 1970-01-01
    • 2015-09-11
    • 1970-01-01
    • 2016-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多