问题 JPA条件API查询子类属性


我想执行一个匹配特定子类属性的查询,所以我正在尝试使用 treat()

在这个例子中我想要:

名称以'a'开头的所有科目,
或者所有科目,都是人,姓氏以'a'开头

private List<Subject> q1()
{
    CriteriaBuilder b = em.getCriteriaBuilder();

    CriteriaQuery<Subject> q = b.createQuery(Subject.class);
    Root<Subject> r = q.from(Subject.class);
    q.select(r);
    q.distinct(true);
    q.where(
        b.or(
            b.like(r.get(Subject_.name), "a%"),
            b.like(b.treat(r, Person.class).get(Person_.lastName), "a%")));

    return em.createQuery(q).getResultList();
}

明显, Person 扩展 SubjectSubject 是抽象的,继承是 SINGLE_TABLESubject 具有 @DiscriminatorOptions(force = true) 出于其他原因 (非进水)。

但生成的SQL是这样的:

select distinct subject0_.ID as ID2_71_, subject0_.CODE as CODE3_71_, ...
from SUBJECT subject0_ 
where subject0_.DTYPE='Person' and (subject0_.name like 'a%' or subject0_.lastName like 'a%')

虽然我期待:

select distinct subject0_.ID as ID2_71_, subject0_.CODE as CODE3_71_, ...
from SUBJECT subject0_ 
where subject0_.name like 'a%' or (subject0_.DTYPE='Person' and subject0_.lastName like 'a%')

有没有办法使用条件构建器生成预期的查询?

注意

  • 使用另一个   - q.from(Person.class)
  • 使用子查询 - q.subquery(Person.class)
  • 移动  领域到 学科
  • 使用本机查询
  • 使用实体图

是不可接受的

我对可以直接声明和使用的东西感兴趣 哪里 条款(只生产 CriteriaBuilder 和/或单身 ,就像 treat() 条款),如果确实存在。


13000
2017-12-13 14:02


起源



答案:


解决方案是,使用Hibernate并在此特定场景中,非常简单:

private List<Subject> q1()
{
    CriteriaBuilder b = em.getCriteriaBuilder();

    CriteriaQuery<Subject> q = b.createQuery(Subject.class);
    Root<Subject> r = q.from(Subject.class);
    q.select(r);
    q.distinct(true);
    q.where(
        b.or(
            b.like(r.get(Subject_.name), "a%"),
            b.and(
                b.equal(r.type(), Person.class),
                b.like(((Root<Person>) (Root<?>) r).get(Person_.lastName), "a%"))));

    return em.createQuery(q).getResultList();
}

请注意 双重演员,它避免了编译错误,并允许在同一个表上执行查询子句。这会产生:

select distinct subject0_.ID as ID2_71_, subject0_.CODE as CODE3_71_, ...
from SUBJECT subject0_ 
where subject0_.DTYPE in ('Office', 'Team', 'Role', 'Person', ...) 
    and (subject0_.name like 'a%' 
        or subject0_.DTYPE='Person' and (subject0_.lastName like 'a%'))

无需更改模型或其他任何内容。


6
2017-12-21 08:25





更新:

你期待的sql - 虽然它可以用纯jpa crtieria api生成 - 它不会起作用并且会抛出一个异常,因为你(hibernate)不能实例化抽象类主题。

引起:org.hibernate.InstantiationException:无法实例化   抽象类或接口:

如果这个类不是抽象的,那就有效。喜欢这个:

CriteriaBuilder b = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Contact> q;
q = b.createQuery(Contact.class);
Root r = q.from(Contact.class);

   q.select(r);
   q.distinct(true);
q.where(
        b.or(
                b.like(r.get(Contact_.name),"t%"),
                b.and(
                        b.equal(r.get(Contact_.contact_type),"customer"),
                        b.like(r.get(Customer_.lastName),"t%")
                )
        )
);

return getEntityManager().createQuery(q).getResultList();

(我的客户是您的人,我的主题类是联系人类)


以只读方式访问鉴别器列可能是一种有效的解决方法。 如果discriminator_column是contact_type,请执行以下操作:

@Column(name = "contact_type",insertable = false,updatable = false)
@XmlTransient
private String contact_type;

接触是一个抽象类,客户作为子类,如下:

CriteriaBuilder b = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Contact> q = b.createQuery(Contact.class);
           Root<Contact>  r = q.from(Contact.class);
           q.distinct(true);

           q.where(
                b.or(
                        b.like(r.get(Contact_.name), "t%"),
                        b.and(
                                b.equal(r.get(Customer_.contact_type), "customer"),
                                b.like(r.get(Customer_.name), "%t")
                        )
                )
                );
           return getEntityManager().createQuery(q).getResultList();

