问题
I have two entities, User
and Roles
each one with active boolean parameter and ManyToMany
relationship.
This boolean parameter is used for logical delete.
Why does the result include inactive Roles when I run this query below?
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<User> query = builder.createQuery(User.class);
//Select
Root<User> user = query.from(User.class);
//Join
SetJoin<User, Role> role = user.joinSet("roles", JoinType.LEFT);
Predicate rolesActivePredicate = builder.isTrue(role.get("active"));
role.on(rolesActivePredicate);
query.multiselect(user).distinct(true);
//Where
Predicate usernamePredicate = builder.equal(user.get("username"), username);
Predicate activePredicate = builder.isTrue(user.get("active"));
query.where(usernamePredicate, activePredicate);
TypedQuery<UnikUser> typedQuery = em.createQuery(query);
return Optional.of(typedQuery.getSingleResult());
回答1:
- Consider the following
User
s with@OneToMany
relationship toRole
s.
User-1 : Active-Role-1, Active-Role-2, In-Active-Role-3
User-2 : In-Active-Role-3
User-3 : Active-Role-1, Active-Role-2
- JPA first finds
User
records that satisfy your condition (have at-least one an active role)
User-1
User-3
This is where the role of your where conditions end. Once it has been decided which record id to fetch, JPA will fetch those
User
records in full.User-1
andUser-3
with all their associated roles (including inactive). It might dojoin
or anotherselect
but whenever it does, it will fetch all of its associated fieldsIn summary, Once it has decided to fetch an
entity
, it cannot do any filtering on its associated fields. In this case, you expected a filtered objects inuser.getRoles()
but it cannot.If JPA had allowed that, it cannot do
dirty checking
,cascading
orrepeatable read
. So it does not allow it
来源:https://stackoverflow.com/questions/62659001/why-left-join-on-criteriaquery-doesnt-filter-results