I am using JPA 2. For safety reasons, I am working type safe with CriteriaQuery's (and thus, I am not searching for any solutions to typed queries and so on).
I recently came across an issue in which I needed to set a SQL-LIMIT.
After a lot of searching, I was still not successful in finding a solution.
CriteriaQuery<Product> query = getEntityManager().getCriteriaBuilder().createQuery(Product.class);
Root<Product> product = query.from(Product.class);
query.select(product);
return em.createQuery(query).getResultList();
Can anyone help me?
Define limit and offset on the Query:
return em.createQuery(query)
.setFirstResult(offset) // offset
.setMaxResults(limit) // limit
.getResultList();
From the documentation:
TypedQuery setFirstResult(int startPosition)
Set the position of the first result to retrieve. Parameters:
startPosition - position of the first result, numbered from 0
TypedQuery setMaxResults(int maxResult)
Set the maximum number of results to retrieve.
For the sake of completeness, I want to answer the initial question with regards to the JPA Criteria API.
First of all, you might clarify for yourself beforehand when to use JPQL and when to use the Criteria API depending on the use case. There is a nice article on this at the ObjectDB documentation website which states:
A major advantage of using the criteria API is that errors can be detected earlier, during compilation rather than at runtime. On the other hand, for many developers string based JPQL queries, which are very similar to SQL queries, are easier to use and understand.
I recommend this article in general because it describes concisely how to use the JPA Criteria API. There is a similar article about the Query API.
Back to the question:
A CriteriaQuery offers a set of restrictions that are accessible - for instance - by using the where() method. As you might intuitively guess: you cannot limit the query to a particular number of results with such a restriction - except you have a trivial case like limiting on a unique identifier (which would make the usage of the Criteria API obsolete). Simply explained: a limit is not a criterion and therefore not covered by that api. See also the old but gold Java EE docs for more details.
Solution
However, you can of course use your CriteriaQuery object as a foundation for a JPQL query. So first, you create your CriteriaQuery as is:
CriteriaQuery<Product> criteriaQuery =
getEntityManager().getCriteriaBuilder().createQuery(Product.class);
Root<Product> product = criteriaQuery.from(Product.class);
criteriaQuery.select(product);
Then use the JPA Query constructor for CriteriaQuery objects:
Query limitedCriteriaQuery = getEntityManager().createQuery(criteriaQuery)
.setMaxResults(resultLimit); // this is the important part
return limitedCriteriaQuery.getResultList();
And that is basically how you should use both APIs according to the documentation and the provided articles.
Related
Hello this query used to work in Hibernate Search 4.2, after upgrading to v5 apparently now it doesn't split the search terms:
#Indexed
public class Foo {
#DocumentId
private Integer id;
.....
}
.....
QueryBuilder qb = fullTextEntityManager.getSearchFactory()
.buildQueryBuilder().forEntity(Foo.class).get();
org.apache.lucene.search.Query luceneQuery = qb
.keyword()
.onFields("id")
.matching("123 567")
.createQuery();
In v4, Hibernate Search would create a query matching either of the 2 IDs in the example, however in v5, Hibernate Search no longer splits the "123 567" into 2 terms and treats the whole string as a single value. The same type of query seems to yield the old v4 behavior on any other field that's not the DocumentId. I've read the migration guide and I haven't seen any mention of this change of behavior. How would you rewrite this query now?
Can someone shed some light on this? Thank you.
The field mapped as #DocumentId needs to be treated as a special case. Since it's also used for deletion (and updates) of the document within the index, it has to be treated as a single keyword to avoid any ambiguity: no Analyzer will be applied to it.
The QueryBuilder DSL automatically pre-processes the matching() clause with the same Analyzer as used during the indexing pipeline; since the id field is treated as a single, unique keyword the input text is not broken down in this case.
To load entities by id I'd generally recommend to use a traditional Hibernate Criteria, not using a full-text query.
If you want to use the full-text query to combine it with other full-text restrictions you can either map the id property into an additional #Field (on top of #DocumentId) and give it a different name, or you can target each individual id with a TermQuery and combine all term queries using a BooleanQuery.
I suspect it's not mentioned in the migration guide as this was never intended to work like it did in v4. The fact that it no longer works as in older versions is likely the consequence of a bug fix.
While building a query using Hibernate, I noticed something rather odd. If I use sequential named parameters for the ORDER BY clause, Hibernate throws a QuerySyntaxException (the colon prefix being an unexpected token):
createQuery("FROM MyEntity ORDER BY :orderProperty :orderDirection");
However, when this is done with a plain SQL query the query is created without a problem:
createSQLQuery("SELECT * FROM my_entity_table ORDER BY :orderProperty :orderDirection");
I know Hibernate is doing more String evaluation for the HQL query, which is probably why the SQL query is created without an error. I am just wondering why Hibernate would care that there are two sequential named parameters.
This isn't a huge issue since it is simple to work around (can just append the asc or desc String value to the HQL instead of using a named paramater for it), but it struck my curiosity why Hibernate is preventing it (perhaps simply because 99% of the time sequential named parameters like this result in invalid SQL/HQL).
I've been testing this in my local, and I can't get your desired outcome to work with HQL.
Here is quote from the post I linked:
You can't bind a column name as a parameter. Only a column value. This name has to be known when the execution plan is computed, before binding parameter values and executing the query. If you really want to have such a dynamic query, use the Criteria API, or some other way of dynamically creating a query.
Criteria API looks to be the more useful tool for your purposes.
Here is an example:
Criteria criteria = session.createCriteria(MyEntity.class);
if (orderDirection.equals("desc")) {
criteria.addOrder(Order.desc(orderProperty));
}
else {
criteria.addOrder(Order.asc(orderProperty));
}
According to the answer accepted in this question, you can only define parameters in WHERE and HAVING clauses.
The same answer also gives you some ways to have a workaround for your problem, however I will add one more way to do this:
Use the CASE - WHEN clause in your ORDER BY, this would work by the following way:
SELECT u FROM User u
ORDER BY
CASE WHEN '**someinputhere**' = :orderProperty
AND '**someotherinput**' = :orderDirection
THEN yourColumn asc
ELSE yourColumn desc END
Please, note that in this approach would required you to write all the possible inputs for ordering. Not really beautiful but really useful, especially because you would not need to write multiple queries with different orderings, plus with this approach you can use NamedQueries, which would be possible by writing the query dinamically using string concats.
Hope this can solve your problem, good luck!
Is it possible to transform a JPQL sting like select x from Clazz x into a CriteriaQuery object where I could check if the query's root is Clazz or not (using CriteriaQuery.getRoots) or do the same thing differently (not transforming into CriteriaQuery)?
The JPA docs are quite simple (e.g. CriteriaBuilder's class comment says "Return an instance of CriteriaBuilder for the creation of CriteriaQuery objects." - 10 or 20 sentences and references more wouldn't hurt). It thus doesn't mention anything about reversing the CriteriaQuery -> TypedQuery process.
jpa - transforming jpql join query to criteria api and How to convert a JPQL with subquery to Criteria API equivalent? cover concrete cases. I'm looking for a generic programmatic solution.
Specifying a type which doesn't match with the argument of CriteriaBuilder.createQuery(Class) causes an IllegalArgumentException, but that's more of a workaround and doesn't allow a very useful set of methods to investigate.
No, not possible, or at least no standardised way.
See also this issue where someone has requested it for a future version of JPA.
I'm using GAE's datastore and JPA persistence framework to store my entities. Though when attempting to retreive some specific entities I run into the problem mentioned below.
The following exception is thrown when invoking the getResultList() method on my TypedQuery: javax.persistence.PersistenceException: Illegal argument
EntityManager em = Persistence.createEntityManagerFactory("test-persistence")
.createEntityManager();
String q = "SELECT c FROM c TestBord c WHERE c.publiclyAvailible=true
AND c.avarageRating='5'
AND c.user LIKE 'user%'
AND c.nameBord LIKE 'bord%'";
TypedQuery<TestBord> tq = em.createQuery(q, TestBord.class);
List<TestBord> l = tq.getResultList();
As also shown above, here is the query I'm using:
SELECT c FROM c KvCBord c WHERE c.publiclyAvailible=true
AND c.avarageRating='5'
AND c.user LIKE 'user%'
AND c.nameBord LIKE 'bord%'
It seems to break when I use two LIKE clauses, anybody have any ideas on how to work around this problem, or knows how to properly rewrite the query?
NOTE: Works fine with just one LIKE clause though.
AppEngine translates your GQL query into a low level Datastore API query. According to the Restrictions on queries Java docs, "Inequality filters are limited to at most one property". This is usually because of index selection. The LIKE operator becomes an inequality filter and cannot apply to both .user and .nameBord properties in the same query.
It doesn't work because App Engine has restrictions in its queries. Queries results come from indexes, and you can't have indexes that support 2 or more inequality filters.
I recommend using Search API for that searches. It's easy, practical, fast, and you can do more complex searches:
https://developers.google.com/appengine/docs/java/search/
I'm trying to convert a simple Play/JPA query to use the criteria API. Below isn't even the query I'm trying to convert; this one's even simpler -- just trying to get something to succeed to begin with.
All the examples I've been finding online expect you to be able to use a class that has _ appended to the class name, much like what I've seen hibernate queries do to table name aliases in the generated SQL. However, I can't get my code to compile this way since there is no class: ExtendedHaulTrain_ (there is however ExtendedHaulTrain)
Is there some kind of annotation I need to add to the ExtendedHaulTrain class? Perhaps I have not been reading deeply enough but the examples I've found so far don't address the issue of the class with the underbar appended.
Here's my code that fails to compile on the last line, specifically on ExtendedHaulTrain_
Query query = JPA.em().createQuery("select DISTINCT(x.trnType) from ExtendedHaulTrain x");
List<String> trainTypes = query.getResultList();
//as criteria query
CriteriaBuilder cb = JPA.em().getCriteriaBuilder();
CriteriaQuery<ExtendedHaulTrain> q = cb.createQuery(ExtendedHaulTrain.class);
Root<ExtendedHaulTrain> xhtRoot = q.from(ExtendedHaulTrain.class);
q.select(xhtRoot.get(ExtendedHaulTrain_.trnType)).distinct(true);
Instead of the MetaModel classes(they end with '_') you can always use the attribute name in form of a string as refrence.
q.select(xhtRoot.get("trynType")).distinct(true);
As noted in my comment there is a notion of a meta-model class I'd rather avoid. So below is how I converted my existing query to use the criteria API. Again, this is just to get a success under my belt; I'm probably not going to replace this query. Rather I have another more complex query, for which I intend to use the Criteria API; this was just to get some familiarity with the Criteria API -- there will probably be more questions to follow!
/*
Query query = JPA.em().createQuery("select DISTINCT(x.trnType) from ExtendedHaulTrain x");
List<String> trainTypes = query.getResultList();
*/
CriteriaBuilder cb = JPA.em().getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(ExtendedHaulTrain.class);
Root root = cq.from(ExtendedHaulTrain.class);
cq.select(root.get("trnType")).distinct(true);
List<String> trainTypes = JPA.em().createQuery(cq).getResultList();
I understand that you do not like these meta-models but this is actually a very useful thing, which keeps your code on the safe side of type-safety (believe me, once you begin to write more queries, you will see the advantage). And the advantage is: you can generate them automatically with the so called meta-model generators (which are annotation processing tools). Hibernate has for example something one generator. In Eclipse it is very easy to generate them. Also in Maven it is easy. I recommend to use them.
UPDATE
Type Safety means actually beside not having to write xhtRoot.get("trynType") also that you work with correct join types. Do not forget, that compared to NamedQueries, CriteriaQueries are not checked on deployment. This means, if you remove or use the wrong type in the generic part of a join result (WrongOwner below)
Join<WrongOwner, Address> address = cq.join(Pet_.owners).join(Owner_.addresses);
you will know that on compile time.