【问题标题】:JPA criteria API query subclass propertyJPA 条件 API 查询子类属性
【发布时间】:2016-03-19 01:00:51
【问题描述】:

我想执行一个匹配特定子类属性的查询,所以我尝试使用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_TABLE并且Subject@DiscriminatorOptions(force = true) 因为其他原因(非影响)。 p>

但是生成的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%')

有没有办法使用标准生成器生成预期的查询?

注意

  • 使用另一个 Root - q.from(Person.class)
  • 使用子查询 - q.subquery(Person.class)
  • lastName 字段移至 Subject
  • 使用原生查询
  • 使用实体图

不可接受

我对可以在 WHERE 子句中直接声明和使用的东西感兴趣(仅由 CriteriaBuilder 和/或单个 Root ,就像treat() 子句一样),如果它确实存在的话。

【问题讨论】:

    标签: hibernate jpa inheritance criteria criteria-api


    【解决方案1】:

    解决方案是,使用 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%'))
    

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

    【讨论】:

    • 我遇到了常见的错误:java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [inheritedTags] on this ManagedType [...]。我究竟做错了什么? (我的子类是抽象的)我真的需要答案请帮忙!
    【解决方案2】:

    更新:

    您期望的 sql - 虽然它可以使用纯 jpa crtieria api 生成 - 但它不会工作并且会抛出异常,因为您(休眠)无法实例化抽象类主题。

    原因: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;
    

    然后 Contact 是一个抽象类,客户是以下子类:

    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 ?
        )
    

    【讨论】:

    • 您访问的是Customer_.name,与Contact_.name相同。如果您使用在 Customer 上定义的属性并导致编译错误 Path 类型中的方法 get(SingularAttribute super Contact,Y>) 不适用,它将不起作用对于参数 (SingularAttribute)
    • 查看我的更新,如果你省略了 root 的类型,则不会出现问题。
    • 您在搞乱与元模型查询翻译无关的事情......但您的更新为我指明了正确的方向。虽然不值得赏金或接受,但仍然值得几个 +1。谢谢。
    猜你喜欢
    • 2011-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多