条件 API:在具有继承用户权限的树中查找实体

Criteria API : finding entities in a tree with inherited user permissions

我正在尝试使用条件 API 在位置的层次结构中执行相当复杂的搜索。

我这里只写相关实体及其相关属性

实体

位置
- Location upperLocation(可能为空)
- Set<Location> childrenLocation(可能为空)
- Set<User> managers(可能为空)

警报
- Location originatedIn(可能为空)

用户

更多详情

User 和 Location 之间的关系是 ManyToMany。

位置表示是分层的。例如,世界是一个包含美国、英国、法国等国家/地区的位置,这些国家/地区本身包含城市,...

树中可以有无限级别的子位置。

警报是否源自某个位置。

如果用户是某个位置的有效(在数据库中)经理,或者如果他是树中位置 parents 之一的经理,则该用户被视为该位置的经理。基本上,如果您是美国的经理,您将自动被视为美国所有 children 地点的经理,以及他们的 children,等等...

我尝试建立的标准必须找到起源于用户是直接经理或继承经理的位置的警报。

代码

我有一个 DAO :
AlertRepository extends JpaRepository<Alert, Long>, JpaSpecificationExecutor<Alert>
我在上面请求查询,只是传递规范。

List<Alert> alerts = dao.findAll(buildSpecificationForUser(user))

以及规范生成器:

private Specification<Alert> buildSpecificationForUser(final User user) {
        return new Specification<Alert>() {
            @Override
            public Predicate toPredicate(Root<Alert> root, CriteriaQuery<?> query, CriteriaBuilder builder) {

                query.distinct(true);

                Expression<Collection<User>> managersOfLocations = root.get("originatedIn").get("managers");
                return builder.isMember(user, managersOfLocations);
            }

        };

    }

使用此功能,我只收到用户直接管理的位置的警报。

问题

如何让它在用户是继承经理的位置中找到警报?

更新

我也试过这个:

Join<Alert, User> managersOfLocation = root.join("originatedIn").join("upperLocation",JoinType.LEFT).join("managers",JoinType.LEFT);
return builder.equal(managersOfLocation.get("id"),user.getId());

但是结果集 return 如果用户不是任何位置的管理员,则所有警报事件

由于 Location 的表示是分层的,因此您需要 hierarchical query to fetch the data that you need. However, there is no direct hierarchical query support in JPA 2.1. If you expect to have a lot of Locations, you'll have to update your data model to use materialized paths or nested sets

如果您不打算拥有大量的位置,或者修改您的 table 结构现在不是一个选项,您可以创建一个视图来执行分层 SQL你,并映射到那个。例如:

如果您创建以下视图 manager_location_all(在 PostgreSQL 9.4.4 上测试):

CREATE OR REPLACE VIEW manager_location_all AS
 WITH RECURSIVE ancestor_descendant(ancestor_id, descendant_id) AS (
         SELECT root.upper_location_id, root.location_id
           FROM location root
         UNION ALL
         SELECT l.upper_location_id, ad.descendant_id
           FROM location l
           JOIN ancestor_descendant ad ON l.location_id = ad.ancestor_id
        )
 SELECT manager_location.manager_id, ancestor_descendant.descendant_id AS location_id
   FROM ancestor_descendant
   JOIN manager_location ON ancestor_descendant.ancestor_id = manager_location.location_id
  UNION ALL
 SELECT manager_location.manager_id, manager_location.location_id
   FROM manager_location;

然后您可以使用该视图将 Set<User> allManagers 映射添加到您的 Location 对象,然后在您的规范构建器中执行 root.get("originatedIn").get("allManagers")。但是,这种方法不会像更新数据模型那样具有可扩展性。