【问题标题】:JPA Criteria query : writing query with array operatorsJPA 标准查询:使用数组运算符编写查询
【发布时间】:2020-11-17 09:33:12
【问题描述】:

我有以下实体

Book {
  private String title;
  
  @ManyToMany
  @JoinTable(
     name = "book_should_read_user",
     joinColumns = @JoinColumn(name = "book_id"),
     inverseJoinColumns = @JoinColumn(name = "user_id")
  )
  private Set<User> shouldRead; // the users who should read the book

  @OneToMany(mappedBy = "book")
  private Set<BookReadAction> actions; // all the read actions performed on the book
}
BookReadAction {
  @ManyToOne
  private Book book;

  @ManyToOne
  private User user;
}

现在我想查询应该阅读集合中所有用户已阅读的所有书籍。 postgres 中的以下 sql 查询可以解决问题:

select * 
from book 
where id in (
   select distinct id 
   from (
      select book.id id, 
             array_agg(book_should_read_user.user_id) suggested_readers, 
             array_agg(distinct book_read_action.user_id) read_by 
     from book b
         inner join book_should_read_user on book.id = book_should_read_user.book_id
         inner join book_read_action on book.id = book_read_action.book_id
     group by book.id) subquery
   where suggested_readers <@ read_by)

然而,我想以编程方式添加这个子句,所以我宁愿使用 JPA 标准 API。尽管做了一些尝试,我还是很挣扎。是否可以在 JPA 标准 API 中从此查询构建谓词?

【问题讨论】:

    标签: java sql hibernate jpa criteria-api


    【解决方案1】:

    您不能将您编写的查询完全写为 HQL,而是等效的:

    select * 
    from book b
    where exists (
         select 1
         from book b
             inner join book_should_read_user on book.id = book_should_read_user.book_id
             inner join book_read_action on book.id = book_read_action.book_id
         where b.id = book.id
         group by book.id
         having array_agg(book_should_read_user.user_id) <@ array_agg(distinct book_read_action.user_id)
    )
    

    要使用 HQL 或 JPA Criteria API 编写此查询,您将需要通过 SQLFunction 提供 &lt;@ 运算符或整体谓词的自定义实现,您可以在 Hibernate 方言中注册。像这样的:

    public class ArrayContainsFunction implements SQLFunction {
            
        @Override
        public boolean hasArguments() {
            return true;
        }
        
        @Override
        public boolean hasParenthesesIfNoArguments() {
            return true;
        }
        
        @Override
        public Type getReturnType(Type firstArgumentType, Mapping mapping) throws QueryException {
            SessionFactoryImplementor sfi = (SessionFactoryImplementor) mapping;
            return sfi.getTypeHelper().basic(Integer.class);
        }
        
        @Override
        public String render(Type firstArgumentType, List args, SessionFactoryImplementor factory) throws QueryException {
            return "array_agg(" + args.get(0) + ") <@ array_agg(" + args.get(1) + ") and 1=";
        }
    }
    

    注册的时候应该可以在HQL中这样使用... HAVING array_contains(shouldRead.id, readAction.id)

    【讨论】:

    • 感谢您的建议,这确实是解决我问题的正确方法
    【解决方案2】:

    使用条件 API,您无法将子查询实现为 from 子句。

    https://www.objectdb.com/java/jpa/query/jpql/from#FROM_and_JOIN_in_Criteria_Queries

    您将不得不重组您的查询

    【讨论】:

      猜你喜欢
      • 2020-12-16
      • 1970-01-01
      • 2014-11-11
      • 2012-04-01
      • 2016-05-23
      • 2012-09-25
      • 2012-01-19
      • 2021-05-27
      • 1970-01-01
      相关资源
      最近更新 更多