How to join an entity with custom select using CriteriaBuilder - java

I have a scenario where I need to get the row with maximum date. The SQL query for this would be
SELECT c.*
FROM course c
INNER JOIN
(SELECT moduleId ,MAX(endDate) AS max_date
FROM course
WHERE moduleId = 12345
GROUP BY moduleId
) customSelect
ON customSelect.moduleId = c.moduleId AND c.endDate = customSelect.max_date
WHERE c.moduleId = 12345
I need to convert this query to JPA using CriteriaBuilder.Since this isn't a direct entity join, rather a selection join to root entity, I'm having trouble to figure out how to join the custom select part to the root entity Course with below syntax:
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Course> criteriaQuery = builder.createQuery(Course.class);
Root<Course> root = criteriaQuery.from(Course.class);
And then how to root.join(JoinType.INNER) to join customSelect part?
If someone can show some pointers to the syntax of of joining the root to a custom selection, that would be great.

Try this
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Course> criteriaQuery = builder.createQuery(Course.class);
Root<Course> root = criteriaQuery.from(Course.class);
criteriaQuery.select(root).where(cb.equal(root.get(Course_.moduleId), 12345));
criteriaQuery.orderBy(cb.desc(r.get(Course_.endDate)));
TypedQuery<Course> query = em.createQuery(criteriaQuery);
query.setMaxResult(1);
Course result = query.getSingleResult();

Related

Hibernate: Query to select values from multiple tables using CriteriaQuery

Let's say, I have a query like
Select a.valA, b.valB
from tableA a join tableB b on a.joinCol = b.joinCol
where a.someCol = 1.
I want to execute it using Hibernate (and Spring Data) in one query to the database. I know, I can write just
Query query = em.createQuery(...);
List<Object[]> resultRows = (List<Object[]>)query.getResultList();
But my question would be - is it possible to do it in a typesafe way, using CriteriaQuery for example? The difficulty is, that, as you see, I need to select values from different tables. Is there some way to do this?
Simple example where an Employee has many to many relation to several jobs that he may have :
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Tuple> criteria = builder.createTupleQuery();
Root<TableA> root = criteria.from(TableA.class);
Path<Long> qId = root.get("id");
Path<String> qTitle = root.get("title");
Join<TableA, TableB> tableTwo = root.join("joinColmn", JoinType.INNER);
criteria.multiselect(qId, qTitle, tableTwo);
List<Tuple> tuples = session.createQuery(criteria).getResultList();
for (Tuple tuple : tuples)
{
Long id = tuple.get(qId);
String title = tuple.get(qTitle);
TableB tableB= tuple.get(tableTwo);
}
but saw that there is an alternate answer here :
JPA Criteria API - How to add JOIN clause (as general sentence as possible)

How to add filter on second table parameter while doing left join using criteriaquery

I am stuck in a situation where I am not able to create a filter on left join between two table.
My query is something like this.
select count(*)
from orders o
left
join payments p
on o.id = p.o_id
where o.uid_id = 1
and p.name = "abc"
I am trying to do left join using criteria query. In criteria query.
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Orders> query = cb.createQuery(Orders.class);
Root<Orders> ordersRoot = query.from(Orders.class);
Join<Orders, Payments> join = ordersRoot.join(JOIN_COLUMN, JoinType.LEFT);
List<Predicate> predicates = new ArrayList<>();
predicates.addAll(getPredicates(cb, ordersRoot)); //this method gives other predicates on order table
query.where(predicates.toArray(new Predicate[predicates.size()]));
query.select(ordersRoot).distinct(true);
TypedQuery<Orders> query = entityManager.createQuery(criteriaQuery);
List<Orders> list = query.getResultList();
This is my criteria Java code.
In this I have not added for this check.
orders and payments table has one to many relationship.

Build criteria query with joins and custom parameters

