Ищете HQL builder (язык запросов Hibernate)
Вопрос
Я ищу строителя для HQL на языке Java.Я хочу избавиться от таких вещей, как:
StringBuilder builder = new StringBuilder()
.append("select stock from ")
.append( Stock.class.getName() )
.append( " as stock where stock.id = ")
.append( id );
Я бы предпочел что-то вроде:
HqlBuilder builder = new HqlBuilder()
.select( "stock" )
.from( Stock.class.getName() ).as( "stock" )
.where( "stock.id" ).equals( id );
Я немного погуглил, но ничего не смог найти.
Я написал быстрый и тупой HqlBuilder
на данный момент это соответствует моим потребностям, но я бы хотел найти тот, у которого больше пользователей и тестов, чем у меня одного.
Примечание:Я бы хотел иметь возможность делать подобные вещи и многое другое, чего мне не удалось сделать с помощью Criteria API:
select stock
from com.something.Stock as stock, com.something.Bonus as bonus
where stock.someValue = bonus.id
т.е.выберите все акции, собственность которых someValue
указывает на Любой бонус из бонусной таблицы.
Спасибо!
Решение
@Sébastien Rocca-Serra
Теперь мы приближаемся к чему-то конкретному.Соединение, которое вы пытаетесь выполнить, на самом деле невозможно с помощью Criteria API, но вложенный запрос должен выполнить то же самое.Сначала вы создаете DetachedCriteria
для получения бонусной таблицы используйте IN
оператор для someValue
.
DetachedCriteria bonuses = DetachedCriteria.forClass(Bonus.class);
List stocks = session.createCriteria(Stock.class)
.add(Property.forName("someValue").in(bonuses)).list();
Это эквивалентно
select stock
from com.something.Stock as stock
where stock.someValue in (select bonus.id from com.something.Bonus as bonus)
Единственным недостатком было бы, если бы у вас были ссылки на разные таблицы в someValue
и ваши идентификаторы не уникальны для всех таблиц.Но ваш запрос будет страдать от того же недостатка.
Другие советы
Не делает ли API критериев сделать это для тебя?Это выглядит почти в точности так, как вы просите.
Для типобезопасного подхода к вашей проблеме рассмотрим Querydsl Запрос.
Пример запроса становится
HQLQuery query = new HibernateQuery(session);
List<Stock> s = query.from(stock, bonus)
.where(stock.someValue.eq(bonus.id))
.list(stock);
Querydsl использует APT для генерации кода, подобного JPA2, и поддерживает коллекции JPA / Hibernate, JDO, SQL и Java.
Я являюсь сопровождающим Querydsl, поэтому этот ответ предвзят.
Для другого типобезопасного запроса dsl я рекомендую http://www.torpedoquery.org.Библиотека все еще молода, но она обеспечивает безопасность типов за счет прямого использования классов вашей сущности.Это означает ранние ошибки компилятора, когда запрос больше не применяется до проведения рефакторинга или редизайна.
Я также привел вам пример.Я думаю, судя по вашим постам, что вы пытаетесь ввести ограничение подзапроса, поэтому я основал этот пример на этом:
import static org.torpedoquery.jpa.Torpedo.*;
Bonus bonus = from(Bonus.class);
Query subQuery = select(bonus.getId());
Stock stock = from(Stock.class);
where(stock.getSomeValue()).in(subQuery);
List<Stock> stocks = select(stock).list(entityManager);
Похоже, вы хотите использовать API запроса критериев, встроенный в Hibernate.Чтобы выполнить ваш приведенный выше запрос, это выглядело бы примерно так:
List<Stock> stocks = session.createCriteria(Stock.class)
.add(Property.forName("id").eq(id))
.list();
Если у вас еще нет доступа к сеансу гибернации, вы можете использовать 'DetachedCriteria' следующим образом:
DetachedCriteria criteria = DetachedCriteria.forClass(Stock.class)
.add(Property.forName("id").eq(id));
Если вы хотите получить все акции, которые имеют Бонус с определенным идентификатором, вы могли бы сделать следующее:
DetachedCriteria criteria = DetachedCriteria.forClass(Stock.class)
.createCriteria("Stock")
.add(Property.forName("id").eq(id)));
Для получения дополнительной информации ознакомьтесь с Запросы по критериям из документации по гибернации
select stock
from com.something.Stock as stock, com.something.Bonus as bonus
where stock.bonus.id = bonus.id
Это всего лишь объединение.Hibernate делает это автоматически, тогда и только тогда, когда у вас есть сопоставление между Stock
и Bonus
настройка и если bonus
является свойством Stock
. Criteria.list()
вернется Stock
объекты, и вы просто вызываете stock.getBonus()
.
Обратите внимание, если вы хотите сделать что-то вроде
select stock
from com.something.Stock as stock
where stock.bonus.value > 1000000
Вам нужно использовать Criteria.createAlias()
.Это было бы что-то вроде
session.createCriteria(Stock.class).createAlias("bonus", "b")
.add(Restrictions.gt("b.value", 1000000)).list()
Criterions API не предоставляет всех функциональных возможностей, доступных в HQL.Например, вы не можете выполнить более одного объединения над одним и тем же столбцом.
Почему бы вам не использовать ИМЕНОВАННЫЕ ЗАПРОСЫ?Внешний вид намного более чистый:
Person person = session.getNamedQuery("Person.findByName")
.setString(0, "Marcio")
.list();
Я написал решение GPL для OMERO, которое вы могли бы легко создать в соответствии с вашей ситуацией.
- Источник: QueryBuilder.java
- Тест: QueryBuilderMockTest - запрос
Использование:
QueryBuilder qb = new QueryBuilder();
qb.select("img");
qb.from("Image", "img");
qb.join("img.pixels", "pix", true, false);
// Can't join anymore after this
qb.where(); // First
qb.append("(");
qb.and("pt.details.creationTime > :time");
qb.param("time", new Date());
qb.append(")");
qb.and("img.id in (:ids)");
qb.paramList("ids", new HashSet());
qb.order("img.id", true);
qb.order("this.details.creationEvent.time", false);
Он функционирует как конечный автомат "выбрать-> из-> присоединиться-> где-> заказать" и т.д.и поддерживает необязательные параметры.Было несколько запросов, которые Criteria API не смог выполнить (см. ХХХХ-879), так что в конце концов было проще написать этот небольшой класс для переноса StringBuilder.(Примечание:есть билет HHH-2407 описание ветви гибернации, которая должна объединить их.После этого, вероятно, имело бы смысл повторно посетить Criteria API)
Взгляните на пакет поиска, доступный на сайте переход в спящий режим-универсальный-dao проект.Это довольно приличная реализация HQL Builder.
Я знаю, что эта тема довольно старая, но я также искал HqlBuilder И нашел это Проект "скринсейвер"
Это НЕ заставка Windows, это
"Система управления лабораторной информацией (LIMS) для высокопроизводительных скрининговых установок (HTS), которые проводят скрининг на основе малых молекул и RNAi."
Он содержит HQLBuilder, который выглядит довольно хорошо.
Вот примерный список доступных методов:
...
HqlBuilder select(String alias);
HqlBuilder select(String alias, String property);
HqlBuilder from(Class<?> entityClass, String alias);
HqlBuilder fromFetch(String joinAlias, String joinRelationship, String alias);
HqlBuilder where(String alias, String property, Operator operator, Object value);
HqlBuilder where(String alias, Operator operator, Object value);
HqlBuilder where(String alias1, Operator operator, String alias2);
HqlBuilder whereIn(String alias, String property, Set<?> values);
HqlBuilder whereIn(String alias, Set<?> values);
HqlBuilder where(Clause clause);
HqlBuilder orderBy(String alias, String property);
HqlBuilder orderBy(String alias, SortDirection sortDirection);
HqlBuilder orderBy(String alias, String property, SortDirection sortDirection);
String toHql();
...
Теперь также доступны стандартные Сейф типа JPA запрос и менее стандартный, но тоже хороший Запрос объекта
Примеры:
Сейф типа JPA
EntityManager em = ...
CriteriaBuilder qb = em.getCriteriaBuilder();
CriteriaQuery<Stock> c = qb.createQuery(Stock.class);
Root<Stock> = c.from(Stock.class);
Predicate condition = qb.eq(p.get(Stock_.id), id);
c.where(condition);
TypedQuery<Stock> q = em.createQuery(c);
List<Stock> result = q.getResultList();
Запрос объекта
EntityManager em = ...
ObjectQuery<Stock> query = new GenericObjectQuery<Stock>(Stock.class);
Stock toSearch = query.target();
query.eq(toSearch.getId(),id);
List<Stock> res = (List<Stock>)JPAObjectQuery.execute(query, em);