如何使 CriteriaBuilder 加入自定义“开启”条件?
Posted
技术标签:
【中文标题】如何使 CriteriaBuilder 加入自定义“开启”条件?【英文标题】:How to make a CriteriaBuilder join with a custom "on" condition? 【发布时间】:2013-04-06 02:08:47 【问题描述】:我想使用 CriteriaBuilder 查询我加入 2 个表的位置。在 mysql 中,我尝试进行的查询如下所示:
SELECT * FROM order
LEFT JOIN item
ON order.id = item.order_id
AND item.type_id = 1
我想获得所有订单,如果他们有 #1 类型的项目,我想加入这个项目。但是,如果没有找到 #1 类型的项目,我仍然想获得订单。我不知道如何用 CriteriaBuilder 来做这个。我只知道如何制作:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Order> cq = cb.createQuery(Order.class);
Root<Order> order = cq.from(Order.class);
Join<Order, Item> item = order.join(Order_.itemList, JoinType.LEFT);
Join<Item, Type> type = order.join(Item_.type, JoinType.LEFT);
cq.select(order);
cq.where(cb.equal(type.get(Type_.id), 1));
这个查询被破坏了,因为它在 MySQL 中产生了类似的结果:
SELECT * FROM order
LEFT JOIN item
ON order.id = item.order_id
WHERE item.type_id = 1
结果将仅包含类型为 #1 的订单。没有的订单被排除在外。如何使用 CriteriaBuilder 创建第一个示例中的查询?
【问题讨论】:
我想获得所有订单,如果他们有一个类型为 #1 的商品,该商品应该包含在结果中您想要什么结果类型?命令?还是元组/多选?你想要什么比所有订单更多?请解释 对不起,我不是很清楚。我已经更正了描述。我实际上对将项目作为结果的一部分并不感兴趣。我只想要我的结果中的订单。基本上,我想根据特定类型的项目对结果进行排序,同时仍然获得没有该类型项目的订单。 使用 CriteriaBuilder 是可能的!看看我下面的回答 【参考方案1】:可以从JPA的2.1
版本开始使用on
方法Join<Z, X> on(Predicate... restrictions);
方法如下:
Root<Order> order = cq.from(Order.class);
Join<Order, Item> item = order.join(Order_.itemList, JoinType.LEFT);
item.on(cb.equal(item.get(Item_.type), 1));
【讨论】:
如果这行得通,那将是相当多的可以替换的本机查询。下次我需要加入时我会尝试一下。 我没有明白你所说的“很少有可以替换的原生查询”是什么意思!先试一试,你会发现效果很好! 我的意思是我有很多地方使用entityManager.createNativeQuery(String)
,因为CriteriaBuilder的缺点。 :-)
@Mitchapp 请帮我建立类似的查询。请看这个问题***.com/questions/46793743/…
如果我们不使用元模型类怎么办。那么如何编写这个查询@hzitoun 你能更新这个答案吗【参考方案2】:
我认为这与这个问题中提出的问题相同。看起来在 CriteriaBuilder 中是不可能的。在 Hibernate Criteria API 中是可能的,但这可能对您没有帮助。
JPA Criteria API: Multiple condition on LEFT JOIN
【讨论】:
第一个 MySQL 是我希望 CriteriaBuilder 生成的。第二个 MySQL 代表显示的 CriteriaQuery 生成的内容。如果我只是将or item.id is null
添加到第二个 MySQL 查询中,我将得不到我想要的。这样做会排除 type_id 不同于 1 的商品的订单,并且只返回类型为 1 的商品或根本没有商品的订单。
谢谢。我可能会在较小的范围内尝试一下,但在我当前的项目中,它可能对我没有帮助。现在我在 EclipseLink (JPA 2.0) 上。据我所知 (link),我需要的功能要到 JPA 2.1 才能使用。希望很快。
使用 CriteriaBuilder 是可能的!看看我在你下面的答案【参考方案3】:
我知道这个问题已经提出了很长时间,但最近遇到了同样的问题,我从 Oracle 论坛找到了这个解决方案,我复制并粘贴了以防链接不再可用。
MiguelChillitupaArmijos 29-abr-2011 1:41 (en respuesta a 840578) Think 你应该使用类似的东西:
em.createQuery("SELECT DISTINCT e.Id" +
" from Email e " +
" left join e.idEmailIn e2 *with* e2.responseType = 'response'" +
" where e.type = 'in' and e.responseMandatory = true").getSingleResult();
这是链接。
JPA Criteria : LEFT JOIN with an AND condition
【讨论】:
【参考方案4】:如果您将 Hibernate 3.6 与 JPA 2.0 一起使用,则有一个解决方法 这不是更好的解决方案,但它对我来说是完美的。
我已经用@Where hibernate注解复制了实体。这意味着每次你使用这个实体的连接时,hibernate都会在生成的SQL的连接语句上添加额外的条件。
例如,最初我们有以下示例:
@Entity
@Table(name = "PERSON")
public class Person
@Id
@Column(name = "PERSON_ID")
private Long id;
@Id
@Column(name = "PERSON_NAME")
private String name;
@OneToMany(mappedBy = "person", fetch = FetchType.LAZY)
private Set<Address> addresses;
@Entity
@Table(name = "ADDRESS")
public class Address
@Id
@Column(name = "ADDRESS_ID")
private Long id;
@Id
@Column(name = "ADDRESS_STREET")
private String street;
@ManyToOne
@JoinColumn(name = "PERSON_ID")
private Person person;
为了在条件加入上添加额外的条件,我们需要复制地址@Entity映射,添加@Where注解@Where(clause = "ADDRESS_TYPE_ID = 2")。
@Entity
@Table(name = "ADDRESS")
@Where(clause = " ADDRESS_TYPE_ID = 2")
public class ShippingAddress
@Id
@Column(name = "ADDRESS_ID")
private Long id;
@Id
@Column(name = "ADDRESS_STREET")
private String street;
@OneToOne
@JoinColumn(name = "PERSON_ID")
private Person person;
另外,我们需要为新实体添加重复映射关联。
@Entity
@Table(name = "PERSON")
public class Person
@Id
@Column(name = "PERSON_ID")
private Long id;
@Id
@Column(name = "PERSON_NAME")
private String name;
@OneToMany(mappedBy = "person", fetch = FetchType.LAZY)
private Set<Address> addresses;
@OneToOne(mappedBy = "person")
private ShippingAddress shippingAddress;
最后,您可以在条件中使用与此特定实体的联接:
PersonRoot.join(Person_.shippingAddress, JoinType.LEFT);
Hibernate Snippet SQL 应该如下所示:
left outer join
address shippingadd13_
on person11_.person_id=shippingadd13_.person_id
and (
shippingadd13_.ADDRESS_TYPE_ID = 2
)
【讨论】:
有什么方法可以参数化@Where(clause = " ADDRESS_TYPE_ID = 2")
?【参考方案5】:
在 Hibernate 4.3 版本中支持 ON 子句,任何人都知道在使用 ON 子句进行外连接时,附加自定义条件的参数索引与现有映射过滤器的索引之间是否存在参数索引问题?
以下面的 Person 实体类为例,假设我添加了这个过滤器来限制地址类型,并且启用了过滤器来填充 IN 子句。当我在 ON 子句中添加附加条件(例如使用 'street' 列)部分时,IN 子句的参数索引将导致问题 [2]。是已知问题吗?
[1] @Filter(name = "addressTypes", condition = "ADDRESS_TYPE in (:supportedTypes)")
[2] 原因:错误 22018:BIGINT 类型的字符串格式无效。 私有设置地址;
【讨论】:
以上是关于如何使 CriteriaBuilder 加入自定义“开启”条件?的主要内容,如果未能解决你的问题,请参考以下文章
Hibernate CriteriaBuilder 加入多个表
Jpa 重写方言dialect 使用oracle / mysql 数据库自定义函数
使用 CriteriaBuilder 的 JPA 计数(*)。如何使用 CriteriaBuilder 检索 count(*) 列