JPA(2.0) Criteria Simple Join - java

I am having problem creating criteriaQuery for the following sql.
Any help would be appreciated.Lets say I have two tables Member and Person.
I am joining on name and age and having where clause for both of the table.
I am using OpenJPA(2.0)
select *
from Member
join Person
on Member.name = Person.name
and Member.age = Person.age
where Member.name = 'someOne'
and Member.age = '24'
and Person.gender = 'F'
and Person.type = 'employee'

How about something like this:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Tuple> criteria = builder.createTupleQuery();
Root<Member> fromMember = criteria.from(Member.class);
Root<Person> fromPerson = criteria.from(Person.class);
criteria.multiselect(fromMember, fromPerson);
List<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(builder.equal(fromMember.get(Member_.name), fromPerson.get(Person_.name)));
predicates.add(builder.equal(fromMember.get(Member_.age), fromPerson.get(Person_.age)));
predicates.add(builder.equal(fromMember.get(Member_.name), "someOne"));
predicates.add(builder.equal(fromMember.get(Member_.age), 24));
predicates.add(builder.equal(fromPerson.get(Person_.gender), "F"));
predicates.add(builder.equal(fromPerson.get(Person_.type), "employee"));
criteria.where(predicates.toArray(new Predicate[predicates.size()]));
List<Tuple> result = em.createQuery(criteria).getResultList();
This will return a 2-element tuple made up of Member and Person. You can enumerate the individual fields in the call to multiselect if you would rather have individual fields in each tuple.

Related

How to select only some fields using JPA criteria

I have a Student entity and want to select only two fields - id and age. After reading different posts I wrote the following code:
CriteriaQuery<Student> criteriaQuery = ..
var root = criteriaQuery.from(Student.class);
criteriaQuery.multiselect(root.get("id"), root.get("age"));
typedQuery = entityManager.createQuery(criteriaQuery);
List<Student> students = typedQuery.getResultList();
However, it doesn't work. How to do it using hibernate jpa provider?
If you only want two fields and use multiselect you can use a TupleQuery like in the example below:
CriteriaQuery<Tuple> criteriaQuery = criteriaBuilder.createTupleQuery();
var root = criteriaQuery.from(Student.class);
criteriaQuery.multiselect(root.get("id"), root.get("age"));
typedQuery = entityManager.createQuery(criteriaQuery);
List<Tuple> students = typedQuery.getResultList();
To access the values of the tuples use the get method of it.
E.g.:
Long id = (Long) students.get(0).get(0);

how to write subquery using criteria

please help me out writing criteria builder for this query
SELECT *
FROM XYZ
WHERE date_v < "2020/01" AND
id NOT IN (SELECT id FROM XYZ WHERE date_v = '2020/01')
i have looked at using subqueries in jpa criteria api but i am unable to figure it
I have tried using subquery and joins but it throwing different error after all i get to know that i need to get more clarity about query criteria usages. any help much appreciated
You have to create XyzEntity with Long id and LocalDate date_v fields.
// query
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<XyzEntity> query = cb.createQuery(XyzEntity.class);
Root<XyzEntity> root = query.from(XyzEntity.class);
LocalDate date = LocalDate.of(2020, 1, 1);
// subquery
Subquery<Long> subQuery = query.subquery(Long.class);
Root<XyzEntity> subRoot = subQuery.from(XyzEntity.class);
Predicate idSubPredicate = cb.equal(root.get("id"), subRoot.get("id"));
Predicate dateSubPredicate = cb.equal(subRoot.get("date_v"), date);
subQuery.select(subRoot.get("id")).where(idSubPredicate, dateSubPredicate);
// query predicates
Predicate datePredicate = cb.greaterThan(root.get("date_v"), date);
Predicate notExistsPredicate = cb.exists(subQuery).not();
// query result
query.select(root).where(datePredicate, notExistsPredicate);
List<XyzEntity> result = entityManager.createQuery(query).getResultList();
I have mentioned the corrections in comments for the answer but I feel providing full solution seems good and helps others:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Entity> query = cb.createQuery(Entity.class);
Root<Entity> root = query.from(Entity.class);
// subquery
Subquery<Long> subQuery = query.subquery(Long.class);
Root<Entity> subRoot = subQuery.from(Entity.class);
Predicate subPredicate = cb.equal(subRoot.get("date_v"), dateValue);
subQuery.select(subRoot.get("id")).where(subPredicate);
// query predicates
Predicate datePredicate = cb.lessThan(root.get("date_v"), dateValue);
Predicate notExistsPredicate = root.get("id").in(subQuery).not();
// query result
query.select(root).where(datePredicate, notExistsPredicate);
Query d = entityManager.createQuery(query);
List<Entity> resultList = d.getResultList()

JPA Criteria with ElementCollection

