3.5.3.4.5. 自定义排序

UI table 中按照实体属性排序的功能是通过 CollectionContainerSorter 实现的,需要为 CollectionContainer 设置该排序器。标准的实现是,如果数据在一页以内能显示,则在内存做数据排序,否则会使用合适的 “order by” 语句发送数据库的请求。”order by” 语句是中间层的 JpqlSortExpressionProvider bean创建的。

有些实体属性需要一个特殊的排序实现。下面我们用个例子解释一下如何自定义排序:假设有 Foo 实体带有 String 类型的 number 属性,但是我们知道该属性其实是只保存数字。所以我们希望排序的顺序是 1, 2, 3, 10, 11。但是,默认的排序行为会产生这样的结果:1, 10, 11, 2, 3

首先,在 web 模块创建一个 CollectionContainerSorter 类的子类,在内存进行排序:

  1. package com.company.demo.web;
  2. import com.company.demo.entity.Foo;
  3. import com.haulmont.chile.core.model.MetaClass;
  4. import com.haulmont.chile.core.model.MetaPropertyPath;
  5. import com.haulmont.cuba.core.entity.Entity;
  6. import com.haulmont.cuba.core.global.Sort;
  7. import com.haulmont.cuba.gui.model.BaseCollectionLoader;
  8. import com.haulmont.cuba.gui.model.CollectionContainer;
  9. import com.haulmont.cuba.gui.model.impl.CollectionContainerSorter;
  10. import com.haulmont.cuba.gui.model.impl.EntityValuesComparator;
  11. import javax.annotation.Nullable;
  12. import java.util.Comparator;
  13. import java.util.Objects;
  14. public class CustomCollectionContainerSorter extends CollectionContainerSorter {
  15. public CustomCollectionContainerSorter(CollectionContainer container,
  16. @Nullable BaseCollectionLoader loader) {
  17. super(container, loader);
  18. }
  19. @Override
  20. protected Comparator<? extends Entity> createComparator(Sort sort, MetaClass metaClass) {
  21. MetaPropertyPath metaPropertyPath = Objects.requireNonNull(
  22. metaClass.getPropertyPath(sort.getOrders().get(0).getProperty()));
  23. if (metaPropertyPath.getMetaClass().getJavaClass().equals(Foo.class)
  24. && "number".equals(metaPropertyPath.toPathString())) {
  25. boolean isAsc = sort.getOrders().get(0).getDirection() == Sort.Direction.ASC;
  26. return Comparator.comparing(
  27. (Foo e) -> e.getNumber() == null ? null : Integer.valueOf(e.getNumber()),
  28. EntityValuesComparator.asc(isAsc));
  29. }
  30. return super.createComparator(sort, metaClass);
  31. }
  32. }

如果有几个界面需要这个自定义的排序,可以在界面中实例化 CustomCollectionContainerSorter

  1. public class FooBrowse extends StandardLookup<Foo> {
  2. @Inject
  3. private CollectionContainer<Foo> fooDc;
  4. @Inject
  5. private CollectionLoader<Foo> fooDl;
  6. @Subscribe
  7. private void onInit(InitEvent event) {
  8. CustomCollectionContainerSorter sorter = new CustomCollectionContainerSorter(fooDc, fooDl);
  9. fooDc.setSorter(sorter);
  10. }
  11. }

如果排序器定义了一些全局的行为,则可以创建自定义的工厂在系统级别实例化该排序器:

  1. package com.company.demo.web;
  2. import com.haulmont.cuba.gui.model.*;
  3. import javax.annotation.Nullable;
  4. public class CustomSorterFactory extends SorterFactory {
  5. @Override
  6. public Sorter createCollectionContainerSorter(CollectionContainer container,
  7. @Nullable BaseCollectionLoader loader) {
  8. return new CustomCollectionContainerSorter(container, loader);
  9. }
  10. }

然后在 web-spring.xml 注册该工厂以替换默认工厂:

  1. <bean id="cuba_SorterFactory" class="com.company.demo.web.CustomSorterFactory"/>

现在我们在 core 模块为数据库级别的排序创建我们自己定义的 JpqlSortExpressionProvider 实现:

  1. package com.company.demo.core;
  2. import com.company.demo.entity.Foo;
  3. import com.haulmont.chile.core.model.MetaPropertyPath;
  4. import com.haulmont.cuba.core.app.DefaultJpqlSortExpressionProvider;
  5. public class CustomSortExpressionProvider extends DefaultJpqlSortExpressionProvider {
  6. @Override
  7. public String getDatatypeSortExpression(MetaPropertyPath metaPropertyPath, boolean sortDirectionAsc) {
  8. if (metaPropertyPath.getMetaClass().getJavaClass().equals(Foo.class)
  9. && "number".equals(metaPropertyPath.toPathString())) {
  10. return String.format("CAST({E}.%s BIGINT)", metaPropertyPath.toString());
  11. }
  12. return String.format("{E}.%s", metaPropertyPath.toString());
  13. }
  14. }

spring.xml 注册此 expression provider,覆盖默认:

  1. <bean id="cuba_JpqlSortExpressionProvider" class="com.company.demo.core.CustomSortExpressionProvider"/>