TypedQuery вместо обычного запроса в JPA

Можно ли написать этот запрос как TypedQuery и позволить двум длинным полям работать в объекте с двумя общедоступными полями внутри.

    Query q = em.createQuery(
            "SELECT c.id, COUNT(t.id) " +
            "FROM PubText t " +
            "JOIN t.comm c " +
            "WHERE c.element = ?1 " +
            "GROUP BY c.id");
    q.setParameter(1, e);
    List<?> rl = q.getResultList();
    Iterator<?> it = rl.iterator();
    HashMap<Long, Long> res = new HashMap<Long, Long>();
    while (it.hasNext()) {
        Object[] n = (Object[]) it.next();
        res.put((Long)n[0], (Long)n[1]);
    }
    return res;

person Hasan Tuncay    schedule 14.03.2013    source источник


Ответы (2)


В JPA есть особенность именно для этого - выражения конструктора:

Query q = entityManager.createQuery("SELECT NEW com.example.DTO( c.id, COUNT(t.id)) FROM ...");
List<DTO> dtos = q.getResultList();

Ваш класс DTO может быть POJO. Все, что ему потребуется, это открытый конструктор, принимающий 2 Longs. Обратите внимание, что вы должны указать полное имя вашего класса после NEWoperator.

person kostja    schedule 14.03.2013
comment
привет @kostja, я получаю сообщение об ошибке (невозможно создать TypedQuery для запроса с более чем одним возвратом). Мой SQL выглядит так: SELECT NEW com.company.ui.EntityIDKey(c.companyId, c.name) FROM Company c WHERE c.companyId is not null and c.name is not null and length(trim(c.name)) > 0 order by c.name asc. Я использую TypedQuery следующим образом: List<EntityIDKey> companies = getEntityManager().createQuery(sql, EntityIDKey.class).getResultList(); - person Web User; 05.11.2014
comment
это может иметь место, если ваш EntityIDKey не является объектом. Провайдер может не поддерживать такие запросы. Вы пробовали использовать обычный запрос? - person kostja; 06.11.2014
comment
это правда, EntityIDKey не является сущностью. Я использую провайдер Hibernate и почему-то предполагал, что он будет работать. Обычный запрос, который создает набор Company, отлично работает. - person Web User; 06.11.2014
comment
Вроде, как бы, что-то вроде. Я был бы счастливее, если бы предупреждения о безопасности типов можно было избежать, потому что q.getResultList() возвращает список универсальных типов. - person Web User; 06.11.2014
comment
Ах да, к сожалению, единственный способ избавиться от них, который я знаю, - это сообщить Java, что вы действительно знаете, что делаете, используя @SuppressWarnings("unchecked"). Возможно, есть более безопасный способ использования Criteria API, но я недостаточно знаком с ним. - person kostja; 06.11.2014
comment
Если я аннотирую EntityIDKey с помощью @Entity (даже если он не сохраняется; это просто DTO между уровнем сервиса и контроллером пользовательского интерфейса), противоречит ли это шаблону или соглашению? Таким образом, я могу использовать TypedQuery. - person Web User; 06.11.2014
comment
Пока вы не создаете таблицы из своих сущностей (проверьте свой persistence.xml, если сомневаетесь), с «поддельными» сущностями все будет в порядке. Однако IMHO подавление предупреждений о типах - это прямой (и, следовательно, понятный) способ решения проблемы, в то время как моделирование объектов - это скорее обходной путь. Если я, как более поздний сопровождающий, увижу объект без соответствующей таблицы, мне потребуется некоторое время, чтобы выяснить цель. - person kostja; 06.11.2014
comment
@kostja: Поскольку DTO, вероятно, не является сущностью, вы не можете использовать TypedQuery. Это неверно. Из docs.oracle.com/javaee/6/api/javax/persistence/ это разрешено, поэтому в выражении конструктора в JSR-317 четко указано, что: Конструктор может использоваться в списке SELECT чтобы вернуть экземпляр класса Java. Указанный класс не обязательно должен быть сущностью или отображаться в базе данных. Имя конструктора должно быть полностью определенным. - person cinhtau; 03.02.2015
comment
Да, мне интересно, почему я был так уверен в этом ограничении :) Спасибо за уловку, @cinhtau, предложение удалено. - person kostja; 04.02.2015

Теперь новый код выглядит так. Спасибо за помощь.

    TypedQuery<CommUsed> q = em.createQuery(
        "SELECT new CommUsed(c.id,COUNT(t.id)) " +
        "FROM PubText t " +
        "JOIN t.comm c " +
        "WHERE c.element = ?1 " +
        "GROUP BY c.id", CommUsed.class);
    q.setParameter(1, e);
    HashMap<Long, Long> res = new HashMap<Long, Long>();
    for (CommUsed u : q.getResultList())
        res.put(u.commID, u.cnt);
person Hasan Tuncay    schedule 14.03.2013
comment
Понятно, значит, вы все-таки можете использовать TypedQueries :) Полезно знать. К сожалению, кажется, нет разумного способа заполнить Map прямо из запроса, поэтому, боюсь, часть преобразования в последних 3 строках должна остаться. - person kostja; 14.03.2013
comment
Ах, хорошо. Нам не нужно сжимать все в одну строку, как в старые времена C. :-) - person Hasan Tuncay; 15.03.2013
comment
@HasanTuncay, почему вы не использовали функцию выражения конструктора, предложенную @kostja? - person Web User; 05.11.2014
comment
Я сделал. Я только что добавил к нему проверку типа TypedQuery. - person Hasan Tuncay; 06.11.2014