有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

JavaSpringDataJPA:使用联接表进行排序和分页

我有一个场景,我想过滤、排序和分页一个结果,其中有3个表参与

目前,我使用SpringDataJPA的规范特性在单个实体上实现:repository.findAll(specification, pageRequest)

这很好,但现在我有另一个场景,排序/筛选属性分布在3个表上,这些表通过一对多关系连接

以下是我的设想:

@Entity
public class CustomerEntity ... {
  ...

  @Column(nullable = false)
  public String                                 customerNumber;

  @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, orphanRemoval = true)
  public List<CustomerItemEntity> items;
}


@Entity
public class CustomerItemEntity ... {
  ...

  @Column(nullable = false)
  public String                                 itemNumber;

  @ManyToOne(optional = false)
  @JoinColumn(name = "customerId")
  public CustomerEntity customer;

  @OneToMany(mappedBy = "item", cascade = CascadeType.ALL, orphanRemoval = true)
  public List<DocumentEntity> documents;
}


@Entity
public class DocumentEntity ... {
  ...

  @Column(nullable = false)
  public LocalDate                                 validDate;

  @ManyToOne(optional = false)
  @JoinColumn(name = "itemId")
  public CustomerItemEntity item;
}

有没有办法使用PageRequestSpecification,其中customerNumberitemNumbervalidDate同时用于过滤、排序和分页


共 (2) 个答案

  1. # 1 楼答案

    我发现您可以使用使用自定义查询创建的联接别名,按OneToMany列的内容进行排序。在我的代码库中,它是:

    @Query("select p from Person p join p.roles r")
    Page<Person> search(Pageable pageable);
    

    因此,我能够使用以下方法进行排序:

    r.role
    

    使用以下命令从控制器传递:

    @PageableDefault(size = 15, sort = "r.role") Pageable pageable
    
  2. # 2 楼答案

    试着这样做:

    Specification<CustomerEntity> joins = (customer, query, cb) ->  {
        // from CustomerEntity c
        // join c.items i
        Join<CustomerEntity, CustomerItemEntity> items = customer.join("items");
    
        // join i.documents d
        Join<CustomerItemEntity, DocumentEntity> documents = items.join("documents");
    
        // // where c.customerNumber = ?1 and i.itemNumber = ?2 and d.validDate = ?3 
        return cb.and( 
                customer.equal(customer.get("customerNumber", customerNumber)),
                items.equal(items.get("itemNumber", itemNumber)), 
                documents.equal(documents.get("validDate", validDate))
        );
    };
    
    // sort by c.customerNumber asc
    PageRequest pageRequest = new PageRequest(0, 2, new Sort(Sort.Direction.ASC, "customerNumber"));
    
    Page<CustomerEntity> customerPage = CustomerRepo.findAll(joins, pageRequest);
    

    但我不知道你为什么需要这里

    您可以使相同的操作更简单:

    @Query("select c from CustomerEntity c join c.items i join i.documents d where c.customerNumber = ?1 and i.itemNumber = ?2 and d.validDate = ?3")
    Page<CustomerEntity> getCustomers(String customerNumber, String itemNumber, LocaleDate validDate, Pageable pageable);  
    

    但所有这些都没有意义,因为你的三个实体有顺序的一对多关联。在这种情况下,您只能使用最后一个条件,而不是三个条件:where d.validDate = ?1。然后,查询方法变得更加简单:

    @Query("select c from CustomerEntity c join c.items i join i.documents d where d.validDate = ?1")
    Page<CustomerEntity> getCustomers(LocaleDate validDate, Pageable pageable);
    

    更新

    要按连接实体的字段添加排序,我们可以使用queryorderBy方法:

    Specification<CustomerEntity> joins = (customer, query, cb) ->  {
    
        Join<CustomerEntity, CustomerItemEntity> items = customer.join("items");
        Join<CustomerItemEntity, DocumentEntity> documents = items.join("documents");
    
        // Ascending order by 'Document.itemNumber'
        query.orderBy(cb.asc(documents.get("itemNumber")));
    
        return cb.and( 
                customer.equal(customer.get("customerNumber", customerNumber)),
                items.equal(items.get("itemNumber", itemNumber)), 
                documents.equal(documents.get("validDate", validDate))
        );
    };
    
    Page<CustomerEntity> customerPage = CustomerRepo.findAll(joins, new PageRequest(0, 2));
    

    要按多个参数进行排序,可以将它们传递给用逗号或List分隔的方法:

    query.orderBy(cb.asc(items.get("customerNumber")), cb.desc(documents.get("itemNumber")));