产生

select

distinct contact0_.id as id2_1_,
    contact0_.contact_type as contact_1_1_,
    contact0_.name as name3_1_ 
from
    Contact contact0_ 
where
    contact0_.name like ? 
    or contact0_.contact_type=? 
    and (
        contact0_.name like ?
    )

4
2017-12-16 22:50



你正在访问 顾客姓名,这是一样的 联系人姓名。如果使用定义的属性,它将无法工作 顾客 并导致编译错误 Path <Contact>类型中的get(SingularAttribute <?super Contact,Y>)方法不适用于参数(SingularAttribute <Customer,String>) - Michele Mariotti
如果您省略了未出现问题的root类型,请参阅我的更新。 - user993553
你搞乱了与元模型查询翻译没什么关系的东西......但你的更新指出了我正确的方向。虽然不值得赏金或接受,但它仍然值得一两个+1。谢谢。 - Michele Mariotti


答案:


解决方案是,使用Hibernate并在此特定场景中,非常简单:

private List<Subject> q1()
{
    CriteriaBuilder b = em.getCriteriaBuilder();

    CriteriaQuery<Subject> q = b.createQuery(Subject.class);
    Root<Subject> r = q.from(Subject.class);
    q.select(r);
    q.distinct(true);
    q.where(
        b.or(
            b.like(r.get(Subject_.name), "a%"),
            b.and(
                b.equal(r.type(), Person.class),
                b.like(((Root<Person>) (Root<?>) r).get(Person_.lastName), "a%"))));

    return em.createQuery(q).getResultList();
}

请注意 双重演员,它避免了编译错误,并允许在同一个表上执行查询子句。这会产生:

select distinct subject0_.ID as ID2_71_, subject0_.CODE as CODE3_71_, ...
from SUBJECT subject0_ 
where subject0_.DTYPE in ('Office', 'Team', 'Role', 'Person', ...) 
    and (subject0_.name like 'a%' 
        or subject0_.DTYPE='Person' and (subject0_.lastName like 'a%'))

无需更改模型或其他任何内容。


6
2017-12-21 08:25





更新:

你期待的sql - 虽然它可以用纯jpa crtieria api生成 - 它不会起作用并且会抛出一个异常,因为你(hibernate)不能实例化抽象类主题。

引起:org.hibernate.InstantiationException:无法实例化   抽象类或接口:

如果这个类不是抽象的,那就有效。喜欢这个:

CriteriaBuilder b = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Contact> q;
q = b.createQuery(Contact.class);
Root r = q.from(Contact.class);

   q.select(r);
   q.distinct(true);
q.where(
        b.or(
                b.like(r.get(Contact_.name),"t%"),
                b.and(
                        b.equal(r.get(Contact_.contact_type),"customer"),
                        b.like(r.get(Customer_.lastName),"t%")
                )
        )
);

return getEntityManager().createQuery(q).getResultList();

(我的客户是您的人,我的主题类是联系人类)


以只读方式访问鉴别器列可能是一种有效的解决方法。 如果discriminator_column是contact_type,请执行以下操作:

@Column(name = "contact_type",insertable = false,updatable = false)
@XmlTransient
private String contact_type;

接触是一个抽象类,客户作为子类,如下:

CriteriaBuilder b = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Contact> q = b.createQuery(Contact.class);
           Root<Contact>  r = q.from(Contact.class);
           q.distinct(true);

           q.where(
                b.or(
                        b.like(r.get(Contact_.name), "t%"),
                        b.and(
                                b.equal(r.get(Customer_.contact_type), "customer"),
                                b.like(r.get(Customer_.name), "%t")
                        )
                )
                );
           return getEntityManager().createQuery(q).getResultList();

产生

select

distinct contact0_.id as id2_1_,
    contact0_.contact_type as contact_1_1_,
    contact0_.name as name3_1_ 
from
    Contact contact0_ 
where
    contact0_.name like ? 
    or contact0_.contact_type=? 
    and (
        contact0_.name like ?
    )

4
2017-12-16 22:50



你正在访问 顾客姓名,这是一样的 联系人姓名。如果使用定义的属性,它将无法工作 顾客 并导致编译错误 Path <Contact>类型中的get(SingularAttribute <?super Contact,Y>)方法不适用于参数(SingularAttribute <Customer,String>) - Michele Mariotti
如果您省略了未出现问题的root类型,请参阅我的更新。 - user993553
你搞乱了与元模型查询翻译没什么关系的东西......但你的更新指出了我正确的方向。虽然不值得赏金或接受,但它仍然值得一两个+1。谢谢。 - Michele Mariotti