Hibernate criteria query multiple criteria - java

In my current project I've faced a problem of getting entities with hibernate criteria query. I have the following entities:
Professor, which contains a list of students
Student, which contains a list of assignments.
Assignment, which contains id of student to which it is assigned to.
Now, I want to get all assignments relative to the professor, i.e. all assignments Professor assigned to his students.
This query shows what I want to implement in criteria query.
select * from Assigment p, Student a, Professor c where p.studentid = a.id and a.proffid = c.id and c.id = 2411;
How can I implement this query using hibernate criteria API?

suppose your tables like that:
#Entity
public class Professor{
K id;
List<Student> students;
}
#Entity
public class Student{
K profid;
List<Assignments> assignments;
}
#Entity
public class Assignments{
K studentid;
}
simple sample by using alias:
Criteria criteria = currentSession.createCriteria(Professor.class, "professor");
criteria.createAlias("professor.students", "student");
criteria.createAlias("student.assigments", "assigment");
criteria.add(Restrictions.eqProperty("professor.id", "student.profid"));
criteria.add(Restrictions.eqProperty("assigment.studentid", "student.profid"));
criteria.add(Restrictions.eq("id", 2411));
return criteria.list();

Related

Querying entity by parameter inside list property

I am trying to write a query in HQL but it doesn't work. The query filters Students by Grades. Grade is a ManyToMany list inside Student's class.
Exapmle (Student.java):
#NamedQuery(name = "getStudentsByGrade" , query = "from Student where :grade in grades")
The Student class has list property:
Private List<Grade> grades;
This :grade member of grades was what i looked for.

Jpa recursive query

consider the following schema
#Entity
Class employee{
#OneToMany()
List<employee> manaagedEmps;
#OneToOne
employee manager;
}
how to write a query that get all the managed employee for a certain manager , direct(the list of managedEmps) and indirect (managed by managed employee).
It seems that JPA does not support recursive queries. Recently I solved the smilar problem by adding "path" field of type ltree (postgresql). Path is generated by adding id separated by dot to path of parent and path of root nodes is just id. With that field you are able to query subtree (direct and indirect employees) of some node (manager):
SELECT * FROM nodes WHERE path ~ '*.42.*{1,}'; /* for path of type ltree */
SELECT * FROM nodes WHERE path LIKE '%.42.%'; /* for path of type varchar */
The following JPQL query returns flat list of subs for employee with id 2.
List<Employee> subs = em.createQuery(
"SELECT e FROM Employee e LEFT JOIN FETCH e.subs WHERE e.path LIKE '%.' || ?1 || '.%'",
Employee.class
).setParameter(1, '2').getResultList();
//Returns a list of the managed employee of the manager with the specified ID.
#NamedQuery(name="queryName", query="SELECT p.managedEmps FROM employee p WHERE p.manager.uuid = :uuid")
I am using postgresql here.
I did this through native query like this:
Suppose following entity
#Entity
#Table(name = "employee")
public class Employee {
#Id
private Long id;
#ManyToOne
#JoinColumn(name = "parent_id")
private Employee parent;
}
Now, following query can be used to get all childs and sub childs under one manager recursively:
public interface IEmployeeRepository extends JpaRepository<Employee, Long> {
#Query(value = "with recursive subordinates as ("
+ " select e1.id as id, e1.parent_id as parent from employee e1 where e1.parent_id = :parentId"
+ " union"
+ " select e2.id, e2.parent_id from employee e2"
+ " inner join subordinates s on (s.id = e2.parent_id)"
+ " ) select * from subordinates", nativeQuery = true)
Collection<Employee2> getChilds(#Param("parentId") Long parentId);
public static interface Employee2 {
Long getId();
Long getParent();
}
}
Now, you have to convert this result Collection into List in your service layer. That's it.
References:
postgres recursive queries
Jpa Projections to get result
Hope this helps.
I usually prefer to offer some code, but in this case I think the article itself does a better job of explaining.

Filter using field from child entity