I need to build following query using JPA and criteria query but I stuck on join conditions. The query is:
SELECT p.*
FROM output cu
JOIN user ur ON cu.id = ur.id AND cu.key = ur.key
JOIN product p ON cu.id = p.id AND cu.key = p.key
WHERE p.refreshtimestamp IS NOT NULL AND cu.active = true
So far I have following, but how to apply join conditions:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Output> cq = cb.createQuery(Output.class);
Root<Output> output= cq.from(Output.class);
Join<Output, User> user = output.join("user", JoinType.INNER);
Join<User, Product> product = user.join("product", JoinType.INNER);
Any help will be appreciated
Following should help.
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Product> criteria = builder.createQuery(Product.class);
Root<Output> outputRoot = criteria.from(Output.class);
Root<User> userRoot = criteria.from(User.class);
Root<Product> productRoot = criteria.from(Product.class);
Predicate[] predictes = {
builder.equal(outputRoot.get("id"), userRoot.get("key")),
builder.equal(productRoot.get("id"), userRoot.get("key")),
builder.notEqual(productRoot.get("refreshtimestamp"), null), // not sure about null
builder.equal(outputRoot.get("active"), true)
};
criteria.select(productRoot);
criteria.where(builder.and(predicates));
Although this would produce cross joins query, it will work because of where clause making it work like inner join as you require.

In Hibernate 5, what's the CriteriaQuery equivalent of Criteria's restriction and projection?

