JPA - Criteria API和EmbeddedId

37

我想使用Criteria来进行以下查询。我有一个定义了EmbeddedIdEntity

 @Entity
 @Table(name="TB_INTERFASES")
 public class Interfase implements Serializable {

  @EmbeddedId
  private InterfaseId id;
 }

 @Embeddable
 public class InterfaseId implements Serializable {
  @Column(name="CLASE")
  private String clase;
 }

我尝试执行的标准查询是:

 CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
 CriteriaQuery<Interfase> criteriaQuery = criteriaBuilder.createQuery(Interfase.class);
 Root<Interfase> entity = criteriaQuery.from(Interfase.class);
 criteriaQuery.where(
   criteriaBuilder.equal(entity.get("clase"), "Clase"),
 );

但是这会抛出一个IllegalArgumentException异常:

java.lang.IllegalArgumentException: Not an managed type: class InterfaseId

我也尝试过以下这些查询:

 Root<Interfase> entity = criteriaQuery.from(Interfase.class);
 criteriaQuery.where(
   criteriaBuilder.equal(entity.get("id").get("clase"), "Clase"),
 );

还有这一个...

 Root<Interfase> entity = criteriaQuery.from(Interfase.class);
 criteriaQuery.where(
   criteriaBuilder.equal(entity.get("id.clase", "Clase"),
 );

我一直没有好的运气。 所以我的问题是,当我的类使用Embedded和EmbeddedId注释时,如何使用条件进行查询?

谢谢! Mauro。

3个回答

67
你需要使用路径导航来访问Embeddable的属性。这里有一个来自JPA 2.0规范(使用静态元模型)的示例:

6.5.5 路径导航

...

在下面的示例中, ContactInfo是一个嵌入式类,包含一个地址和一组电话号码。 Phone是一个实体。

CriteriaQuery<Vendor> q = cb.createQuery(Vendor.class);
Root<Employee> emp = q.from(Employee.class);
Join<ContactInfo, Phone> phone =
    emp.join(Employee_.contactInfo).join(ContactInfo_.phones);
q.where(cb.equal(emp.get(Employee_.contactInfo)
                    .get(ContactInfo_.address)
                    .get(Address_.zipcode), "95054"))
    .select(phone.get(Phone_.vendor));
以下是等效的Java Persistence查询语言查询:
SELECT p.vendor
FROM Employee e JOIN e.contactInfo.phones p
WHERE e.contactInfo.address.zipcode = '95054'

所以在您的情况下,我认为您需要这样做:

criteriaBuilder.equal(entity.get("id").get("clase"), "Referencia 111")

参考资料

  • JPA 2.0规范
    • 第6.5.5节 "路径导航"

更新: 我已经使用Hibernate EntityManager 3.5.6和以下查询测试了提供的实体:

CriteriaBuilder builder = em.getCriteriaBuilder();

CriteriaQuery<Interfase> criteria = builder.createQuery(Interfase.class);
Root<Interfase> interfaseRoot = criteria.from(Interfase.class);
criteria.select(interfaseRoot);
criteria.where(builder.equal(interfaseRoot.get("id").get("clase"), 
    "Referencia 111"));

List<Interfase> interfases = em.createQuery(criteria).getResultList();

代码运行正常,生成以下SQL:

17:20:26.893 [main] DEBUG org.hibernate.SQL - 
    select
        interfase0_.CLASE as CLASE31_ 
    from
        TB_INTERFASES interfase0_ 
    where
        interfase0_.CLASE=?
17:20:26.895 [main] TRACE org.hibernate.type.StringType - binding 'Referencia 111' to parameter: 1

符合预期。


嗨 Pascal,我尝试过了,但没有成功。正如我在帖子中所说的那样,当我执行entity.get("id")时,会出现IllegalArgumentException异常。有什么建议吗? - Mauro Monti
@Mauro,你在问题中没有提到。你使用的是哪个JPA实现? - Pascal Thivent
@Pascal,再次感谢您的回复...我正在使用Hibernate版本3.5.0-Beta2。 异常说明InterfaseId是一个未受管理的实体。 具体来说,IllegalArgumentException: Not an managed type: class InterfaseId - Mauro Monti
@Mauro 我的观点是,在你的问题中没有写到你尝试过 entity.get("id"),读者必须猜测你尝试了什么,这不好。无论如何,我会用另一种实现方式来尝试你的映射。 - Pascal Thivent
@Pascal,非常感谢您的回答,它们总是非常有帮助! - Oleg
显示剩余3条评论

0
尝试将metamodel类复制并粘贴到实体保存的相同文件夹中(在NetBeans 8.2中,它们会自动生成,并带有与实体相同的名称,但结尾带有下划线,应该类似于Interfase_InterfaseId_)。
强制导入metamodelInterfase_InterfaseId_,并引用所需字段。
CriteriaBuilder builder = em.getCriteriaBuilder();

CriteriaQuery<Interfase> criteria = builder.createQuery(Interfase.class);
Root<Interfase> interfaseRoot = criteria.from(Interfase.class);
criteria.select(interfaseRoot);
criteria.where(builder.equal(interfaseRoot.get(Interfase_.id).get(InterfaseId_.clase),"Referencia 111"));
List<Interfase> interfases = em.createQuery(criteria).getResultList();

0

这是一个老问题,但无论如何...

另一个极其简单的解决方案是

InterfaseId id = new InterfaseId();
id.setClase("Clase");
Interfase found = em.find(id);

除非你试图做一些与你所要求的不同的事情,否则这是“理智”的方法。对id进行查询将返回最多一个结果。


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