CriteriaBuilder.isEmpty 关于 ElementCollection 与 JPQL 方法 [英] CriteriaBuilder.isEmpty on an ElementCollection vs. JPQL approach

查看:25
本文介绍了CriteriaBuilder.isEmpty 关于 ElementCollection 与 JPQL 方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 JPA2 标准 API 对以下类进行简单查询:

I'm trying to do a simple query using the JPA2 criteria API on the following class(es):

// a lot of imports

@Entity
public class Thing {
    enum Type { FIRST, SECOND, THIRD };

    @SequenceGenerator(name = "Thing_SeqGen", sequenceName = "Thing_Id_Seq", initialValue = 1000)
    @Id
    @GeneratedValue(generator = "Thing_SeqGen")
    private int id;

    private String name = "name";

    @Enumerated(EnumType.STRING)
    @ElementCollection(targetClass = Thing.Type.class)
    @CollectionTable(name = "TYPES", joinColumns = { @JoinColumn(referencedColumnName = "ID", name = "TYPE_ID") })

    private Set<Thing.Type> typeSet = new HashSet<Thing.Type>();
    public static void main(final String[] args) {
        new Thing().start();
    }

    public void start() {
        final Thing firstThing = new Thing();
        firstThing.setName("First one");
        firstThing.setTypeSet(EnumSet.of(Thing.Type.FIRST));
        final Thing firstAndSecondThing = new Thing();
        firstAndSecondThing.setName("Test2");
        firstAndSecondThing.setTypeSet(EnumSet.of(Thing.Type.FIRST, Thing.Type.SECOND));
        final Thing bareThing = new Thing();
        bareThing.setName("Test3");

        final EntityManagerFactory emf =  Persistence.createEntityManagerFactory("sandbox");
        final EntityManager em = emf.createEntityManager();

        em.getTransaction().begin();
        em.persist(firstThing);
        em.persist(firstAndSecondThing);
        em.persist(bareThing);
        em.getTransaction().commit();

        em.getTransaction().begin();
        final CriteriaBuilder cb = em.getCriteriaBuilder();
        final CriteriaQuery<Thing> c = cb.createQuery(Thing.class);
        final Root<Thing> root = c.from(Thing.class);
        final Join<Thing, Set<Thing.Type>> typeJoin = root.join("typeSet");

        c.select(root).distinct(true).where(cb.isEmpty(typeJoin));

        final List<Thing> results = em.createQuery(c).getResultList();

        em.getTransaction().commit();
    }

    // getter/setter methods omitted
}

我要查询的内容:查找所有没有排版的内容.

What I want to query: Find all things which has no typeset.

完成这项工作的 JPQL 是:

The JPQL which does the job is:

从 Thing t 中选择 t,其中 t.typeSet 为空

JPQL 查询返回一个预期结果.条件查询不返回任何结果.CriteriaBuilder 创建的:

The JPQL query returns one result which is expected. The criteria query returns no results. The CriteriaBuilder created:

SELECT DISTINCT t0.ID, t0.NAME FROM THING t0, TYPES t1 WHERE (((SELECT COUNT(t2.ID) FROM THING t2 WHERE (t1.TYPE_ID = t0.ID)) = 0) **AND (t1.TYPE_ID = t0.ID)**)

最后一个 theta-join(标记为 **)将其全部杀死.而且我不知道为什么表 THING 被指定两次 (THING to, THING t1).

The last theta-join (marked **) kills it all. And I have no idea why the table THING is specified twice (THING to, THING t1).

显然我做错了.但我不知道哪里出了问题.

Obviously I'm doing wrong. But I have no clue what's the fault.

推荐答案

我想问题在于您试图在 Criteria 案例中进行显式连接,而在 JPQL 中则没有.所以省略加入并做类似的事情

I'd guess the problem is that you're trying to do an explicit join in the Criteria case, whereas in the JPQL you don't. So omit the join and do something like

Metamodel model = emf.getMetamodel();
ManagedType thingType = model.managedType(Thing.class);
CollectionAttribute typeSetAttr = thingType.getCollection("typeSet");
c.select(root).distinct(true).where(cb.isEmpty(root.get(typeSetAttr)));

然后这应该转换为与您发布的相同的 JPQL ......或者至少它适用于 DataNucleus JPA 实现.

This should then translate into the same JPQL as you posted ... or at least it does for DataNucleus JPA implementation.

这篇关于CriteriaBuilder.isEmpty 关于 ElementCollection 与 JPQL 方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