Can we select distinct jsonb column in CriteriaBuilder? - jpa

Need to use CriteriaBuilder to select distinct jsonb column.
I tried below but not working :
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery query = builder.createQuery();
Root<Person> root = query.from(Person.class);
Expression<String> function = builder.function("jsonb_extract_path_text", String.class,
root.<String>get("user"), builder.literal("address"));
query.multiselect(function).distinct(true);
Do you know how I can make such a distinct in my JPA CriteriaQuery?

Related

How to write this query in JPA

How to write this query in JPA?
select sol.ID_UNICA, sol.version
from uexdfr01.SOLUTION sol
join
(select solution4_.ID_UNICA, max(solution4_.version) as maxVersion
from uexdfr01.ORDER order3_
inner join uexdfr01.SOLUTION solution4_ on order3_.ID_SOLUTION=solution4_.ID_SOLUTION
where solution4_.ID_UNICA in (130,139,143,129,126,128,141,121,124,131)group by solution4_.ID_UNICA) as groupedtt
on sol.ID_UNICA = groupedtt.ID_UNICA
AND sol.version = groupedtt.maxVersion*
If you are not sure about db relationships use createNativeQuery and pass your query inside parenthesis.
Query selectRecords = getEntityManager().createNativeQuery(sqlQuery.toString());
Returned values are type of Objects so make sure to assign them in List as following way..
List<Object[]> data= selectRecords.getResultList();

Criteria query containing a collection valued property

Is it possible (and if so how) to create a criteria query that results in a tuple or array of which some elements are collections from a collection valued property?
Given an entity Dummy which has a List<SubEntities> with name subs
class Dummy {
String name;
List<SubEntity> subs;
}
class SubEntity {
// some attributes
}
I want a criteria API query which results in something with the structure
Tuple>
An Array instead of a Tuple would be fine, same for an Array or similar for List.
I tried the following:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object[]> q = cb.createQuery(Object[].class);
Root<DummyEntityWithCollection> root = q.from(DummyEntityWithCollection.class);
Join<Object, Object> subs = root.join("subs");
q.select(cb.array(root.get("name"), subs));
List<Object[]> list = em.createQuery(q).getResultList();
But the Object[]s contained in list have as the second element SubEntitys instead of List<SubEntity>.
This one fails in the same way:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> q = cb.createTupleQuery();
Root<DummyEntityWithCollection> root = q.from(DummyEntityWithCollection.class);
Join<Object, Object> subs = root.join("subs");
q.multiselect(root.get("name"), subs);
List<Tuple> list = em.createQuery(q).getResultList();
This variant
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object[]> q = cb.createQuery(Object[].class);
Root<DummyEntityWithCollection> root = q.from(DummyEntityWithCollection.class);
q.select(cb.array(root.get("name"), root.get("subs")));
List<Object[]> list = em.createQuery(q).getResultList();
Doesn't work at all and results in an invalid SQL statement with a single . as one select column (at least for Hibernate and HSQLDB).
The question How can I retrieve a collection property using criteria Api seems to indicate that it is not possible but it is based on Hibernate and I would like to get a JPA based answer. Especially an answer pointing out the section of the JPA spec that makes it clear that this is not possible would be appreciated.
JPQL defines the select clause in section 4.8 as
select_clause ::= SELECT [DISTINCT] select_item {, select_item}*
select_item ::= select_expression [[AS] result_variable]
select_expression ::= single_valued_path_expression | scalar_expression | aggregate_expression | identification_variable | OBJECT(identification_variable) | constructor_expression
so you can see that multi-valued expressions are not selectable in JPQL.
Criteria is simply a way to create a query using an API and objects. See JPA spec chapter 6.1:
The semantics of criteria queries are designed to reflect those of Java Persistence query
language queries.
So it's reasonable to assume the same constraint applies to the Criteria API.

Criteriabuilder like, how to do it for Long?

