Delete multiple objects at once using CriteriaBuilder - java

I'm trying to delete a bunch of objects with one query using the CriteriaBuilder API. I'm looking for something like this select:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<T> query = criteriaBuilder.createQuery(entityClass);
Root<T> root = query.from(entityClass);
query.select(root).where(/*some condition*/);
return entityManager.createQuery(query).getResultList();
but then a delete instead of a select. As far as I can see, there's no remove or delete method on CriteriaQuery. Is it possible using this API?
I can of course execute the select, then call entityManager.remove(object) for each result, but that feels very inefficient.

Try this:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaDelete<SomeClass> query = criteriaBuilder.createCriteriaDelete(SomeClass.class);
Root<SomeClass> root = query.from(SomeClass.class);
query.where(root.get("id").in(listWithIds));
int result = entityManager.createQuery(query).executeUpdate();
The where clause can laso look like this:
query.where(criteriaBuilder.lessThanOrEqualTo(root.get("id"), someId));

in JPA 2.1, there are Criteria APIs exactly as what you want.it looks like this:
CriteriaBuilder cBuilder = em.getCriteriaBuilder();
CriteriaDelete<T> cq = cBuilder.createCriteriaDelete(entityClass);
Root<T> root = cq.from(entityClass);
cq.where(/*some codition*/);
int result = em.createQuery(cq).executeUpdate();
you can refert to JPA 2.1 SPEC and API here

Related

JPA create count query from existing CriteriaQuery

I have a query with some predicates, I need to count total records for paging.
Currently, what I'm doing is declare 2 roots for the query to get result list (1) and the count query (2), then with each predicate, duplicate it with different root like this
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<A> cq = cb.createQuery(A.class);
Root<A> root = cq.from(A.class);
CriteriaQuery<Long> cq = cb.createQuery(A.class);
Root<A> rootCount = countQuery.from(A.class);
List<Predicate> predicates = new ArrayList<>();
List<Predicate> predicatesCount = new ArrayList<>();
Predicate p = cb.equal(root.get(A.ID), 1);
predicates.add(p);
Predicate p1 = cb.equal(rootCount.get(A.ID), 1);
predicatesCount.add(p1);
...
// execute both query to get result
So the question is:
Is it possible to create count query from query (1)? Or something to reuse the predicates with count query?
Thanks for reading!
The below example showcases setting up a criteria builder/predicate restrictions, then reusing that to do a count query as well.
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<EntityStub> criteriaQuery = builder.createQuery(EntityStub.class);
Root<EntityStub> entity_ = criteriaQuery.from(EntityStub.class);
entity_.alias("entitySub"); //assign alias to entity root
criteriaQuery.where(builder.equal(entity_.get("message"), "second"));
// Generic retrieve count
CriteriaQuery<Long> countQuery = builder.createQuery(Long.class);
Root<T> entity_ = countQuery.from(criteriaQuery.getResultType());
entity_.alias("entitySub"); //use the same alias in order to match the restrictions part and the selection part
countQuery.select(builder.count(entity_));
Predicate restriction = criteriaQuery.getRestriction();
if (restriction != null) {
countQuery.where(restriction); // Copy restrictions
}
Long count = entityManager.createQuery(countQuery).getSingleResult();
See if that helps you, take note of the root alias, and when doing a Count Query, make sure the Entity class type is Long.class
https://forum.hibernate.org/viewtopic.php?p=2471522#p2471522
You could use Blaze-Persistence to generate the count query for you as it's not that easy to implement such a count query efficiently.
Blaze-Persistence is a library that works on top of JPA/Hibernate and adds support for advanced SQL constructs, rich pagination support and much more. It also has a JPA Criteria implementation which you can use as a drop-in replacement. You can then convert this query to a Blaze-Persistence Core query builder which allows to generate a count query: https://github.com/Blazebit/blaze-persistence#jpa-criteria-api-quick-start
I think this guy answered your question with its utility class like so :
Long count = JpaUtils.count(entityManager, criteriaQuery);
https://stackoverflow.com/a/9246377/5611906

