首页 > 解决方案 > 带有规范的 Spring Data JPA @Query

问题描述

我需要创建一个 REST api。Api 允许用户在 URL 中提供动态搜索条件。例如,假设我有一个带有 Note_ID、NOTE_TEXT、STATUS、PERSON_ID 列的 NOTES 表。这张表是用来记录每个人的。

现在我希望我的 REST api 成为https://server:host/MyApi/Notes?search=NoteText== 'My Java Adventure'。API 应提供所有具有 NOTE_TEXT 作为“我的 Java 冒险”的注释。同样,用户也可以在 url 中提供状态,也可以将运算符用作 LIKE。如https://www.baeldung.com/rest-api-search-language-rsql-fiql中所述,我能够通过 rsql 解析器完成此操作

现在我有一个额外的要求,即基于用户安全 person_id 过滤器应该自动应用于查询。

我发现我们没有 findBy 方法可以接受 Specification、Pageable 和额外的 personId。例如我不能有一个存储库函数作为 findByPersonId(Specification spec, Pageable page, Long personId);

本来想用SpEL来用的,后来发现如果在findBy方法上使用@Query注解,就会忽略Specifications。

似乎我无法同时拥有 Specification 和 @Query 。我只需要使用规范添加更多子句。实际上,我的 where 子句非常复杂,我必须附加它并且使用规范来获取它似乎很困难。它类似于 Select * from NOTES where exists (select 'x' from ABC a where n.person_id = a.person_id)

有没有办法我可以编写 @Query 并在其上使用规范?

理想情况下,我已经实现了类似的查询

select * from test.SCH_FORUM_THREAD t
where exists (select 'x' from test.FORUM_THREAD_ACCESS fta, school.SCH_GROUP_PERSON gp
                where gp.GROUP_ID = fta.GROUP_ID
                and t.THREAD_ID = fta.THREAD_ID
                and gp.PERSON_ID = :personId)
    or exists (select 'x' from test.FORUM_THREAD_ACCESS fta
                where fta.THREAD_ID = t.THREAD_ID
                and fta.PERSON_ID = :personId);

所以有两个带有 or 条件的存在子句。通过遵循如何使用 JPA Criteria Builder 编写查询(包括子查询和存在),我能够使第二个存在

现在挣扎第一个存在,因为它也加入了。任何想法如何使用规范来做到这一点。同样存在两个,这是否意味着我需要两个规范。我可以在一个规范中实现它吗?

标签: spring-bootspring-data-jpa

解决方案


我能够通过创建复杂的规范代码来解决它。就像是

@Override
public Predicate toPredicate(Root<ForumThread> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
            Subquery<ForumThread> subQuery = query.subquery(ForumThread.class);
            Root<ForumThread> subRoot = subQuery.from(ForumThread.class);
            Join<ForumThreadAccess, GroupPerson> fragpjoin = subRoot.join("groupPersons");
            Predicate threadPredicate = builder.equal(root.get("threadId"), subRoot.get("threadId"));
            Predicate personPredicate = builder.equal(fragpjoin.get("personId"), personId); 
            subQuery.select(subRoot).where(threadPredicate, personPredicate);


            Predicate existsGroupPredicate = builder.exists(subQuery);


            Subquery<ForumThreadAccess> subQuery1 = query.subquery(ForumThreadAccess.class);
            Root<ForumThreadAccess> subRoot1 = subQuery1.from(ForumThreadAccess.class);
            Predicate threadPredicate1 = builder.equal(root.get("threadId"), subRoot1.get("threadId"));     
            Predicate personPredicate1 = builder.equal(subRoot1.get("personId"), personId); 
            subQuery1.select(subRoot1).where(threadPredicate1, personPredicate1);


            Predicate existsPersonPredicate = builder.exists(subQuery1);

            return builder.or(existsGroupPredicate,existsPersonPredicate);


        }

为了使其工作,您的实体还应该有适当的@OneToMany 和@ManyToMany。

谢谢


推荐阅读