How to conditionally WHERE populated foreign keys with OneToMany Entity? - java

I am assuming a setup as described in: https://howtodoinjava.com/hibernate/hibernate-one-to-many-mapping-using-annotations/
Employee:
#JoinColumn(name="EMPLOYEE_ID")
private Set<AccountEntity> accounts;
Account:
#ManyToOne
private EmployeeEntity employee;
Using JPA/Hibernate, how can I fetch results using a WHERE condition that applies to Account. In other words, query something like Select all employees with accounts with sales > 0 or similar.

I'm assuming sales column as INT in account table.
you can write a query like following:
TypedQuery<Employee> query = em.createQuery("SELECT e FROM Employee e JOIN Account a ON e.id = a.accounts.employee_id WHERE a.sales > :sales", Employee.class);
query.setParameter("sales", salesInput);
List<Employee> items = query.getResultList();
I would recommend you to go through this tutorial to learn about CRUD operations in Hibernate Associations
https://www.dineshonjava.com/spring-crud-example-using-many-to-one/

Related

Hibernate DetachedQuery and Spring's HibernateTemplate with Restriction giving wrong results

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

JPA CriteriaBuilder - sort by the number of associated entities in a one-to-many relationship

I have two entities Customer and Order in a one-to-many relation.
For each customer I need to count the number of associated orders and sort the results by this number.
In a native postgres query it looks like this:
select cust.id, count(order.id) from customers cust
left outer join orders order
on cust.id = order.customer_id
where .... conditions ...
group by cust.id
order by count desc;
But I must do this using CriteriaBuilder because this query is part of a larger piece of code that uses CriteriaBuilder to put in additional conditions. In Hibernate I would have probably used Projections, but I can't find anything similar in JPA.
Any help in composing the query using CriteraBuilder would be much appreciated.
Thank you in advance.
Supposing that the entity Customer has a OneToMany property like this:
#OneToMany(mappedBy = "customerId")
private Collection<Order> orders;
You can use the following query:
EntityManager em; // to be built or injected
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<Customer> customer = cq.from(Customer.class);
CollectionJoin<Customer, Order> orders = customer.join(Customer_.orders, JoinType.LEFT);
cq.select(cb.tuple(customer, cb.count(orders)));
cq.where(... add some predicates here ...);
cq.groupBy(customer.get(Customer_.id));
cq.orderBy(cb.desc(cb.count(orders)));
List<Tuple> result = em.createQuery(cq).getResultList();
for (Tuple t : result) {
Customer c = (Customer) t.get(0);
Long cnt = (Long) t.get(1);
System.out.println("Customer " + c.getName() + " has " + cnt + " orders");
}
The above approach uses Metamodel. If you don't like it, you can replace Customer_.orders with "orders" and Customer_.id with "id".
If the OneToMany property is of another type, replace CollectionJoin with the collection of the proper type (ListJoin, SetJoin, MapJoin).
Use this inside the specification
cq.orderBy(cb.desc(cb.count(orders)));
Also send PageRequest(1, 10, Sort.unsorted()). This is how I did it.
If you are passing the Sort value as unsorted and then override criteria query with your own logic of sorting on your joined entity

How to write following JPQL using Criteria?

Student - Course : OneToMany
JPQL :
select Student from Student student, Course course where
student.name=:STUDENTNAME and (course.courseName=:COURSENAME or
course.courseDuration=courseDuration)
Let us suppose one student might have 10 courses i want to retrieve only two records having Student - Courses(2).
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Student> criteriaQuery = criteriaBuilder.createQuery(Student.class);
Root<Student> studentRoot = criteriaQuery.from(Student.class);
studentRoot.fetch("courses", JoinType.LEFT);
Predicate condition = criteriaBuilder.equal(studentRoot.get("studentName"), "someName");
//how to add course condition here.
criteriaQuery.where(condition);
I Hope your Student entity has relation with Course mapping with property name course in student entity.
Here is Criteria Query
Criteria criteria = getSession().createCriteria(Student.class);
criteria.createAlias("course", "course");
criteria.add(Restrictions.eq("name", STUDENTNAME));
Criterion courseNameCrit = Restrictions.eq("course.courseName", courseName);
Criterion courseDurationCrit = Restrictions.eq("course.courseDuration", courseDuration);
criteria.add(Restrictions.disjunction().add(courseNameCrit).add(courseDurationCrit));
List<Student> studentList = criteria.list();
There is no equivalent of JPA QL “JOIN FETCH” in Criteria API.
JOIN FETCH uses EAGER fetch irrespective of the fetch type specified in the model annotation.
If you are using criteria api, you can call the getter method of child in the dao,so that it gets loaded.

Hibernate Criteria query: getting a list of objects with a m..n relationship where child table does not have a certain property

I have the following tables
CREATE TABLE "COMPANIES" (
"ID" NUMBER NOT NULL ,
"NAME" VARCHAR2 (100) NOT NULL UNIQUE
)
/
CREATE TABLE "COMPANIESROLES" (
"ID" NUMBER NOT NULL ,
"COMPANYID" NUMBER NOT NULL ,
"ROLENAME" VARCHAR2 (30) NOT NULL
)
/
CREATE TABLE "ROLES" (
"NAME" VARCHAR2 (30) NOT NULL
)
/
This structure represents a number of companies and the roles allowed for each company. For these tables, there are the corresponding Hibernate objects:
public class Company implements Serializable {
private Long id;
private String name;
private Set<Role> companyRoles;
//(getters and setters omitted for readability)
}
public class Role implements Serializable {
private String name;
//(getters and setters omitted for readability)
}
Finding out all companies, which have a specific role using the Hibernate Criteria API is no problem:
Session session = this.sessionFactory.getCurrentSession();
Criteria criteria = session.createCriteria(Company.class);
criterion = Restrictions.eq("companyRoles.name", "ADMIN");
criteria.add(criterion);
List<Company> companyList = criteria.list();
Hibernate translates this to an SQL query (approximately)
SELECT *
FROM companies this_
inner join companyroles cr2_
ON this_.id = cr2_.companyid
inner join roles role1_
ON cr2_.rolename = role1_.NAME
WHERE role1_.NAME = 'ADMIN'
And now the problem: how can I reverse the query, i.e. find out all companies, which do not have a mapping for the role "ADMIN"? If I simply try to reverse the criterion by setting
criterion = Restrictions.ne("companyRoles.name", "ADMIN");
(not equals instead of equals), Hibernate creates a query like this
SELECT *
FROM companies this_
inner join companyroles cr2_
ON this_.id = cr2_.companyid
inner join roles role1_
ON cr2_.rolename = role1_.NAME
WHERE role1_.NAME != 'ADMIN'
Obviously, this does not produce the desired output, as the list still contains companies having the role "ADMIN", as long as the companies have at least one other role.
What I want to have is a list of companies, which do not have the role "ADMIN". As an additional restriction, this should be doable by just modifying the Criterion object, if possible (this is because the criterion is built automatically as part of an internal framework, and it is not possible to make larger changes there). The solution should also work, when the Criteria object contains other, additional criterions.
How is this doable, or is it?
You need a subquery (DetachedCriteria).
DetachedCriteria sub = DetachedCriteria.forClass(Company.class);
criterion = Restrictions.eq("companyRoles.name", "ADMIN");
sub.add(criterion);
sub.setProjection(Projections.property("id"));
Criteria criteria = session.createCriteria(Company.class);
criteria.add(Property.forName("id").notIn(sub));
List<Company> companyList = criteria.list();
Something like that should do it.

JPQL for Entities with No Items in ManyToMany Relationship

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

Categories

Resources