Criteria api - illegalStateException

I've written following code by using Criteria Api:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Entity> criteriaQuery = criteriaBuilder.createQuery(Entity.class);
Root<Entity> root = criteriaQuery.from(Entity.class);
criteriaQuery.select(root);
CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
countQuery.select(criteriaBuilder.count(root));
Long countOfRows = entityManager.createQuery(countQuery).getSingleResult();
As a result I get an exception:
java.lang.IllegalStateException: No criteria query roots were
specified
The exception is thrown by getSingleResult method (last line of the code). Thank you for help!
It works after forcing countQuery to add root:
((QueryStructure)((CriteriaQueryImpl)countQuery).queryStructure).roots.add(root)

Order By Oracle Function In JPA Criteria Builder

I have the following SQL
SELECT ID,LASTTIMEEXECUTEDDATE as d FROM STATISTICSDATE ORDER BY LASTTIMEEXECUTEDDATE
which using CriteriaBuilder works just fine:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<StatisticsDate> cq = cb.createQuery(StatisticsDate.class);
Root<StatisticsDate> rootEntry = cq.from(StatisticsDate.class);
CriteriaQuery<StatisticsDate> all = cq.select(rootEntry).orderBy(cb.desc(
rootEntry.get("lastTimeExecutedDate")));
TypedQuery<StatisticsDate> allQuery = em.createQuery(all);
However now i need to get more accurate results using this:
SELECT ID,LASTTIMEEXECUTEDDATE as d FROM STATISTICSDATE ORDER BY
to_timestamp(LASTTIMEEXECUTEDDATE, 'DD.MM.YYYY:HH24:MI:SS') desc;
I can do this via native sql BUT i would like to know if it is possible to use it via CriteriaBuilder.
What troubles me is to_timestamp(LASTTIMEEXECUTEDDATE, 'DD.MM.YYYY:HH24:MI:SS')
Thanks
Try it like this:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<StatisticsDate> cq = cb.createQuery(StatisticsDate.class);
Root<StatisticsDate> rootEntry = cq.from(StatisticsDate.class);
CriteriaQuery<StatisticsDate> all = cq.select(rootEntry).orderBy(cb.desc(
cb.function(
"to_timestamp", Timestamp.class,
rootEntry.get("lastTimeExecutedDate"),
cb.literal("DD.MM.YYYY:HH24:MI:SS")
)
));
TypedQuery<StatisticsDate> allQuery = em.createQuery(all);

Simplest JPA criteria query

I've used hibernate for a long, now i started using JPA and i can't find a short way to write a simply select in less than seven lines (the use of criteria is a must in this project), is there a shorter way to build this query?
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Transaction> cq = cb.createQuery(Transaction.class);
Root<Transaction> root = cq.from(Transaction.class);
Collection<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(cb.equal(root.get("originalOperationId"), originalOperationId));
cq.where(predicates.toArray(new Predicate[predicates.size()]));
List<Transaction> resultado = em.createQuery(cq).getResultList();
return resultado;

JPA Criteria Query distinct

I am trying to write a distinct criteria query, using:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<RuleVar> query = builder.createQuery(RuleVar.class);
Root<RuleVar> ruleVariableRoot = query.from(RuleVar.class);
query.select(ruleVariableRoot.get("foo").<String>get("foo")).distinct(true);
Based on the example in the javadoc for CriteriaQuery.select()
CriteriaQuery<String> q = cb.createQuery(String.class);
Root<Order> order = q.from(Order.class);
q.select(order.get("shippingAddress").<String>get("state"));
However, this gives me an error:
The method select(Selection<? extends RuleVar>) in the type CriteriaQuery<RuleVar> is not applicable for the arguments (Path<String>)
Can someone please point out what I am doing wrong? Or how to get a Selection object from a Path?
I got it. The problem was my CriteraQuery needed to be of type String. This works:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<String> query = builder.createQuery(String.class);
Root<RuleVar> ruleVariableRoot = query.from(RuleVar.class);
query.select(ruleVariableRoot.get(RuleVar_.varType)).distinct(true);

Categories