Свойство подкласса 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 ?
)