Before Hibernate 5 deprecated the Criteria class, you could add restrictions to a Criteria to act as a constraint, and projections to act as select statements, like so
Criteria criteria = session.createCriteria(T.class)
.add(Restrictions.or(Restrictions.eq(property, constraintValue)
.set(Projection(Projections.projectionList()
.add(Projections.property(selectValue)));
But, since you now need to use CriteriaQuery like so,
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery(T.class);
Root<T> root = criteriaQuery.from(T.class);
criteriaQuery.select(root);
Query<T> query = session.createQuery(criteriaQuery);
However I have been unable to figure out how to add certain things which are required in SQL statements, mainly because searching for documentation tends to wind up on documentation about Criteria, due to the naming similarity.
So, how can I recreate a simple query, like the one below, using CriteriaQuery?
SELECT selectValue
FROM tables.T
WHERE property = constraintValue
Source.
Has multiple examples, but turns out that the simple select statement we were trying to recreate can be done like so:
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<SELECTVALUETYPE> criteriaQuery = criteriaBuilder.createQuery(SELECTVALUETYPE.class);
Root<PARENTCLASS> root = criteriaQuery.from(PARENTCLASS.class);
criteriaQuery.select(root);
criteriaQuery.where(criteriaBuilder.equal(root.get(property), constraintValue));
Query<SELECTVALUETYPE> query = session.createQuery(criteriaQuery);
Note that this is a generic answer, and won't actually run. The reason being, SELECTVALUETYPE needs to be replaced with the data type of selectValue.
For example, CriteriaQuery might become:
String selectValue -> CriteriaQuery
T selectValue -> CriteriaQuery
Therefore, a working example for the statement
Select name
From Users
Where ID = 1
Could be expressed with the following block
int ID = 1;
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<String> criteriaQuery = criteriaBuilder.createQuery(String.class);
Root<User> root = criteriaQuery.from(User.class);
criteriaQuery.select(root.get("name");
criteriaQuery.where(criteriaBuilder.equal(root.get("ID"), ID));
Query<String> query = session.createQuery(criteriaQuery);
List<String>results = query.getResultList();
for(String name : results){
System.out.println("Name: " + name);
}

Conversion of SQL Left Outer Join Query to JPA Criteria

I have the following native SQL query that I am trying to convert to JPA criteria:
select et.* from t_empl_tx et, t_dept d
where et.assigned_dept = d.dept (+)
and et.employee_id = :employee_id
and (et.start_date >= d.dept_est_date and
et.start_date <= d.dept_close_date or
et.start_date is null or
d.dept is null)
(Note that (+) is roughly equivalent to a left outer join in this case. Yes, I know it denotes the OPTIONAL table, etc, etc).
Here is my attempt at the code:
EntityManager entityManager = getEntityManager();
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<EmployeeTransaction> criteriaQuery = criteriaBuilder.createQuery(EmployeeTransaction.class);
Root<EmployeeTransaction> root = criteriaQuery.from(EmployeeTransaction.class);
// this line bombs!
Join<EmployeeTransaction, Department> join =
root.join(EmployeeTransaction_.assignedDepartment).join(Department_.id).join(DepartmentCompositeId_.department, JoinType.LEFT);
List<Predicate> predicates = new ArrayList<>();
predicates.add(criteriaBuilder.equal(root.get(EmployeeTransaction_.id).get(EmployeeTransactionCompositeId_.employeeId), employeeId));
predicates.add(criteriaBuilder.or(
criteriaBuilder.and(
criteriaBuilder.greaterThanOrEqualTo(root.<Date>get(EmployeeTransaction_.requestedStartDate), join.get(Department_.id).<Date>get(DepartmentCompositeId_.departmentCreationDate)),
criteriaBuilder.lessThanOrEqualTo(root.<Date>get(EmployeeTransaction_.requestedStartDate), join.<Date>get(Department_.departmentCloseDate))
),
criteriaBuilder.isNull(root.get(EmployeeTransaction_.requestedStartDate)),
criteriaBuilder.isNull(join.get(Department_.id).get(DepartmentCompositeId_.departmentCreationDate))
));
criteriaQuery.select(root).where(predicates.toArray(new Predicate[]{}));
TypedQuery<EmployeeTransaction> query = entityManager.createQuery(criteriaQuery);
List<EmployeeTransaction> result = query.getResultList();
This issue seems to be that I'm trying to join a string column, assigedDepartment, to a single field of a composite ID. This is perfectly legal in SQL, but not so easy in the code.
One option is to convert to a number of subqueries, which seems to kill the point of the left outer join entirely.
Can anyone point out what I'm doing wrong?
Jason
You should post your entities so that the answers can be more specific.
However, I'll give a try.
If I am right, you can rewrite the query:
select et.*
from t_empl_tx et
left join t_dept d on et.assigned_dept = d.dept
where
et.employee_id = :employee_id
and (
et.start_date >= d.dept_est_date
and et.start_date <= d.dept_close_date
or et.start_date is null
or d.dept is null)
So, shortly, you have to move the JoinType.LEFT to assignedDepartment join:
EntityManager entityManager = getEntityManager();
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<EmployeeTransaction> criteriaQuery = criteriaBuilder.createQuery(EmployeeTransaction.class);
Root<EmployeeTransaction> root = criteriaQuery.from(EmployeeTransaction.class);
Join<EmployeeTransaction, Department> department = root.join(EmployeeTransaction_.assignedDepartment, JoinType.LEFT);
Path<Date> employeeTransactionRequestedStartDate = root.get(EmployeeTransaction_.requestedStartDate);
Path<DepartmentCompositeId> departmentId = department.get(Department_.id);
Path<Date> departmentCreationDate = departmentId.get(DepartmentCompositeId_.departmentCreationDate)
Path<Date> departmentCloseDate = departmentId.get(DepartmentCompositeId_.departmentCloseDate)
criteriaQuery.select(root).where(
criteriaBuilder.equal(root.get(EmployeeTransaction_.id).get(EmployeeTransactionCompositeId_.employeeId), employeeId),
criteriaBuilder.or(
criteriaBuilder.and(
criteriaBuilder.greaterThanOrEqualTo(employeeTransactionRequestedStartDate, departmentCreationDate)),
criteriaBuilder.lessThanOrEqualTo(employeeTransactionRequestedStartDate, departmentCloseDate)
),
criteriaBuilder.isNull(employeeTransactionRequestedStartDate),
criteriaBuilder.isNull(departmentCreationDate)
)
);
TypedQuery<EmployeeTransaction> query = entityManager.createQuery(criteriaQuery);
List<EmployeeTransaction> result = query.getResultList();

Categories

Resources