【问题标题】:Why left join on CriteriaQuery doesn't filter results?为什么 CriteriaQuery 上的左连接不过滤结果?
【发布时间】:2020-10-20 20:21:53
【问题描述】:

我有两个实体,UserRoles,每个实体都具有活动布尔参数和 ManyToMany 关系。

这个布尔参数用于逻辑删除。

当我在下面运行此查询时,为什么结果包含非活动角色?

 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());

【问题讨论】:

  • 如果您没有充分的理由怀疑该问题是由 jpa 2.2 引起的,则应使用jpa 标签代替,以吸引更多答案。出于同样的原因,您应该添加criteria-api 标签

标签: java hibernate jpa criteria-api


【解决方案1】:
  1. 考虑以下Users 与@OneToManyRoles 的关系。
 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
  1. JPA 首先找到满足您条件的 User 记录(至少有一个活跃角色)
 User-1
 User-3
  1. 你的 where 条件的作用到此结束。一旦决定要获取哪个记录 id,JPA 将完整获取那些 User 记录。 User-1User-3 及其所有相关角色(包括非活动角色)。它可能会执行join 或另一个select,但无论何时执行,它都会获取其所有相关字段

  2. 总之,一旦它决定获取entity它就不能对其关联的字段进行任何过滤。在这种情况下,您希望在 user.getRoles() 中有一个过滤的对象,但它不能。

  3. 如果 JPA 允许这样做,它就不能这样做 dirty checkingcascadingrepeatable read。所以它不允许它

【讨论】:

  • 明白了!是否有其他方法可以避免这种无效结果?
  • 好吧。你知道你有合适的用户。那么当你创建userDto 时,你为什么不做过滤呢?即当将用户映射到 userDto 时,您可以创建一个名为 UserToUserWithActiveRolesDtoMapper 的自定义映射器,然后在内存中进行过滤。或者您必须编写查询来选择列并使用行映射器来创建对象
  • 我没有名声。
猜你喜欢
  • 1970-01-01
  • 2013-03-24
  • 2011-02-01
  • 2015-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
相关资源
最近更新 更多