I have class Person containing field age, Class Student extends Person containing field class, and class Teacher extends Person and containing field salary.
I want to get all Person who are in class X, if they are Student, and earn 1500$, if they are Teacher, using the same jpql query. The query must return a list of Person. I can make two query and make a method that call both query and join the result but is there a better solution.
I hope I was able to explain
Use "TYPE" and JPA 2.1 "TREAT" to filter on the class.
"SELECT p from Person p where treat(p as Teacher).salary = 1500 or TYPE(p) = Student"
If you cannot use JPA 2.1, you can use a subquery on Teacher to return Teacher ids with salary=1500, and use the result in the main query.
"SELECT p from Person p where p.id in(SELECT t.id from Teacher t where t.salary=1500) or TYPE(p) = Student"

How to create a JPA query with LEFT OUTER JOIN

I am starting to learn JPA, and have implemented an example with JPA query, based on the following native SQL that I tested in SQL Server:
SELECT f.StudentID, f.Name, f.Age, f.Class1, f.Class2
FROM Student f
LEFT OUTER JOIN ClassTbl s ON s.ClassID = f.Class1 OR s.ClassID = f.Class2
WHERE s.ClassName = 'abc'
From the above SQL I have constructed the following JPQL query:
SELECT f FROM Student f LEFT JOIN f.Class1 s;
As you can see, I still lack the condition OR s.ClassID = f.Class2 from my original query. My question is, how can I put it into my JPQL?
Write this;
SELECT f from Student f LEFT JOIN f.classTbls s WHERE s.ClassName = 'abc'
Because your Student entity has One To Many relationship with ClassTbl entity.
If you have entities A and B without any relation between them and there is strictly 0 or 1 B for each A, you could do:
select a, (select b from B b where b.joinProperty = a.joinProperty) from A a
This would give you an Object[]{a,b} for a single result or List<Object[]{a,b}> for multiple results.
Normally the ON clause comes from the mapping's join columns, but the JPA 2.1 draft allows for additional conditions in a new ON clause.
See,
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL#ON
Please see :
public interface YourDBRepository extends JpaRepository<Employee, Long> {
#Query("select new com.mypackage.myDTO(dep.empCode, dep.empName, em.EmployeeCode, em.EmployeeName) \n" +
"from Department dep\n" +
"left join Employee em\n" +
"on dep.DepartmentCode = em.DepartmentCode") // this is JPQL so use classnames
List<myDTO> getDeptEmployeeList();
}
You can also use CrudRepository and include #JoinColumn with FK table class in PK table class and have List return list and then do find operation to achieve the same.
In Department entity class:
#OneToMany
#Fetch(FetchMode.JOIN)
#JoinColumn(name="DEPT_CODE")
private List<Employee> employees;
CriteriaBuilder is yet another option.

JPA CriteriaQuery OneToMany

I have two entities with a OneToMany relationship. To make it simple, let's suppose them as School and Students, with a unidirectional relationship from school to students. I want to find the school object that has a specific student (a student with a specific age, name, ssn, ...).
I know that I can create a simple criteria as the following for simple School's properties (for School's name, as the following):
ParameterExpression<String> p = criteriaBuilder.parameter(String.class, "schoolName");
criteria = criteriaBuilder.and(criteria, criteriaBuilder.like(schoolRoot.get("schoolName") , p));
queryResult.setParameter("schoolName", schoolName + "%");
but, how can I query students with a specific property value while the students is represented as a java.util.List instead of being a basic property?
Can somebody can help me figure this out? I hope I have been able to explain my problem.
Thanks
CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<School> query = criteriaBuilder.createQuery(School.class);
Root<School> schoolRoot = query.from(School.class);
Join<School, Student> join = schoolRoot.join(School_.students);
query.where(criteriaBuilder.equal(join.get(Student_.name), "john"));
It looks up a student with name john in all schools.
I guess that would do the similar thing;
FROM School sch WHERE 'Student Name' = ANY (SELECT stud.name FROM sch.students stud)

Categories