JPA CriteriaBuilder - not in a Collection - jpa

What is the difference between:
First test run on the Builder:
Predicate predicate = root.get(PersonEntity_.name).in(names);
criteriaBuilder.not(predicate);
Second test run on the Query:
Predicate predicate2 = root.get(PersonEntity_.name).in(names).not();
criteriaQuery.where(predicate2);
This seems to give the same results. Am I missing something? Should we choose the CriteriaBuilder above the CriteriaQuery?
Complete example:
List<String> names = new ArrayList<String>();
names.add("John");
names.add("Emma");
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<PersonEntity> criteriaQuery = criteriaBuilder.createQuery(PersonEntity.class);
Root<PersonEntity> root = criteriaQuery.from(PersonEntity.class);
// First test run on the Builder
Predicate predicate = root.get(PersonEntity_.name).in(names);
criteriaBuilder.not(predicate);
// Second test run the query
// Predicate predicate2 = root.get(PersonEntity_.name).in(names).not();
// criteriaQuery.where(predicate2);
List<PersonEntity> list = entityManager.createQuery(cq).getResultList();

There is not difference between these variants. Both statements result in the same.

Related

JPA Criteria: Obtain total count just before full result with all columns; reuse Where clause

In JPA Criteria I have a complex query which works. It involves many Joins and a complex Where clause. But right before I run it for the full selection, I need to get a quick COUNT of the full resultset.
I tried to reuse my where clause and all my Joins and select from my top element, nvRoot, using cb.count. But I got the error Caused by: java.lang.IllegalStateException: No criteria query roots were specified.
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Result> criteriaQuery = cb.createQuery(Result.class);
Root<NvisionTrainee> nvRoot = criteriaQuery.from(Nv.class);
Join<Object,Object> plans = nvRoot.join("plans", JoinType.LEFT);
// etc., other Joins
Predicate where = cb.conjunction();
// Complex Where clause built...
criteriaQuery.where(where);
// --- HERE I NEED TO RUN A QUICK COUNT QUERY, with all Joins/Where as built
// --- BUT THE BELOW DOESN'T WORK:
// --- Caused by: java.lang.IllegalStateException: No criteria query roots were specified
CriteriaQuery<Long> cqCount = cb.createQuery(Long.class);
cqCount.select(cb.count(nvRoot));
cqCount.distinct(true);
cqCount.where(where);
Long totalCount = entityManager.createQuery(cqCount).getSingleResult();
// --- THIS FULL QUERY WORKS (THE REMAINDER), IT GETS ME MY FULL SELECTION
CompoundSelection<Result> selectionFull = cb.construct(
Result.class,
nvRoot.get("firstName"),
// etc. - many columns
);
criteriaQuery.select(selectionFull);
criteriaQuery.distinct(true);
TypedQuery<Result> query = entityManager.createQuery(criteriaQuery);
List<Result> results = query.getResultList();
Per the comment below, I tried adding cqCount.from(Nv.class) in the code, but that gave me:
Invalid path: 'generatedAlias2.id'
The simplest workaround would be to extract the predicate-building part into a method and reuse it like so:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
//count query
CriteriaQuery<Long> cqCount = cb.createQuery(Long.class);
Root<NvisionTrainee> nvCountRoot = buildQuery(cqCount, ...);
cqCount.select(cb.count(nvCountRoot));
cqCount.distinct(true);
Long totalCount = entityManager.createQuery(cqCount).getSingleResult();
//actual query
CriteriaQuery<Result> criteriaQuery = cb.createQuery(Result.class);
Root<NvisionTrainee> nvRoot = buildQuery(criteriaQuery, ...); //you might need to return other paths created inside buildQuery if you need to use them in the SELECT clause
CompoundSelection<Result> selectionFull = cb.construct(
Result.class,
nvRoot.get("firstName"),
...
);
criteriaQuery.select(selectionFull);
criteriaQuery.distinct(true);
TypedQuery<Result> query = entityManager.createQuery(criteriaQuery);
List<Result> results = query.getResultList();
where buildQuery is defined like so:
private Root<NvisionTrainee> buildQuery(CriteriaQuery<?> query, ... /* possibly many other arguments*/) {
Root<NvisionTrainee> nvRoot = query.from(Nv.class);
Join<Object,Object> plans = nvRoot.join("plans", JoinType.LEFT);
// etc., other Joins - build your WHERE clause here
return nvRoot;
}
Aliases for roots are generated in some random manner between queries so let's hardcode them.
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Result> criteriaQuery = cb.createQuery(Result.class);
Root<NvisionTrainee> nvRoot = criteriaQuery.from(Nv.class);
// -- root alias --
nvRoot.alias("nvRoot");
Join<Object,Object> plans = nvRoot.join("plans", JoinType.LEFT);
// -- root alias --
plans.alias("plansRoot");
// etc., other Joins
Predicate where = cb.conjunction();
// Complex Where clause built...
criteriaQuery.where(where);
CriteriaQuery<Long> cqCount = cb.createQuery(Long.class);
// -- Added additional roots with the same alias names --
Root<NvisionTrainee> nvRootCqCount = cqCount.from(Nv.class);
nvRootCqCount.alias("nvRoot");
Join<Object,Object> plansCqCount = nvRootCqCount.join("plans", JoinType.LEFT);
plansCqCount.alias("plansRoot");
// etc., other Joins
cqCount.select(cb.count(nvRootCqCount));
cqCount.distinct(true);
// -- and here 'where' substituted with 'criteriaQuery.getRestriction()' --
cqCount.where(criteriaQuery.getRestriction());
Long totalCount = entityManager.createQuery(cqCount).getSingleResult();
// --- THIS FULL QUERY WORKS (THE REMAINDER), IT GETS ME MY FULL SELECTION
CompoundSelection<Result> selectionFull = cb.construct(
Result.class,
nvRoot.get("firstName"),
// etc. - many columns
);
criteriaQuery.select(selectionFull);
criteriaQuery.distinct(true);
TypedQuery<Result> query = entityManager.createQuery(criteriaQuery);
List<Result> results = query.getResultList();
Written by hand so I'm not sure if this works. I have had similar problem with error: Invalid path: 'generatedAlias2.id'.

