关于Java:如何使用JPA Criteria API将不相关的实体加入

How to join unrelated entities with the JPA Criteria API

两个数据库表具有外键关系。

它们通过JPA映射到两个实体A和B,但是从这些实体中手动删除了连接列,因此在JPA世界中,类A和B不相关,并且您不能通过字段/属性从一个导航到另一个。

使用JPA Criteria API,是否可以创建将两个表连接在一起的查询?

我在互联网上找到的所有示例都使用join列来实现目标,但是如上所述,它已从代码中删除,因为大多数时候我对A和B之间的关系不感兴趣,并且我担心可能的开销 。


第一:外键关系不仅用于导航。它们主要用于确保在关系中不引入任何虚假值。它们还可以帮助数据库进行查询优化。我建议您重新考虑一下。

无论如何,要创建使用多个不相关实体的查询,您需要将它们放置为from(root)实体(就像在SQL或JPQL中那样)

1
2
3
4
5
6
7
SELECT .... FROM Link l, Training t WHERE l.attribute = t.attribute;

Root<Link> rootLink = criteriaQuery.from(Link.class);
Root<Training> rootTraining = criteriaQuery.from(Training.class);
...
criteriaQuery.where(
    criteriaBuilder.equal(rootLink.get(link_.linkAttribute), trainingLink));


正如我在本文中所解释的,从Hibernate 5.1开始,您可以在使用JPQL和HQL时加入不相关的实体:

1
2
3
4
5
6
7
8
Tuple postViewCount = entityManager.createQuery(
   "select p as post, count(pv) as page_views" +
   "from Post p" +
   "left join PageView pv on p.slug = pv.slug" +
   "where p.title = :title" +
   "group by p", Tuple.class)
.setParameter("title","Presentations")
.getSingleResult();

但是,此功能在Criteria API中不可用,因为这需要扩展API。

解决方案

虽然您可以尝试使用两个root对象并通过WHERE子句谓词模拟INNER JOIN,但是生成的SQL并不是解决此问题的最佳方法。

您应该考虑使用jOOQ,除了使您能够以任何可能的方式联接表之外,如果将生成的SQL查询传递给JPA createNativeQuery方法,则还可以获取实体。


最新JPA规范(2.1)不适用于无关实体的加入

但是,Hibernate 5.1.0+和EclipseLink 2.4.0+支持临时连接。
http://blog.anthavio.net/2016/03/join-unrelated-entities-in-jpa.html

另一种可能性是本机查询
http://www.oracle.com/technetwork/articles/vasiliev-jpql-087123.html


您可以使用Blaze-persistence的JPA Criteria实现将其转换为JPQL / HQL,并提供该功能作为JPA Criteria API的扩展。

在自述文件中查找" Blaze-Persistence JPA-Criteria模块依赖项":https://github.com/Blazebit/blaze-persistence/blob/master/README.md

然后使用BlazeCriteriaBuilder cb = BlazeCriteria.get(criteriaBuilderFactory),最后使用blazeCriteriaQuery.getCriteriaBuilder(entityManager)呈现JPA Criteria Query对象。