Свойство подкласса API запросов API JPA

Я хочу выполнить запрос, соответствующий определенному свойству подкласса, поэтому я пытаюсь использовать 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 extends Subject, Subject является абстрактным, наследование SINGLE_TABLE, и Subject имеет @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%')

Есть ли способ создать ожидаемый запрос с помощью построителя критериев?

Обратите внимание, что

  • с использованием другого Root - q.from(Person.class)
  • с помощью подзапросов - q.subquery(Person.class)
  • перемещение поля lastName до темы
  • с использованием собственных запросов
  • с использованием диаграмм сущностей

неприемлемы.

Мне интересно то, что может быть объявлено и использовано непосредственно в предложении WHERE (, выпущенном только из CriteriaBuilder и/или одиночного Root, как и предложение treat()), если оно существует.

Ответ 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%'))

Нет необходимости менять модель или что-то еще.

Ответ 2

Update:

Ваш ожидающий sql - в то время как он может быть genereated с чистым 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();

(Мой клиент - ваш человек, а мой предметный класс - это класс контактов)


Доступ к столбцу дискриминатора как к readonly может быть рабочим решением для вас. если discinator_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 ?
    )