i try use "like" method from Criteriabuilder for get all record based on pattern " 10% ".
I want get record where ID is - 101, 10002, 1003,1000 etc...
I've use this code:
Predicate p = cb.like(r.<String>get("ID").as(String.class), "10%")
but i got Exception where i see what postgres can't execute query like this:
SELECT ID, NAME, SOMETHING FROM TABLE WHERE ID LIKE 10%
That is JPA (Glassfish 4.x) generate wrond query.
Right query must like that :
SELECT ID, NAME, SOMETHING FROM TABLE WHERE CAST (ID as TEXT) LIKE '10%'
How to build query via Criteria API that i got a right query for postgres ?
Updated:
I try write a CAST function :
Expression<String> postgresqlCastFunction = cb.function("CAST", String.class, r.<String>get("ID").as(String.class));
Predicate p = cb.like(postgresqlCastFunction, "10%");
but got a query like this :
FROM TABLE WHERE (CAST(ID) LIKE ?)
, so, how to add need expression in function for this right result -
FROM TABLE WHERE (CAST(ID as TEXT) LIKE ?) ..
An example implementation using PostgreSQL native TO_CHAR function may look as follows:
JPQL
SELECT r FROM Records r
WHERE FUNCTION('TO_CHAR', r.ID, 'FM9999999999') LIKE :pattern
Criteria API
Path<String> id = r.get("ID");
Expression<String> format = cb.literal("FM9999999999");
Expression<String> function= cb.function("TO_CHAR", String.class, id, format);
ParameterExpression<String> pattern = cb.parameter(String.class, "pattern");
Predicate like = cb.like(function, pattern);
cq.where(like);
also you can build the query as an one-liner:
cq.where(cb.like(cb.function("TO_CHAR", String.class, r.get("ID"), cb.literal("FM9999999999")), cb.parameter(String.class, "pattern")));
Execute the above query:
Query q = em.createQuery(cq).setParameter("pattern", "10%");

How to delete entities from a joined table with JPA 2.1 CriteriaDelete

I want to delete (JPA 2.1) all patients from one Hospital, but run into a problem:
UPDATE/DELETE criteria queries cannot define joins
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaDelete<PatientEntity> delete = cb.createCriteriaDelete(PatientEntity.class);
Root<PatientEntity> root = delete.from(PatientEntity.class);
Join<PatientEntity, HospitalEntity> join = root.join(PatientEntity_.Hospital);
delete.where(cb.equal(join.get(HospitalEntity_.id), id));
Query query = entityManager.createQuery(delete);
query.executeUpdate();
Error:
UPDATE/DELETE criteria queries cannot define joins
How should I delete all Patients, while the Join cannot be performed?
You can use a subquery that selects proper entities and 'in' clause for that.
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaDelete<PatientEntity> delete = cb.createCriteriaDelete(PatientEntity.class);
Root<PatientEntity> root = delete.from(PatientEntity.class);
Subquery<PatientEntity> subquery = delete.subquery(PatientEntity.class);
Root<PatientEntity> root2 = subquery.from(PatientEntity.class);
subquery.select(root2);
/* below are narrowing criteria, based on root2*/
Join<PatientEntity, HospitalEntity> join = root2.join(PatientEntity_.Hospital);
subquery.where(cb.equal(join.get(HospitalEntity_.id), id));
delete.where(root.in(subquery));
Query query = entityManager.createQuery(delete);
query.executeUpdate();

Join Two table in Criteria Query

I have three tables one is ItemCategory,ItemMaster and Price. I am referring itemaCategoryId in ItemMaster table and like that referring itemmasterid in price. Now i have to display contents of price order by itemcategory id. This is my criteria query.
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Price> cq = cb.createQuery(Price.class);
Root<Price> root = cq.from(Price.class);
Root<ItemMaster> itemMasterRoot = cq.from(ItemMaster.class);
Root<ItemCategory> itemCategoryRoot = cq.from(ItemCategory.class);
Join<ItemMaster, ItemCategory> s=itemMasterRoot.join(ItemMaster_.category);
Join<Price,ItemMaster> y=root.join(Price_.itemMaster);
Path<Long> itemMasterId=root.get(Price_.itemMasterId);
cq.select(root).where(cb.equal(root.get(Price_.priceMasterId), priceMasterId))
.orderBy(cb.asc(itemMasterId));
TypedQuery<Price> q = entityManager.createQuery(cq);
Above my criteria Query
If you use multiple from statements, you get the cartesian product of all entities. If you want to preserve the relationships, use join instead:
Root<Price> price = cq.from(Price.class);
Join<Price,ItemMaster> itemMaster = price.join(Price_.itemMaster);
Join<ItemMaster, ItemCategory> itemCategory = itemMaster.join(ItemMaster_.category);
However it looks like at least the second join may be useless, because you are able to access the category property directly using the getter, isn't it?:
Price aPriceResult;
ItemCategory categoryResult = aPriceResult.getItemMaster().getCategory();