I have a entity called A which has one property userGroups:
#entity
Public class A {
#OneToMany
#JoinTable(name = "a_user_groups", ...)
private Set<UserGroup> userGroups;
...
}
I need to find those A entities which their userGroups has no intersection with given Set<UserGroup> userGroups parameter.
This is my method:
#Query("SELECT a FROM A a WHERE :userGroups intersect a.userGroups is NULL")
List<A> getAWithNoIntersectionInGroups(#param("userGroups") Set<UserGroup> userGroups)
But there is no intersect keyword in jpa.
Not In keyword not works here:
Suppose there is an A entity with user groups ids {1,3} and my passed user group ids are {3,4}, Now {1,3} is not in {3,4} and it's true and entity will be selected, but it shouldn't selected, because there is a mutual item {3}
You can use NOT IN SQL clause instead intersect.
#Query("SELECT a FROM A a WHERE a.userGroups not in (:userGroups)")
List<A> getAWithNoIntersectionInGroups(#param("userGroups") Set<UserGroup> userGroups)
Related
I have a query I'd like to run against a table, let's call it parent where I'm grabbing all rows that match a certain criteria. In SQL:
select * from parent where status = 'COMPLETE';
I have this table and another related table (child) defined as Hibernate entities such that:
#Entity
#Table(name = "parent")
public class Parent {
//...
#OneToMany(mappedBy = "parent")
private Set<Child> children;
//...
}
#Entity
#Table(name = "child")
public class Child {
//...
#ManyToOne
#JoinColumn(name = "parent_id")
private Parent parent;
#Column(name = "key")
private String key;
//...
}
I'd like my query to ALSO pull two optional child records where key is one of two values. So, in SQL:
select *
from parent p, child ca, child cb
where p.status = 'COMPLETED'
and p.id *= ca.parent_id
and ca.key = 'FIRST_KEY'
and p.id *= cb.parent_id
and cb.key = 'SECOND_KEY';
I could do this in Hibernate by just grabbing the result from the first query and iterating over the children collection looking for the rows I want but that's terribly inefficient: one query that does two outer joins vs one query + an additional query for each looking for the children I care about.
Is there a way to replicate the outer joins in the query above in Hibernate where the objects returned will have the children collection only populated with the two (or less, since they are optional) entities I am interested in?
You don't need two outer joins. You could simply use this HQL and Hibernate will add the right children to the right parent:
List<Parent> parentList = session
.createQuery("from Parent p left join fetch p.children c" +
" where p.status = 'COMPLETE' " +
" and (c.key = 'FIRST_KEY' or c.key = 'SECOND_KEY')")
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
.list();
for(Parent parent:parentList) {
System.out.println(parent);;
}
Hope that solves your problem.
My data structure is like this
Department
-> Employees
-> Gender
-> CityID -> Cities
->CityID
->CountryID -> Countries
-> CountryID
Department Class:
public class Department {
#OneToMany(mappedBy = "departmentid", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<Employee> employees = new HashSet<>();
}
I build Crteria like this:
DetachedCriteria criteria = DetachedCriteria.forClass(Department.class);
DetachedCriteria detlCrit = criteria.createCriteria("employees");
detlCrit.add(Restrictions.eq("gender", "MALE"));
detlCrit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
I have 1 Department, 2 Employees in the Tables (1 male, 1 female).
When I excecute this criteria iam expecting Hibernate build one 'Department' object, one 'Employee' object, and city, country etc.,
But what iam getting is 1 Department, 2 Employees.
When I see the queries executed by Hibernate in logs, it shows two queries
First Query:
Select * from Department, Employee
Left outer join City on Employee.cityID = City.cityID
Left outer join Country on City.countryID = City.countryID
Where Employee.DeptID = Department.DeptID
AND Employee.Gender = 'MALE';
Second query:
Select * from Employee
Left outer join City on Employee.cityID = City.cityID
Left outer join Country on City.countryID = City.countryID
Where Employee.DeptID = Department.DeptID;
Second query is wrong there is no Restriction applied on Gender='MALE';
What iam doing wrong? any suggestions? how to solve this?
sorry queries may be not exactly correct, but you got the idea.
Any more details needed please ask, I can provide.
Thanks in advance..
Try this,using SessionFactory.
#Autowired
private SessionFactory sessionFactory;
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Department.class);
criteria.add(Restrictions.eq("gender", "MALE"));
Hope I was useful.
The first query is selecting Department entities and the filtering is applied as you specified in your where clause.
But you cannot truncate associations, you always have to fetch them all eagerly or lazily. That's because Hibernate has to maintain consistency guarantees when flushing back the loaded Department entity and possibly cascading the employees state back to the database.
The second query is most likely because you use a FetchType.EAGER on your employees collection:
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "department", orphanRemoval = true)
private List<Employee> employees = new ArrayList<>();
Once the Department is fetched, the employee collection is fetched eagerly as well.
Try with an HQL query liken this one:
select distinct d
from Department d
left join fetch d.employees e
where e.gender = :gender
In my application, I have an entity A with a list of entities B that should be fetched eagerly :
#Entity
public class A
{
...
/* #OrderBy("cValue.id ASC") */
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="A_ID", nullable=false)
private List<B> BEntries = new ArrayList<B>();
...
}
#Entity
public class B
{
...
#ManyToOne
#JoinColumn(name = "C_ID", nullable = false)
private C cValue;
...
}
In order to get the list of A, I was first doing this simple query :
CriteriaBuilder critBuilder = em.getCriteriaBuilder();
CriteriaQuery<A> critQuery = critBuilder.createQuery(A.class);
Root<A> critRoot = critQuery.from(A.class);
critQuery.select(critRoot);
But there I saw that Hibernate was doing N+1 select queries on the database, 1 on class A, and N on class B (where N is the number of tuples of A in DB).
I was very surprise that, for eager fetching, Hibernate was not directly doing a LEFT JOIN query.
So I first tried to use the annotation #Fetch(FetchMode.JOIN) of Hibernate, but it was not working as expected.
So I transformed my list query with the following additional instructions:
Join<A,B> joinAB = critRoot.join(A_.BEntries, JoinType.LEFT);
joinAB.join(B_.cValue, JoinType.LEFT);
Ok, now the resulting SQL query contains all the needed LEFT JOIN to build the full A object eagerly... but it's still doing the other N queries on B table!
I first thought it was coming from the #OrderBy annotation I put on the Bentries parameter, but even when removed, it's still doing N+1 selects instead of 1...
Any idea why it's behaving like this?... and even why it's not doing a LEFT JOIN by default on eagerly fetched collections in entities?
I have an entity which has a collection of related entities.
public class Student{
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "COURSE_STUDENT_ID" , insertable=false,updatable=false)
private Set <Course> courses;
I want to filter students by course names and student class id. For now I have worked it out how to filter by class id but I have no idea how to filter by courseId given that Student entity has a set of courses and the tables are related. I have read some articles but no code matches the one I have already.
CriteriaBuilder criteriaBuilder = persistenceStore.createCriteriaBuilder();
CriteriaQuery<Object> criteria = criteriaBuilder.createQuery();
Root<Student> root = criteria.from(Student.class);
List<Predicate> params = new ArrayList<Predicate>();
params.add(criteriaBuilder.equal(root.get("classId"),classId));
Predicate[] predicates = new Predicate[params.size()];
params.toArray(predicates);
criteria.select(root);
criteria.where(criteriaBuilder.and(predicates));
Query query = persistenceStore.createQuery(criteria);
List<Student> resultList = query.getResultList();
First of all, there is an error in your Entity: the JoinColumn annotation applies to the entity on the inverse side of the relationship, Course in your case.
So, if Course entity has a property student, Student has a property like:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "student")
private Set<Course> courses;
and in Course entity you have (here it also states that in the db the table course has a field called "student":
#JoinColumn(name = "student", referencedColumnName = "id")
#ManyToOne(optional = false)
private Student student;
Read this link for an entry-level explaination on how to map entity relationships.
Regarding the Criteria Query, since you want to retrieve a List of StudentS, you can define your CriteriaQuery in a more type safe way:
CriteriaQuery<Student> criteria = criteriaBuilder.createQuery(Student.class);
Regarding the question, you have to join the tables in this way:
SetJoin<Student, Course> courses = root.join("courses");
or, using MetaModel:
SetJoin<Student, Course> courses = root.join(Student_.courses);
(had the OneToMany property been defined as a List or a Collection, you'd have had to use the corresponding ListJoin and CollectionJoin classes).
on the courses you can apply the desired Predicate conditions (supposing that Course entity has a string property called courseName):
Predicate p = criteriaBuilder.equal(courses.get("courseName"), "name-to-look-for");
or, using Metamodel:
Predicate p = criteriaBuilder.equal(courses.get(Course_.courseName), "name-to-look-for");
Finally, in order to concatenate correctly a list of predicates, you can use (at least) two techniques:
Predicate p1 = ...;
Predicate p2 = ...;
criteria.where(criteriaBuilder.and(p1, p2));
or
List<Predicate> conditions = new ArrayList<Predicate> ();
conditions.add(p1);
conditions.add(p2);
criteria.where(conditions.toArray(new Predicate[] {}));
See also this excellent article.
type mismatch: cannot convert from cascadetype to cascadetype[]
Answer is:
import javax.persistence.CascadeType;
import this line
Simple JPA/JPQL question. I have an entity with a ManyToMany relationship:
#Entity
public class Employee {
#ManyToMany
#JoinTablename="employee_project"
joinColumns={#JoinColumn(name="employee_id"}
inverseJoinColumns={#JoinColumn(name="project_id"})
private List<Project> projects;
What is the JPQL query to return all the Employees that do not have any projects?
from Employee e where not exists elements(e.projects)
or
from Employee e where size(e.projects) = 0
JQPL does have dedicated IS [NOT] EMPTY comparison operator for checking is collection empty:
SELECT e FROM Employee e WHERE e.projects IS EMPTY