2015-12-13 2 views
11

Я хочу выполнить запрос, соответствующий определенному свойству подкласса, поэтому я пытаюсь использовать treat().Свойства подкласса API-запроса JPA

В этом примере я хочу:

все предметы с именем, начинающимся с «а»,
или все предметы, которые являются лицами, с фамилией на букву «а»

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 расширяет Subject, Subject является абстрактным, наследование SINGLE_TABLE, и Subject имеют @DiscriminatorOptions(force = true) для другого REAS ons (без влияния).

Но сгенерированный 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)
  • двигающейся LastName поля до Темы
  • с помощью Native запросов
  • с использованием диаграмм сущностей

не принимаются.

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

ответ

6

Решение есть, с 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%')) 

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

4

Update:

Вашей ожидая SQL - в то время как он может быть genereated чистого JPA crtieria апи - это не будет работать, и будет отбрасывать исключение вас (еще потому спящий) не может Инстанцировать абстрактную тему класса.

Вызванный: 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 ? 
    ) 
+0

Доступ к * Customer_.name *, который совпадает с * Contact_.name *. он не будет работать, если вы используете свойство, определенное на * Клиент *, и результат в ошибке компиляции * Метод get (SingularAttribute ) в типе Path не применим для аргументов (SingularAttribute ) * –

+1

См. Мое обновление, если вы опускаете тип корня, который не возникает. – user993553

+0

Вы возитесь с чем-то, что мало связано с метамодельным переводом запросов ... но ваше обновление указало мне в правильном направлении. Хотя это не стоит щедрости или принятия, все равно стоит пара +1. Благодарю. –

Смежные вопросы