Часто нам приходится бороться с отношениями «один ко многим» и аналогичными отношениями в SQL и, в свою очередь, в Hibernate.
Когда мы выполняем запрос, в результате мы можем получить список с дублированными объектами, и это правильно, потому что SQL выполняет декартово продукт.

Пожалуйста, подумайте в этой ситуации:

Staff
ID, NAME, SURNAME
1, Davide, Cerbo
2, Valentina, Perazzo
Role
ID, NAME, TITLE
1, Engineer 
2, Chemist
Responsibility
ID, DESCRIPTION
1 Software development
2 Team management
3 Chemical Lab
STAFF_ROLE
STAFF_ID, ROLE_ID
1, 1
2, 2
ROLE_RESPONSIBILITY
RESPONSIBILITY_ID, ROLE_ID
1, 1
2, 1
2, 2
3, 2

Если бы мы знали, что персонал отвечает за управление командой, мы могли бы объединиться, как показано ниже:

SetJoin<Staff, Role> roleJoin = root.join(Staff_.role);
SetJoin<Role, Responsibility> responsibilityJoin = roleJoin.join(Role_.responsibility);
Path<Long> id = responsibilityJoin.get(Responsibility.id);
Predicate predicate = criteriaBuilder.equal(id, idValue);

Но для некоторых предметов у вас будет более одного объекта:

1, Davide, Cerbo
2, Valentina, Perazzo
1, Davide, Cerbo
2, Valentina, Perazzo

Как мы можем это исправить? Нам нужно выполнить подзапрос, и у нас есть два способа:

  1. с использованием предложения IN
SELECT *
FROM staff
WHERE id IN (SELECT staff_id
 FROM staff_role
 WHERE role_id IN (SELECT role_id
 FROM role_responsibility
 WHERE responsibility_id = 1));
  1. с использованием предложения EXISTS
SELECT *
FROM staff
WHERE exists(SELECT staff_id
 FROM staff_role, role_responsibility
 WHERE staff_role.staff_id = staff.id AND role_responsibility.role_id = staff_role.role_id AND role_responsibility.responsibility_id = 1)

И наконец, используя Hibernate, мы имеем:

Subquery<Long> subqueryResponsability = query.subquery(Long.class);
Root<Role> rootSubquery = subqueryResponsability.from(Role.class);
suqueryResponsability.select(rootSubquery.get(Role_.id));
SetJoin<Role, Responsibility> responsibilityJoin = rootSubquery.join(Role_.responsibility);
SetJoin<Responsibility, Staff> staffJoin = rootSubquery.join(Role_.staff);
subqueryResponsability.where(
 criteriaBuilder.equal(responsibilityJoin.get(Responsibility_.id), id),
 criteriaBuilder.equal(root.get(Staff_.id), staffJoin.get(Staff_.id)));
Predicate predicate = criteriaBuilder.exists(subqueryResponsability);

Итак, в заключение:
1) Не используйте отдельный, у вас возникнут проблемы с обработкой разбивки на страницы.
2) Не используйте java.util.Set в качестве типа результата, это уловка. < br /> 3) Используйте внутренний запрос, я предполагаю, что существует, но также IN может быть решением.
4) Я знаю, что эта статья очень проста, но многие люди не понимают этой проблемы :)