JPA Criteria API - 如何添加JOIN子句

57

我正在尝试动态地构建查询,接下来的目标是添加 JOIN 子句(我不知道如何使用API)。

到目前为止,例如,这段代码对我有效:

...
Class baseClass;   
...
CriteriaBuilder cb = JpaHandle.get().getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(this.baseClass);
Root entity_ = cq.from(this.baseClass); 
Predicate restrictions = null;
...
restrictions = cb.conjunction();
restrictions = cb.and(restrictions, entity_.get("id").in(this.listId));
...
cq.where(restrictions);
...
Query qry = JpaHandle.get().createQuery(cq);

(注意:JpaHandle来自于wicket-JPA实现)

我的愿望是尽可能通用地添加JOIN子句!

我在这些类中有特定的注释(this.baseClass)。

例如:

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "assay_id", nullable = false)

那么,在标准的JPA中是否有类似这样的方式?(注意:此代码无法编译)

以下是一个实用失败的方法:

...
Join<Experiment,Assay> experimentAssays = entity_.join( entity_.get("assay_id") );

或者像这样:

...
CriteriaQuery<Customer> q = cb.createQuery(Customer.class);
Root<Customer> c = q.from(Customer.class);
SetJoin<Customer, PurchaseOrder> o = c.join(Customer_.orders);

对我来说,如果能尽可能地更加通用就好了... :

...
Join joinClause = entity_join(entity_.get("assay_id"), entity2_.get("id"));

当然,我在类中有特定的注释(this.baseClass)

感谢您的时间。我将欣赏所有的评论!


1
你能提供你的带注释的领域类或其他示例类吗? - Timo Westkämper
4个回答

64

也许下面摘自Java EE 6教程的第23章-使用Criteria API创建查询可以帮助你理解(实际上,我建议阅读整个第23章):

Querying Relationships Using Joins

For queries that navigate to related entity classes, the query must define a join to the related entity by calling one of the From.join methods on the query root object, or another join object. The join methods are similar to the JOIN keyword in JPQL.

The target of the join uses the Metamodel class of type EntityType<T> to specify the persistent field or property of the joined entity.

The join methods return an object of type Join<X, Y>, where X is the source entity and Y is the target of the join.

Example 23-10 Joining a Query

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Metamodel m = em.getMetamodel();
EntityType<Pet> Pet_ = m.entity(Pet.class);

Root<Pet> pet = cq.from(Pet.class);
Join<Pet, Owner> owner = pet.join(Pet_.owners);

Joins can be chained together to navigate to related entities of the target entity without having to create a Join<X, Y> instance for each join.

Example 23-11 Chaining Joins Together in a Query

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Metamodel m = em.getMetamodel();
EntityType<Pet> Pet_ = m.entity(Pet.class);
EntityType<Owner> Owner_ = m.entity(Owner.class);

Root<Pet> pet = cq.from(Pet.class);
Join<Owner, Address> address = cq.join(Pet_.owners).join(Owner_.addresses);

话虽如此,我还有一些额外的评论:

首先,你代码中的以下行:

Root entity_ = cq.from(this.baseClass);

让我觉得你可能错过了静态元模型类部分。像引用示例中的Pet_这样的元模型类用于描述持久类的元信息。它们通常使用注解处理器(规范元模型类)生成,也可以由开发人员编写(非规范元模型)。但你的语法看起来很奇怪,我认为你正在尝试模仿你错过的东西。
其次,我真的认为你应该忘记这个assay_id外键,在这里你走错了路。你真的需要开始思考对象和关联,而不是表和列。
第三,我不太确定你究竟是什么意思,要添加一个尽可能通用的JOIN子句以及你的对象模型是什么样子,因为你没有提供它(见上一点)。因此,无法更精确地回答你的问题。
总之,我认为你需要多读一些关于JPA 2.0标准和元模型API的内容,我强烈推荐以下资源作为起点。
另请参阅

相关问题


2
"Join<Owner, Address> address = cq.join(Pet_.owners).join(Owner_.addresses);" 哇哦,这真的可以工作吗?或者说它曾经可以工作过?CriteriaQuery甚至没有“join”方法。天啊! - Alkanshel
3
你好,是否有可能加入一个条件,比如: address.on(cb.greatest(addresses.get("id"))); - Se Song
为什么这个问题有这么多赞,查询对象上没有“join”! - JPS
源教程已使用正确的语法进行更新(例如Root.join()而不是CriteriaQuery.join()等):https://javaee.github.io/tutorial/persistence-criteria003.html#GJIUV - Matthew Read

26

实际上,如果您的注释正确,就不必处理静态元模型。

以下是相关实体:

@Entity
public class Pet {
  @Id
  protected Long id;
  protected String name;
  protected String color;
  @ManyToOne
  protected Set<Owner> owners;
}

@Entity
public class Owner {
  @Id
  protected Long id;
  protected String name;
}

你可以使用这个:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Metamodel m = em.getMetamodel();
EntityType<Pet> petMetaModel = m.entity(Pet.class);

Root<Pet> pet = cq.from(Pet.class);
Join<Pet, Owner> owner = pet.join(petMetaModel.getSet("owners", Owner.class));

3
你能给出一个伴随这个的“正确”注释的例子吗? - slashdottir
1
难道不应该是@OneToMany吗? - Kajzer

8
警告!Sun JPA 2示例中存在许多错误,以及Pascal答案中粘贴的内容。请参考此帖子。这篇文章和Sun Java EE 6 JPA 2示例真正阻碍了我对JPA 2的理解。在翻阅Hibernate和OpenJPA手册,并且认为我已经很好地理解了JPA 2之后,当我回到这篇文章时,我仍然感到困惑。

1
一旦你掌握了基础知识,是否有任何好的教程或文档可以推荐?相比Sun的教程,《Pro JPA 2:掌握Java持久性API》要好得多,但是它并没有深入到足够详细的层面。我在使用Criteria API创建复杂连接方面遇到了挑战,并且没有找到充分的文档和示例支持。 - april26
@april26 - 我通过试错的方式在我的WAS 8输出控制台中启用了OpenJPA的日志记录。我能够看到Criteria API生成的SQL,并对其进行调整,直到它输出我期望看到的查询。在此之前,我翻阅了Hibernate和OpenJPA的手册(两者都很庞大,但在Criteria API方面的章节相对较小)。我记得IBM有一篇文章也谈到了这个问题。 - Chris Harris

5

您不需要学习JPA。您可以使用我为JPA2编写的easy-criteria插件 (https://sourceforge.net/projects/easy-criteria/files/)。以下是一个示例:

CriteriaComposer<Pet> petCriteria CriteriaComposer.from(Pet.class).
where(Pet_.type, EQUAL, "Cat").join(Pet_.owner).where(Ower_.name,EQUAL, "foo");

List<Pet> result = CriteriaProcessor.findAllEntiry(petCriteria);

或者

List<Tuple> result =  CriteriaProcessor.findAllTuple(petCriteria);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接