I have something like that:
#Entity
public class Person {
#ElementCollection
private List<String> emails;
...
}
how can I convert the following JPQL into a Criteria Query:
select p from Person p
where exists (
select 1
from p.emails e
where e like :email
)
If you don't really need the power of LIKE and an exact match is enough you can check if emails contains email.
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Person> criteria = builder.createQuery(Person.class);
Root<Person> p = criteria.from(Person.class);
criteria.select(p);
Expression<List<String>> emails = p.get(Person_.emails);
criteria.where(builder.isMember("[email address]", emails));
TypedQuery<Person> tq = entityManager.createQuery(criteria);
List<Person> persons = tq.getResultList();
Note that p.get(Person_.emails) requires a static meta model of the Person class. If you do not have that, you can replace that part by p.get("emails") at the cost of type-safety.
If you do need to perform a LIKE you will have to join on the collection.
Join<Person, String> emailJoin = p.join(Person_.emails);
criteria.where(builder.like(emailJoin, "[email address]"));
criteria.distinct(true);

jpa criteria - expression based on db field name

I have an entity called User, which has the following relationship to an entity called Company:
#Entity
public class User {
...
#ManyToOne
#JoinColumn(name="COMPANY_ID",referencedColumnName="ID")
private Company company = null;
...
}
And on my database, I have a User table with a "COMPANY_ID" column. How can I create a JPA criteria query using this field?
Using a criteria builder, I've tried the following expressions without success:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(User.class);
Root mockEntityRoot = cq.from(User.class);
//cq.where(cb.equal(mockEntityRoot.get("company"), 2));
//cq.where(cb.equal(mockEntityRoot.get("COMPANY_ID"), 12));
cq.where(cb.equal(mockEntityRoot.get("company.id"), 8));
entityManager.createQuery(cq).getResultList();
But I got the following error: "The attribute [company.id] from the managed type User is not present."
Thanks in advance.
I think you need an explicit join.
Notice I am using Criteria's metamodel.
This is a snippet I have, it's not the same thing as yours but you can have an idea
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Site> q = cb.createQuery(Site.class);
Root<Site> e = q.from(Site.class);
Join<Site,SiteType> siteType = e.join(Site_.siteType);
Predicate predicate = cb.conjunction();
Predicate p1 = cb.equal(siteType.get(SiteType_.id), selectedSiteType.getId());
Predicate p2 = cb.equal(e.get(Site_.markedAsDeleted), false);
predicate = cb.and(p1,p2);
q.where(predicate);
q.select(e);
TypedQuery<Site> tq = entityManager.createQuery(q);
List<Site> all = tq.getResultList();
return all;

using a ParameterExpression versus a variable in JPA Criteria API

When using the JPA Criteria API, what is the advantage of using a ParameterExpression over a variable directly? E.g. when I wish to search for a customer by name in a String variable, I could write something like
private List<Customer> findCustomer(String name) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Customer> criteriaQuery = cb.createQuery(Customer.class);
Root<Customer> customer = criteriaQuery.from(Customer.class);
criteriaQuery.select(customer).where(cb.equal(customer.get("name"), name));
return em.createQuery(criteriaQuery).getResultList();
}
With parameters this becomes:
private List<Customer> findCustomerWithParam(String name) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Customer> criteriaQuery = cb.createQuery(Customer.class);
Root<Customer> customer = criteriaQuery.from(Customer.class);
ParameterExpression<String> nameParameter = cb.parameter(String.class, "name");
criteriaQuery.select(customer).where(cb.equal(customer.get("name"), nameParameter));
return em.createQuery(criteriaQuery).setParameter("name", name).getResultList();
}
For conciseness I would prefer the first way, especially when the query gets longer with optional parameters. Are there any disadvantages of using parameters like this, like SQL injection?
you can use ParameterExpression like this:
assume that you have some input filter, an example could be this:
in your query you have to check the value of a fiscal Code.
let's start:
first of all create criteriaQuery and criteriaBuilder and root
CriteriaBuilder cb = _em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<RootEntity> soggettoRoot = cq.from(RootEntity.class);
1) inizialize a predicateList(use for where clause) and a paramList(use for param)
Map<ParameterExpression,String> paramList = new HashMap();
List<Predicate> predicateList = new ArrayList<>();
2 )check if the input is null and create predicateList and param
if( input.getFilterCF() != null){
//create ParameterExpression
ParameterExpression<String> cf = cb.parameter(String.class);
//if like clause
predicateList.add(cb.like(root.<String>get("cf"), cf));
paramList.put(cf , input.getFilterCF() + "%");
//if equals clause
//predicateList.add(cb.equal(root.get("cf"), cf));
//paramList.put(cf,input.getFilterCF()());
}
3) create the where clause
cq.where(cb.and(predicateList.toArray(new Predicate[predicateList.size()])));
TypedQuery<Tuple> q = _em.createQuery(cq);
4) set param value
for(Map.Entry<ParameterExpression,String> entry : paramList.entrySet())
{
q.setParameter(entry.getKey(), entry.getValue());
}
When using a parameter, likely (dependent on JPA implementation, datastore in use, and JDBC driver) the SQL will be optimised to a JDBC parameter so if you execute the same thing with a different value of the parameter it uses the same JDBC statement.
SQL injection is always down to the developer as to whether they validate some user input that is being used as a parameter.

Categories