JPA 2: изменить @NamedNativeQuery на эквивалент CriteriaBuilder

Можно ли изменить этот запрос @NamedQuery:

SELECT @rownum:=@rownum+1 'no', m.title, m.author, REPLACE(SUBSTRING_INDEX(m.content, ' ', 20), '<br>', ' '), m.viewed, m.hashid FROM book m, (SELECT @rownum:=0) r WHERE m.lang = ?1 AND m.title like CONCAT('%',?2,'%') ORDER BY m.title asc

эквиваленту CriteriaBuilder. Или не?...


person ChuongPham    schedule 14.04.2011    source источник


Ответы (1)


Я думаю, что естественным способом сделать это было бы обрабатывать запрос в JPA, но выполнять нумерацию и искажение строк в Java. Запрос выглядит так:

EntityManagerFactory emf;
String lang;
String keyword;

CriteriaBuilder builder = emf.getCriteriaBuilder();
final CriteriaQuery<Book> criteria = builder.createQuery(Book.class);
Root<Book> root = criteria.from(Book.class);
criteria.where(builder.and(builder.equal(root.get(Book_.lang), lang), builder.like(root.get(Book_.title), ("%" + keyword + "%"))));
criteria.orderBy(builder.asc(root.get(Book_.title)));

Поскольку результаты возвращаются в виде списка, вы можете просто использовать индекс в списке для поля «нет», и вы можете написать метод getContentSnippet() для книги, чтобы выполнить подстроку и замену.

Если вы действительно хотите изменить строку в базе данных, возможно, чтобы уменьшить объем передаваемого текста, вы можете написать запрос кортежа, который может получить книгу и фрагмент. Обратите внимание, что я все равно извлеку всю книгу, а не отдельные поля, потому что это значительно упрощает последующее программирование; если вы хотите избежать переноса всего содержимого, отметьте его для отложенной загрузки в классе Book. Запрос выглядит так:

CriteriaBuilder builder = emf.getCriteriaBuilder();
final CriteriaQuery<Tuple> criteria = builder.createTupleQuery();
final Root<Book> root = criteria.from(Book.class);
final Expression<String> snippetExpr = builder.substring(root.get(Book_.content), 1, 120);
criteria.multiselect(root, snippetExpr);
criteria.where(builder.and(builder.equal(root.get(Book_.lang), lang), builder.like(root.get(Book_.title), ("%" + keyword + "%"))));
criteria.orderBy(builder.asc(root.get(Book_.title)));

Я использую подстроку для построения фрагмента, потому что я использую PostgreSQL, и у него нет эквивалента функции SUBSTRING_INDEX. Вы можете использовать CriteriaBuilder.function, чтобы вызвать его с помощью MySQL. Я все еще не делаю нумерацию или замену в запросе, потому что все еще думаю, что это лучше сделать в коде.

EntityManager em = emf.createEntityManager();
TypedQuery<Tuple> q = em.createQuery(criteria);
List<Tuple> booksAndSnippets = q.getResultList();
for (int i = 0; i < booksAndSnippets.size(); ++i) {
    Tuple bookAndSnippet = booksAndSnippets.get(i);
    int no = i + 1;
    Book book = bookAndSnippet.get(root);
    String snippet = bookAndSnippet.get(snippetExpr).replace("<br>", " ");
}

Я действительно думаю, что вам будет легче использовать JPQL!

person Tom Anderson    schedule 16.04.2011
comment
Да, после того, как я попробовал много разных способов, включая CriteriaBuilder, JPQL оказался для меня самым простым способом. Если Primefaces имеет способ нумерации возвращаемого набора результатов, мне не нужен этот @rownum:=@rownum+1. Увы, пока я просто должен придерживаться JPQL и надеяться, что будущие выпуски JSF, JPA и Primefaces предоставят лучшие способы настройки отображаемой информации. - person ChuongPham; 17.04.2011
comment
Ага, так вот почему вы делаете так много логики в запросе: вы во власти какой-то тупоголовой библиотеки пользовательского интерфейса. Возможно, стоит повторно опубликовать это как вопрос Primefaces/JSF о том, как добавить нумерацию к итерации по списку. - person Tom Anderson; 17.04.2011
comment
Спасибо, Том. Я попробую еще одну вещь, и если это не сработает, я повторно опубликую вышеизложенное как вопрос Primefaces. - person ChuongPham; 17.04.2011