Dynamic JPA OR Expression

How can I construct Criteria Query to update status of multiple items?
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaUpdate<Item> query = builder.createCriteriaUpdate(Item.class);
Root<Item> root = query.from(Item.class);
List<Predicate> predicates = new ArrayList<Predicate>();
for(Long id : itemIds)
{
predicates.add(builder.or(builder.equal(root.get(Item_.id), id)));
}
query.set(root.get(Item_.status), status)
.where(predicates.toArray(new Predicate[predicates.size()]));
entityManager.createQuery(query).executeUpdate();
The above query only work if itemIds. size() == 1. If the itemIds.size() > 2 then the entities will not be updated. Can anyone help to construct correct predicate for OR expression please.
Thank in advance!

Difference between CriteriaBuilder and CriteriaQuery for a Collection

What is the difference between:
criteriaBuilder.in(predicate);
criteriaQuery.where(predicate);
This seems to give the same results. Am I missing something? Should we choose the builder above the query?
Complete example:
List<String> names = new ArrayList<String>();
names.add("John");
names.add("Emma");
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<PersonEntity> criteriaQuery = criteriaBuilder.createQuery(PersonEntity.class);
Root<PersonEntity> root = criteriaQuery.from(PersonEntity.class);
Predicate predicate = root.get(PersonEntity_.name).in(names);
criteriaBuilder.in(predicate);
// or alternative: criteriaQuery.where(predicate);
List<PersonEntity> list = entityManager.createQuery(cq).getResultList();
criteriaBuilder.in(predicate) creates a new predicate. You should file a bug with your provider if it is adding the predicate to the query, as this will not be portable. According to the specification it creates a new predicate, just as root.get(PersonEntity_.name).in(names) does. The query should only use the predicate if it gets added to it such as by calling criteriaQuery.where(predicate).

How to write subquery using criteria builder

Here I am using NativeQuery to perform selecting lookup type using subquery this is working right but I want to use Criteria Builder. How can I use it?
Query query = em.createNativeQuery(
"SELECT * FROM LOOKUPMASTER WHERE PARENTLOOKUPTYPEID = (SELECT LOOKUPID FROM LOOKUPMASTER WHERE LOOKUPTYPE =? ) ",
Lookupmaster.class
);
query.setParameter(1, lookUpType);
I tried to write the above query using criteria builder but I am getting different result here is my criteria query.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Lookupmaster.class);
Root<Lookupmaster> rt = cq.from(Lookupmaster.class);
Path<Object> path = rt.get("parentlookuptypeid");
cq.select(rt);
Subquery<Lookupmaster> subquery = cq.subquery(Lookupmaster.class);
Root rt1 = subquery.from(Lookupmaster.class);
subquery.select(rt1.get("lookupid"));
subquery.where(cb.equal(rt.get("lookuptype"),lookUpType));
cq.where(cb.in(path).value(subquery));
Query qry =em.createQuery(cq);
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object> criteriaQuery = criteriaBuilder.createQuery();
Root<EMPLOYEE> from = criteriaQuery.from(EMPLOYEE.class);
Path<Object> path = from.get("compare_field"); // field to map with sub-query
from.fetch("name");
from.fetch("id");
CriteriaQuery<Object> select = criteriaQuery.select(from);
Subquery<PROJECT> subquery = criteriaQuery.subquery(PROJECT.class);
Root fromProject = subquery.from(PROJECT.class);
subquery.select(fromProject.get("requiredColumnName")); // field to map with main-query
subquery.where(criteriaBuilder.equal("name",name_value));
subquery.where(criteriaBuilder.equal("id",id_value));
select.where(criteriaBuilder.in(path).value(subquery));
TypedQuery<Object> typedQuery = entityManager.createQuery(select);
List<Object> resultList = typedQuery.getResultList();
Here is a link
another article

JPA CriteriaQuery: getCount + condition

I try to get all the count of Articles (Article.class) which are not analyzed (analyzed == false).
Sadly the following code returns absolutely wrong numbers.
Would anybody know why?
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> query = cb.createQuery(Long.class);
query.select(cb.count(query.from(Article.class)));
Root<Article> articles = query.from(Article.class);
Predicate condition = cb.isFalse(articles.get(Article_.analyzed));
query.where(condition);
TypedQuery<Long> unanalyzedArticlesAmount = entityManager.createQuery(query);
return unanalyzedArticlesAmount.getSingleResult();
finally read this post:
How to count the number of rows of a JPA 2 CriteriaQuery in a generic JPA DAO?
and found the following solution:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery query = cb.createQuery();
Root<Article> root = query.from(Article.class);
query.select(cb.count(root));
Predicate condition = cb.isFalse(root.get(Article_.analyzed));
query.where(condition);
TypedQuery<Long> unanalyzedArticlesAmount = entityManager.createQuery(query);
return unanalyzedArticlesAmount.getSingleResult();